summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/resonance-audio
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/resonance-audio')
-rw-r--r--src/3rdparty/resonance-audio/.gitignore19
-rw-r--r--src/3rdparty/resonance-audio/.travis.yml17
-rw-r--r--src/3rdparty/resonance-audio/AUTHORS10
-rw-r--r--src/3rdparty/resonance-audio/CMakeLists.txt109
-rw-r--r--src/3rdparty/resonance-audio/CODEOWNERS6
-rw-r--r--src/3rdparty/resonance-audio/CONTRIBUTING.md14
-rw-r--r--src/3rdparty/resonance-audio/COPYRIGHTS1
-rw-r--r--src/3rdparty/resonance-audio/LICENSE202
-rw-r--r--src/3rdparty/resonance-audio/README.md192
-rwxr-xr-xsrc/3rdparty/resonance-audio/build.sh151
-rw-r--r--src/3rdparty/resonance-audio/patches/0001-resonance_audio-fix-C-20-build.patch36
-rw-r--r--src/3rdparty/resonance-audio/platforms/CMakeLists.txt30
-rw-r--r--src/3rdparty/resonance-audio/platforms/common/room_effects_utils.cc327
-rw-r--r--src/3rdparty/resonance-audio/platforms/common/room_effects_utils.h83
-rw-r--r--src/3rdparty/resonance-audio/platforms/common/room_effects_utils_test.cc197
-rw-r--r--src/3rdparty/resonance-audio/platforms/common/room_properties.h123
-rw-r--r--src/3rdparty/resonance-audio/platforms/common/utils.cc59
-rw-r--r--src/3rdparty/resonance-audio/platforms/common/utils.h55
-rw-r--r--src/3rdparty/resonance-audio/platforms/common/utils_test.cc107
-rw-r--r--src/3rdparty/resonance-audio/qt_attribution.json18
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/CMakeLists.txt335
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_binaural_decoder.cc80
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_binaural_decoder.h71
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_binaural_decoder_test.cc162
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_codec.h62
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_codec_impl.h295
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_codec_impl_test.cc354
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_lookup_table.cc231
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_lookup_table.h92
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_lookup_table_test.cc240
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_spread_coefficients.h610
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/associated_legendre_polynomials_generator.cc147
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/associated_legendre_polynomials_generator.h89
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/associated_legendre_polynomials_generator_test.cc167
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/foa_rotator.cc117
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/foa_rotator.h61
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/foa_rotator_test.cc201
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/hoa_rotator.cc308
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/hoa_rotator.h69
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/hoa_rotator_test.cc201
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/stereo_from_soundfield_converter.cc52
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/stereo_from_soundfield_converter.h35
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/stereo_from_soundfield_converter_test.cc103
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/utils.h120
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/ambisonics/utils_test.cc65
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/api/binaural_surround_renderer.cc35
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/api/binaural_surround_renderer.h255
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/api/resonance_audio_api.cc30
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/api/resonance_audio_api.h416
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator.h117
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator_test.cc53
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/audio_buffer.cc74
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/audio_buffer.h222
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/audio_buffer_test.cc144
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/channel_view.cc50
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/channel_view.h138
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/channel_view_test.cc157
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/constants_and_types.h176
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/integral_types.h121
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/logging.h108
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/misc_math.cc94
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/misc_math.h385
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/misc_math_test.cc373
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/object_transform.h31
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/simd_macros.h65
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/simd_utils.cc1299
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/simd_utils.h296
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/simd_utils_test.cc711
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/source_parameters.h101
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/spherical_angle.cc78
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/spherical_angle.h87
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/spherical_angle_test.cc122
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/base/unique_ptr_wrapper.h33
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/config/global_config.h37
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/config/source_config.cc76
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/config/source_config.h32
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/biquad_filter.cc149
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/biquad_filter.h137
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/biquad_filter_test.cc303
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/channel_converter.cc45
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/channel_converter.h40
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/channel_converter_test.cc80
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/circular_buffer.cc120
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/circular_buffer.h96
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/circular_buffer_test.cc230
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/delay_filter.cc180
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/delay_filter.h85
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/delay_filter_test.cc197
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/distance_attenuation.cc121
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/distance_attenuation.h82
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/distance_attenuation_test.cc132
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/fft_manager.cc209
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/fft_manager.h182
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/fft_manager_test.cc260
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/filter_coefficient_generators.cc165
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/filter_coefficient_generators.h177
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/filter_coefficient_generators_test.cc146
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/fir_filter.cc148
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/fir_filter.h62
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/fir_filter_test.cc104
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/gain.cc102
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/gain.h67
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/gain_mixer.cc115
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/gain_mixer.h96
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/gain_mixer_test.cc188
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/gain_processor.cc94
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/gain_processor.h74
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/gain_processor_test.cc181
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/gain_test.cc132
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/mixer.cc56
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/mixer.h62
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/mixer_test.cc163
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/mono_pole_filter.cc59
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/mono_pole_filter.h57
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/mono_pole_filter_test.cc59
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/multi_channel_iir.cc152
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/multi_channel_iir.h88
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/multi_channel_iir_test.cc117
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/near_field_processor.cc97
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/near_field_processor.h75
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/occlusion_calculator.cc52
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/occlusion_calculator.h54
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/occlusion_calculator_test.cc139
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/partitioned_fft_filter.cc267
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/partitioned_fft_filter.h167
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/partitioned_fft_filter_test.cc761
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/reflection.h34
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/reflections_processor.cc177
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/reflections_processor.h128
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/reflections_processor_test.cc142
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/resampler.cc335
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/resampler.h135
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/resampler_test.cc325
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_compensator.cc214
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_compensator.h110
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_update_processor.cc184
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_update_processor.h113
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/sh_hrir_creator.cc72
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/sh_hrir_creator.h54
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/shoe_box_room.cc61
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/shoe_box_room.h44
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/shoe_box_room_test.cc96
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb.cc350
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb.h173
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb_constants_and_tables.h6508
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb_test.cc324
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/stereo_panner.cc46
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/stereo_panner.h35
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/stereo_panner_test.cc88
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/utils.cc190
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/utils.h93
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/dsp/utils_test.cc194
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_listener.h57
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_ray.cc27
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_ray.h195
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_ray_test.cc243
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_source.h111
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_source_test.cc136
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/collection_kernel.cc128
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/collection_kernel.h79
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/collection_kernel_test.cc353
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/estimating_rt60.cc155
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/estimating_rt60.h38
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/estimating_rt60_test.cc185
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/impulse_response_computer.cc188
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/impulse_response_computer.h112
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/impulse_response_computer_test.cc377
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/mesh.h40
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/parallel_for.cc44
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/parallel_for.h53
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/parallel_for_test.cc65
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path.h36
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path_tracer.cc96
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path_tracer.h59
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path_tracer_test.cc193
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/proxy_room_estimator.cc438
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/proxy_room_estimator.h156
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/proxy_room_estimator_test.cc317
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/reflection_kernel.cc99
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/reflection_kernel.h104
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/reflection_kernel_test.cc300
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sampling.h126
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/scene_manager.cc153
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/scene_manager.h130
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/scene_manager_test.cc202
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sphere.cc120
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sphere.h69
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sphere_test.cc267
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/test_util.cc231
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/test_util.h131
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_binaural_decoder_node.cc111
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_binaural_decoder_node.h88
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_mixing_encoder_node.cc69
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_mixing_encoder_node.h71
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_mixing_encoder_node_test.cc150
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.cc495
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.h190
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl_test.cc202
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/buffered_source_node.cc46
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/buffered_source_node.h62
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/foa_rotator_node.cc67
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/foa_rotator_node.h54
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/gain_mixer_node.cc66
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/gain_mixer_node.h68
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/gain_mixer_node_test.cc246
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/gain_node.cc82
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/gain_node.h69
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/gain_node_test.cc138
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.cc290
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.h404
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager_config.h40
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/hoa_rotator_node.cc69
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/hoa_rotator_node.h55
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/mixer_node.cc57
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/mixer_node.h54
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/mixer_node_test.cc112
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/mono_from_soundfield_node.cc45
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/mono_from_soundfield_node.h44
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/near_field_effect_node.cc117
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/near_field_effect_node.h69
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/near_field_effect_node_test.cc127
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/occlusion_node.cc101
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/occlusion_node.h59
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/occlusion_node_test.cc186
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/reflections_node.cc113
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/reflections_node.h78
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.cc586
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.h175
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.cc162
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.h99
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/source_graph_config.h43
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/source_parameters_manager.cc58
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/source_parameters_manager.h68
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/source_parameters_manager_test.cc91
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/stereo_mixing_panner_node.cc65
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/stereo_mixing_panner_node.h61
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/graph/system_settings.h189
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/audio_nodes_test.cc407
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/node.h258
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/node_test.cc330
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/processing_node.cc89
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/processing_node.h115
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/publisher_node.h51
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/sink_node.cc54
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/sink_node.h59
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/source_node.cc41
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/source_node.h68
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/node/subscriber_node.h48
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/buffer_crossfader.cc66
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/buffer_crossfader.h48
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/buffer_crossfader_test.cc53
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/buffer_partitioner.cc160
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/buffer_partitioner.h152
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/buffer_partitioner_test.cc241
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/buffer_unpartitioner.cc132
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/buffer_unpartitioner.h135
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/buffer_unpartitioner_test.cc294
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/lockless_task_queue.cc156
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/lockless_task_queue.h124
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/lockless_task_queue_test.cc215
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/ogg_vorbis_recorder.cc107
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/ogg_vorbis_recorder.h94
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/planar_interleaved_conversion.cc505
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/planar_interleaved_conversion.h421
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/planar_interleaved_conversion_test.cc875
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/pseudoinverse.h45
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/pseudoinverse_test.cc87
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/sample_type_conversion.cc40
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/sample_type_conversion.h79
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/sample_type_conversion_test.cc80
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/semi_lockless_fifo.h232
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/sum_and_difference_processor.cc40
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/sum_and_difference_processor.h44
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/sum_and_difference_processor_test.cc42
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/task_thread_pool.cc249
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/task_thread_pool.h121
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/task_thread_pool_test.cc185
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/test_util.cc190
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/test_util.h91
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/test_util_test.cc249
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/threadsafe_fifo.h258
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/vorbis_stream_encoder.cc174
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/vorbis_stream_encoder.h114
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/wav.cc53
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/wav.h66
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/wav_reader.cc214
-rw-r--r--src/3rdparty/resonance-audio/resonance_audio/utils/wav_reader.h103
-rw-r--r--src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/LICENSE157
-rwxr-xr-xsrc/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generate_hrtf_assets.py287
-rw-r--r--src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generated/README5
-rw-r--r--src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generated/hrtf_assets.cc1200
-rw-r--r--src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generated/hrtf_assets.h45
-rw-r--r--src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/hrtf_assets.iad8
293 files changed, 50548 insertions, 0 deletions
diff --git a/src/3rdparty/resonance-audio/.gitignore b/src/3rdparty/resonance-audio/.gitignore
new file mode 100644
index 000000000..ec176f56d
--- /dev/null
+++ b/src/3rdparty/resonance-audio/.gitignore
@@ -0,0 +1,19 @@
+# Ignore third party directories by default
+third_party/android-cmake/
+third_party/eigen/
+third_party/embree/
+third_party/fmod/
+third_party/googletest/
+third_party/ios-cmake/
+third_party/nativeaudioplugins/
+third_party/ogg/
+third_party/pffft/
+third_party/vorbis/
+third_party/VST_SDK/
+third_party/WwiseIncludes/
+
+# Ignore build artifacts and install directories by default
+build/
+install/
+
+.DS_Store
diff --git a/src/3rdparty/resonance-audio/.travis.yml b/src/3rdparty/resonance-audio/.travis.yml
new file mode 100644
index 000000000..b37e5b05a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/.travis.yml
@@ -0,0 +1,17 @@
+language: cpp
+
+compiler:
+ - clang
+ - gcc
+
+dist: xenial
+
+os:
+ - linux
+ - osx
+
+install:
+ - ./third_party/clone_core_deps.sh
+
+script:
+ - ./build.sh -t=RESONANCE_AUDIO_TESTS
diff --git a/src/3rdparty/resonance-audio/AUTHORS b/src/3rdparty/resonance-audio/AUTHORS
new file mode 100644
index 000000000..a00d7c3fc
--- /dev/null
+++ b/src/3rdparty/resonance-audio/AUTHORS
@@ -0,0 +1,10 @@
+Google Inc.
+
+Contributors (in alphabetical order):
+Alper Gungormusler
+Dillon Cower
+Frank Boland
+Ian Kelly
+Julius Kammerl
+Marcin Gorzel
+Yero Yeh
diff --git a/src/3rdparty/resonance-audio/CMakeLists.txt b/src/3rdparty/resonance-audio/CMakeLists.txt
new file mode 100644
index 000000000..6af0b3fb3
--- /dev/null
+++ b/src/3rdparty/resonance-audio/CMakeLists.txt
@@ -0,0 +1,109 @@
+# Copyright 2018 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Static/dynamic runtime can be selected via -DSTATIC_MSVC_RUNTIME=ON|OFF
+macro(configure_msvc_runtime)
+ if (NOT BUILD_WWISE_AUTHORING_PLUGIN)
+ option(STATIC_MSVC_RUNTIME "Static linkage of MSVC runtime" ON)
+ SET(MSVC_RUNTIME_FLAG "/MT" CACHE STRING "MSVC Runtime flag")
+ if (STATIC_MSVC_RUNTIME)
+ SET(MSVC_RUNTIME_FLAG "/MT")
+ else ()
+ SET(MSVC_RUNTIME_FLAG "/MD")
+ endif ()
+ message(STATUS "MSVC Runtime flag: ${MSVC_RUNTIME_FLAG}")
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MSVC_RUNTIME_FLAG}" CACHE INTERNAL "" FORCE)
+ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MSVC_RUNTIME_FLAG}d" CACHE INTERNAL "" FORCE)
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${MSVC_RUNTIME_FLAG}" CACHE INTERNAL "" FORCE)
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MSVC_RUNTIME_FLAG}d" CACHE INTERNAL "" FORCE)
+ endif (NOT BUILD_WWISE_AUTHORING_PLUGIN)
+endmacro()
+
+macro(use_cxx11)
+ set(CMAKE_CXX_STANDARD 11)
+ set(CMAKE_CXX_STANDARD_REQUIRED on)
+endmacro(use_cxx11)
+
+project(ResonanceAudio)
+
+# Build options
+option(BUILD_RESONANCE_AUDIO_API "Build Resonance Audio API." OFF)
+option(BUILD_RESONANCE_AUDIO_TESTS "Build Resonance Audio Unit Tests" OFF)
+option(BUILD_UNITY_PLUGIN "Build Unity Resonance Audio Plugin" OFF)
+option(BUILD_GEOMETRICAL_ACOUSTICS_TESTS "Build Resonance Audio's Geometrical Acoustics Tests" OFF)
+option(BUILD_WWISE_AUTHORING_PLUGIN "Build Resonance Audio WWise Authoring Plugin" OFF)
+option(BUILD_WWISE_SOUND_ENGINE_PLUGIN "Build Resonance Audio WWise Sound Engine Plugin." OFF)
+option(BUILD_FMOD_PLUGIN "Build FMOD Resonance Audio Plugin" OFF)
+option(BUILD_VST_MONITOR_PLUGIN "Build Resonance Audio VST Monitor Plugin" OFF)
+
+if (MSVC)
+ configure_msvc_runtime()
+endif ()
+
+if (IOS_PLATFORM STREQUAL "OS" OR IOS_PLATFORM MATCHES "SIMULATOR")
+ message(STATUS "iOS toolchain detected")
+ set (IOS_DETECTED TRUE CACHE BOOL "IOS platform detected")
+ add_definitions(-DPLATFORM_IOS)
+elseif(ANDROID)
+ add_definitions(-DPLATFORM_ANDROID)
+endif ()
+
+cmake_minimum_required(VERSION 3.4.1)
+set(CMAKE_COLOR_MAKEFILE ON)
+
+set(INSTALL_DIR "${PROJECT_SOURCE_DIR}/install" CACHE PATH "Install path")
+
+set(RA_SOURCE_DIR ${PROJECT_SOURCE_DIR}/resonance_audio/)
+
+use_cxx11()
+if (BUILD_RESONANCE_AUDIO_TESTS OR BUILD_GEOMETRICAL_ACOUSTICS_TESTS)
+ enable_testing()
+
+ set(GTEST_DIR "${PROJECT_SOURCE_DIR}/third_party/googletest/" CACHE PATH "Path to GTest library")
+ # Compile Google Test as an object library
+ add_library(gtest OBJECT "${GTEST_DIR}/googlemock/src/gmock-all.cc"
+ "${GTEST_DIR}/googletest/src/gtest-all.cc"
+ "${GTEST_DIR}/googletest/src/gtest_main.cc")
+ target_include_directories(gtest PUBLIC "${GTEST_DIR}/googlemock/")
+ target_include_directories(gtest PUBLIC "${GTEST_DIR}/googlemock/include")
+ target_include_directories(gtest PUBLIC "${GTEST_DIR}/googletest/")
+ target_include_directories(gtest PUBLIC "${GTEST_DIR}/googletest/include")
+endif (BUILD_RESONANCE_AUDIO_TESTS OR BUILD_GEOMETRICAL_ACOUSTICS_TESTS)
+
+if (WIN32)
+ add_definitions(-D_USE_MATH_DEFINES)
+ add_definitions(-DNOMINMAX)
+ # Disable type conversion warnings.
+ add_definitions(/wd4244)
+ add_definitions(/wd4305)
+ add_definitions(/wd4722)
+elseif (APPLE)
+elseif (ANDROID)
+ set(NEON ON)
+elseif (UNIX)
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+ set(CMAKE_CXX_FLAGS "-fPIC -O3 -Wno-vla -msse -msse2 -msse3 -mfpmath=sse")
+endif ()
+
+# Add Eigen3.
+set(EIGEN3_DIR "${PROJECT_SOURCE_DIR}/third_party/eigen/" CACHE PATH "Path to eigen3 library")
+set(EIGEN3_INCLUDE_DIR ${EIGEN3_DIR})
+add_definitions(-DEIGEN_MPL2_ONLY)
+
+include_directories(${PROJECT_SOURCE_DIR})
+include_directories(${EIGEN3_INCLUDE_DIR})
+
+add_subdirectory(resonance_audio)
+add_subdirectory(platforms)
diff --git a/src/3rdparty/resonance-audio/CODEOWNERS b/src/3rdparty/resonance-audio/CODEOWNERS
new file mode 100644
index 000000000..6f999fe96
--- /dev/null
+++ b/src/3rdparty/resonance-audio/CODEOWNERS
@@ -0,0 +1,6 @@
+# Default owners for everything in the repo.
+* @anokta @haroonq
+
+# Wwise plugin.
+platforms/wwise/* @ralphyon
+
diff --git a/src/3rdparty/resonance-audio/CONTRIBUTING.md b/src/3rdparty/resonance-audio/CONTRIBUTING.md
new file mode 100644
index 000000000..9678067f7
--- /dev/null
+++ b/src/3rdparty/resonance-audio/CONTRIBUTING.md
@@ -0,0 +1,14 @@
+# Contributing guidelines
+
+We'd love to accept your patches! Before we can take them, we have to jump a couple of legal hurdles.
+
+## Contributor License Agreements
+
+Please fill out either the individual or corporate Contributor License Agreement (CLA).
+
+ * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](https://code.google.com/legal/individual-cla-v1.0.html).
+ * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA](https://code.google.com/legal/corporate-cla-v1.0.html).
+
+Follow either of the two links above to access the appropriate CLA and instructions for how to sign and return it. Once we receive it, we'll be able to accept your pull requests.
+
+***NOTE***: Only original source code from you and other people that have signed the CLA can be accepted into the main repository.
diff --git a/src/3rdparty/resonance-audio/COPYRIGHTS b/src/3rdparty/resonance-audio/COPYRIGHTS
new file mode 100644
index 000000000..ad864e69a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/COPYRIGHTS
@@ -0,0 +1 @@
+Copyright (c) 2015, 2017, 2018 Google Inc.
diff --git a/src/3rdparty/resonance-audio/LICENSE b/src/3rdparty/resonance-audio/LICENSE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/src/3rdparty/resonance-audio/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/src/3rdparty/resonance-audio/README.md b/src/3rdparty/resonance-audio/README.md
new file mode 100644
index 000000000..1e69c48b4
--- /dev/null
+++ b/src/3rdparty/resonance-audio/README.md
@@ -0,0 +1,192 @@
+# [Resonance Audio](https://developers.google.com/resonance-audio) Source Code [![Travis CI](https://travis-ci.org/resonance-audio/resonance-audio.svg?branch=master)](https://travis-ci.org/resonance-audio/resonance-audio)
+
+This is the official open source project for the Resonance Audio SDK. This
+repository consists of the full source code of the Resonance Audio C++ library,
+as well as the platform integrations into [Unity](https://unity3d.com/),
+[FMOD](https://www.fmod.com/),
+[Wwise](https://www.audiokinetic.com/products/wwise/) and DAW tools.
+
+Resonance Audio started as a Google product and has since graduated to open
+source. It is supported by members of our [steering
+committee](#steering-committee) who are also project committers.
+
+In this document there are some quick instructions for how to build the SDK from
+source code.
+
+For more detailed documentation about using the SDK, visit our [developer
+docs](https://developers.google.com/resonance-audio/). If you are interested in
+contributing to the project, please read the [Contributing to Resonance
+Audio](#contributing-to-resonance-audio) section below.
+
+## Build Instructions
+
+Clone the repository:
+
+ git clone https://github.com/resonance-audio/resonance-audio $YOUR_LOCAL_REPO
+
+### Software Requirements
+
+In addition to the system C++ software development platform tools / toolchains,
+the following software is required to build and install the Resonance Audio
+SDKs:
+
+- [CMake](https://cmake.org/download/)
+- [Git (with Git-Bash on Windows)](https://git-scm.com/downloads)
+
+_Note: For Windows builds, [Visual Studio
+2015](https://www.visualstudio.com/vs/older-downloads/) is recommended._
+
+### Third Party Dependencies
+
+All third party dependencies must be installed into the `third_party` subfolder
+in the repository. To simplify the installation, bash scripts are located within
+the `third_party` that automatically clone, build and install the required third
+party source code.
+
+_Note: On Windows, these scripts can be executed in the Git-Bash console (which
+gets installed as part of Git for Windows)._
+
+#### Core Dependencies ([pffft](https://bitbucket.org/jpommier/pffft), [eigen](https://bitbucket.org/eigen/eigen), [googletest](https://github.com/google/googletest), [SADIE Binaural Measurements](https://www.york.ac.uk/sadie-project/database_old.html))
+
+To clone the dependencies into the repository, run:
+
+ ./$YOUR_LOCAL_REPO/third_party/clone_core_deps.sh
+
+_Note: These dependencies do *not* need to be built, since their source code is
+directly pulled in from the build scripts._
+
+#### [Unity](https://unity3d.com/) Platform Dependencies ([nativeaudioplugins](https://github.com/Unity-Technologies/NativeAudioPlugins), [embree](https://github.com/embree/embree), [ogg](https://github.com/xiph/ogg), [vorbis](https://github.com/xiph/vorbis))
+
+The Unity plugin integrates additional tools to estimate reverberation from game
+geometry and to capture Ambisonic soundfields from a game scene. These features
+require the Embree, libOgg and libVorbis libraries to be *prebuilt*.
+
+To clone and build the additional Unity dependencies, run:
+
+ ./$YOUR_LOCAL_REPO/third_party/clone_build_install_unity_deps.sh
+
+#### [FMOD](https://www.fmod.com/) Platform Dependencies ([FMOD Low Level API](https://www.fmod.com/download#fmodstudio))
+
+To add the additional FMOD dependencies, download and install the [FMOD Studio
+API](https://www.fmod.com/download#fmodstudio) (which includes the FMOD Low
+Level API).
+
+_Note: On Linux, unzip the downloaded package within the `third_party` subfolder
+and rename its folder to `fmod`._
+
+#### [Wwise](https://www.audiokinetic.com/products/wwise/) Platform Dependencies ([WwiseIncludes](https://github.com/audiokinetic/WwiseIncludes))
+
+To clone the additional Wwise dependencies, run:
+
+ ./$YOUR_LOCAL_REPO/third_party/clone_wwise_deps.sh
+
+The Wwise Authoring Plugin (Windows only) also requires the [Microsoft
+Foundation Classes SDK](https://docs.microsoft.com/en-gb/cpp/mfc/mfc-and-atl).
+To install the SDK on Windows:
+
+1. Open the `Control Panel`
+2. Select `Programs->Programs and Features`
+3. Right-click on `Microsoft Visual C++ Build Tools`, and select `Change`
+4. Install `MFC SDK`
+
+#### DAW Tools Dependencies ([VST2 Audio Plug-Ins SDK](https://www.steinberg.net/vst3sdk))
+
+To add the additional DAW Tools dependencies, download the [Steinberg's VST
+3.X.X Audio Plug-Ins SDK](https://www.steinberg.net/vst3sdk) (which
+includes the VST2 Audio Plug-Ins SDK) and extract the package into `third_party`
+subfolder.
+
+### Build Resonance Audio SDKs
+
+This repository provides the `build.sh` script in the root folder that
+configures the build targets, triggers the compilation and installs the
+artifacts for the specified platform into the target installation folder.
+
+The script provides the following flags:
+
+- t=|--target=
+ - `RESONANCE_AUDIO_API`: Builds the Resonance Audio API
+ - `RESONANCE_AUDIO_TESTS`: Runs the Resonance Audio unit tests
+ - `GEOMETRICAL_ACOUSTICS_TESTS`: Runs the geometrical acoustics specific
+ unit tests.
+ - `UNITY_PLUGIN`: Builds the Resonance Audio plugin for Unity
+ - `WWISE_AUTHORING_PLUGIN`: Builds the Resonance Audio authoring plugin
+ for Wwise
+ - `WWISE_SOUND_ENGINE_PLUGIN`: Builds the Resonance Audio sound engine
+ plugin for Wwise
+ - `FMOD_PLUGIN`: Builds the Resonance Audio plugin for FMOD
+ - `VST_MONITOR_PLUGIN`: Builds the Resonance Audio VST Monitor Plugin
+- p=|--profile=
+ - `Debug`: Debug build
+ - `RelWithDebInfo`: Release build with debug information
+ - `Release`: Release build
+- --msvc_dynamic_runtime
+ - Enables dynamic linking against the run-time library on Windows (`/MD`,
+ `/MDd`). By default, all Windows builds are statically linked against
+ the run-time library (`/MT`, `/MTd`). Note that the third party
+ dependencies must be compiled with the same options to avoid library
+ conflicts.
+- --verbose_make
+ - Enables verbose make/build output.
+- --android_toolchain
+ - Enables the Android NDK toolchain to target Android builds (may require
+ adjustments to `ANDROID_NDK`, `ANDROID_NATIVE_API_LEVEL` and
+ `ANDROID_ABI` script variables). For more information, see project
+ documentation at https://github.com/taka-no-me/android-cmake.
+- --ios_os_toolchain
+ - Enables the iOS OS toolchain. For more information, see project
+ documentation at https://github.com/leetal/ios-cmake.
+- --ios_simulator_toolchain
+ - Enables the iOS Simulator toolchain. For more information, see project
+ documentation at https://github.com/leetal/ios-cmake
+
+##### E.g.
+
+To build and run the Resonance Audio unit tests:
+
+ ./$YOUR_LOCAL_REPO/build.sh -t=RESONANCE_AUDIO_TESTS
+
+## Citations
+
+If you find Resonance Audio useful and would like to cite it in your publication, please use:
+
+Gorzel, M., Allen, A., Kelly, I., Kammerl, J., Gungormusler, A., Yeh, H., and Boland, F., *"Efficient Encoding and Decoding of Binaural Sound with Resonance Audio"*, In proc. of the AES International Conference on Immersive and Interactive Audio, March 2019
+
+The full paper is available (open access) at: http://www.aes.org/e-lib/browse.cfm?elib=20446 ([BibTeX](http://www.aes.org/e-lib/browse.cfm?elib=20446&fmt=bibtex))
+
+## Contributing to Resonance Audio
+
+If you would like to contribute changes to the Resonance Audio project, please
+make a pull request for one of our project committers to review.
+
+### Steering Committee
+
+The Resonance Audio project is overseen by a steering committee established to
+help guide the technical direction of the project in collaboration with the
+entire developer community.
+
+The intention of the steering committee is to cultivate collaboration across the
+developer community for improving the project and ensuring Resonance Audio
+continues to work well for everyone.
+
+The committee will lead the Resonance Audio project in major decisions by
+consensus and ensure that Resonance Audio can meet its goals as a truly open
+source project.
+
+The steering committee consists of the following members (company name ordered):
+
+- Martin Dufour, Audiokinetic
+- Aaron McLeran, Epic Games
+- Mathew Block, Firelight Technologies
+- Alper Gungormusler, Google
+- Eric Mauskopf, Google
+- Haroon Qureshi, Google
+- Ian Kelly, Google
+- Julius Kammerl, Google
+- Marcin Gorzel, Google
+- Damien Kelly, Google (YouTube)
+- Jean-Marc Jot, Magic Leap
+- Michael Berg, Unity Technologies
+
+Affiliations are listed for identification purposes only; steering committee
+members do not represent their employers or academic institutions.
diff --git a/src/3rdparty/resonance-audio/build.sh b/src/3rdparty/resonance-audio/build.sh
new file mode 100755
index 000000000..3b0d3aa3a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/build.sh
@@ -0,0 +1,151 @@
+#!/bin/bash
+# Copyright 2018 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Change working directory to script folder
+SCRIPT_DIR="$( cd "$(dirname "$0")" ; pwd -P )"
+cd "${SCRIPT_DIR}"
+
+
+PROFILE="Release"
+VERBOSE_MAKE=""
+
+declare -a BUILD_FLAGS
+declare -a CONFIG_FLAGS
+
+ANDROID_NDK="~/android-ndk-r15c/"
+ANDROID_NATIVE_API_LEVEL="21"
+ANDROID_ABI="armeabi-v7a with NEON"
+
+MSVC_GENERATOR="Visual Studio 14 2015 Win64"
+
+function show_help()
+{
+ cat <<EOF
+*** Resonance Audio compilation script ***
+
+Please select a build target:
+
+ -t= | --target=[RESONANCE_AUDIO_API| # Resonance Audio API C/C++ library
+ RESONANCE_AUDIO_TESTS| # Resonance Audio unit tests
+ GEOMETRICAL_ACOUSTICS_TESTS| # Geometrical Acoustics unit tests
+ UNITY_PLUGIN| # Resonance Audio Unity plugin
+ WWISE_AUTHORING_PLUGIN| # Resonance Audio Wwise authoring plugin
+ WWISE_SOUND_ENGINE_PLUGIN| # Resonance Audio Wwise sound engine plugin
+ FMOD_PLUGIN| # Resonance Audio FMOD plugin
+ VST_MONITOR_PLUGIN] # Resonance Audio VST monitor plugin
+
+ -p= | --profile=[Debug|Release], default: Release
+
+ --verbose_make # Enables verbose make/build output.
+ --android_toolchain # Use Android NDK toolchain (may need adjustments to ANDROID_NDK,
+ # ANDROID_NATIVE_API_LEVEL, ANDROID_ABI script variables).
+ --ios_os_toolchain # Use iOS ARM toolchain.
+ --ios_simulator_toolchain # Use iOS X86 simulator toolchain.
+ --msvc_dynamic_runtime # Enables dynamic runtime environment linking in MSVC builds.
+EOF
+exit
+}
+
+BUILD_TARGET=""
+
+for i in "$@"
+do
+ case $i in
+ -p=*|--profile=*)
+ PROFILE="${i#*=}"
+ shift # past argument=value
+ ;;
+
+ -t=*|--target=*)
+ BUILD_TARGET="${i#*=}"
+ shift # past argument=value
+ ;;
+
+ --verbose_make)
+ CONFIG_FLAGS+=(-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON)
+ shift # past argument with no value
+ ;;
+
+ --android_toolchain)
+ CONFIG_FLAGS+=(-DCMAKE_TOOLCHAIN_FILE=./third_party/android-cmake/android.toolchain.cmake)
+ CONFIG_FLAGS+=(-DANDROID_NDK="${ANDROID_NDK}")
+ CONFIG_FLAGS+=(-DANDROID_NATIVE_API_LEVEL="${ANDROID_NATIVE_API_LEVEL}")
+ CONFIG_FLAGS+=(-DANDROID_ABI="${ANDROID_ABI}")
+ shift # past argument with no value
+ ;;
+
+ --ios_os_toolchain)
+ CONFIG_FLAGS+=(-DCMAKE_TOOLCHAIN_FILE=./third_party/ios-cmake/ios.toolchain.cmake)
+ CONFIG_FLAGS+=(-DIOS_PLATFORM=OS)
+ shift # past argument with no value
+ ;;
+
+ --ios_simulator_toolchain)
+ CONFIG_FLAGS+=(-DCMAKE_TOOLCHAIN_FILE=./third_party/ios-cmake/ios.toolchain.cmake)
+ CONFIG_FLAGS+=(-DIOS_PLATFORM=SIMULATOR64)
+ shift # past argument with no value
+ ;;
+
+ --msvc_dynamic_runtime)
+ CONFIG_FLAGS+=(-DSTATIC_MSVC_RUNTIME:BOOL=OFF)
+ shift # past argument with no value
+ ;;
+
+ *)
+ # unknown option
+ echo "Unknown option: ${i}"
+ show_help
+ ;;
+ esac
+done
+
+[[ -z "$BUILD_TARGET" ]] && show_help
+
+# Number of CPU cores/parallel compilation instances (for Darwin/Linux builds)
+NUM_CORES=8
+
+# Create build environment.
+rm -fr build && mkdir build && cd build
+
+case "$(uname -s)" in
+ Darwin)
+ BUILD_FLAGS+=(-j "${NUM_CORES}")
+ cmake -DBUILD_"${BUILD_TARGET}":BOOL=ON\
+ "${CONFIG_FLAGS[@]}" "$@" ..
+ ;;
+
+ Linux)
+ BUILD_FLAGS+=(-j "${NUM_CORES}")
+ cmake -DBUILD_"${BUILD_TARGET}":BOOL=ON\
+ "${CONFIG_FLAGS[@]}" "$@" ..
+ ;;
+
+ CYGWIN*|MINGW*|MSYS*)
+ cmake -G"${MSVC_GENERATOR}"\
+ -DBUILD_"${BUILD_TARGET}":BOOL=ON\
+ "${CONFIG_FLAGS[@]}" "$@" ..
+ ;;
+
+ *)
+ ;;
+esac
+
+INSTALL_TARGET="install"
+if echo "${BUILD_TARGET}" | grep -q "TESTS"; then
+ INSTALL_TARGET=""
+fi
+
+cmake --build . --config "${PROFILE}" --target "${INSTALL_TARGET}" -- "${BUILD_FLAGS[@]}"
diff --git a/src/3rdparty/resonance-audio/patches/0001-resonance_audio-fix-C-20-build.patch b/src/3rdparty/resonance-audio/patches/0001-resonance_audio-fix-C-20-build.patch
new file mode 100644
index 000000000..2de42ad26
--- /dev/null
+++ b/src/3rdparty/resonance-audio/patches/0001-resonance_audio-fix-C-20-build.patch
@@ -0,0 +1,36 @@
+From f8c9ec7cfabb59977629f303edf7cf90ca8521d3 Mon Sep 17 00:00:00 2001
+From: Marc Mutz <marc.mutz@qt.io>
+Date: Tue, 28 Jun 2022 19:38:13 +0200
+Subject: [PATCH] AlignedAllocator: fix C++20 build
+
+C++17 deprecated, and C++20 removed, the nested pointer, size_type
+etc. typedefs in std::allocator. Since C++11, the way to get these
+types is to go via allocator_traits, so do that.
+
+This breaks compatibility with C++ < 11.
+
+Fixes #61
+---
+ .../resonance_audio/base/aligned_allocator.h | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/resonance_audio/base/aligned_allocator.h b/resonance_audio/base/aligned_allocator.h
+index ac60e8292..628ccaa02 100644
+--- a/resonance_audio/base/aligned_allocator.h
++++ b/resonance_audio/base/aligned_allocator.h
+@@ -72,9 +72,9 @@ void AllignedFree(PointerType mem_block_aligned) {
+ template <typename Type, size_t Alignment>
+ class AlignedAllocator : public std::allocator<Type> {
+ public:
+- typedef typename std::allocator<Type>::pointer Pointer;
+- typedef typename std::allocator<Type>::const_pointer ConstPointer;
+- typedef typename std::allocator<Type>::size_type SizeType;
++ using Pointer = typename std::allocator_traits<std::allocator<Type>>::pointer;
++ using ConstPointer = typename std::allocator_traits<std::allocator<Type>>::const_pointer;
++ using SizeType = typename std::allocator_traits<std::allocator<Type>>::size_type;
+
+ AlignedAllocator() { StaticAlignmentCheck<sizeof(Type), Alignment>(); }
+
+--
+2.25.1
+
diff --git a/src/3rdparty/resonance-audio/platforms/CMakeLists.txt b/src/3rdparty/resonance-audio/platforms/CMakeLists.txt
new file mode 100644
index 000000000..3dcdc2dfc
--- /dev/null
+++ b/src/3rdparty/resonance-audio/platforms/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright 2018 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+if (BUILD_VST_MONITOR_PLUGIN)
+ add_subdirectory(vst)
+endif (BUILD_VST_MONITOR_PLUGIN)
+
+if (BUILD_FMOD_PLUGIN)
+ add_subdirectory(fmod)
+endif (BUILD_FMOD_PLUGIN)
+
+if (BUILD_WWISE_AUTHORING_PLUGIN OR BUILD_WWISE_SOUND_ENGINE_PLUGIN)
+ add_subdirectory(wwise)
+endif (BUILD_WWISE_AUTHORING_PLUGIN OR BUILD_WWISE_SOUND_ENGINE_PLUGIN)
+
+if (BUILD_UNITY_PLUGIN OR BUILD_GEOMETRICAL_ACOUSTICS_TESTS)
+ add_subdirectory(unity)
+endif (BUILD_UNITY_PLUGIN OR BUILD_GEOMETRICAL_ACOUSTICS_TESTS)
diff --git a/src/3rdparty/resonance-audio/platforms/common/room_effects_utils.cc b/src/3rdparty/resonance-audio/platforms/common/room_effects_utils.cc
new file mode 100644
index 000000000..3c74f26dc
--- /dev/null
+++ b/src/3rdparty/resonance-audio/platforms/common/room_effects_utils.cc
@@ -0,0 +1,327 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "platforms/common/room_effects_utils.h"
+
+#include <algorithm>
+#include <cmath>
+#include <numeric>
+
+namespace vraudio {
+
+namespace {
+
+// Air absorption coefficients at 20 degrees Celsius and 50% relative humidity,
+// according to:
+// http://www.music.mcgill.ca/~gary/courses/papers/Moorer-Reverb-CMJ-1979.pdf
+// These coefficients have been extrapolated to other frequencies by fitting
+// an exponential curve:
+//
+// m = a + be^(-cx) where:
+//
+// a = -0.00259118
+// b = 0.003173474
+// c = -0.0002491554
+//
+const float kAirAbsorptionCoefficients[]{0.0006f, 0.0006f, 0.0007f,
+ 0.0008f, 0.0010f, 0.0015f,
+ 0.0026f, 0.0060f, 0.0207f};
+
+// Room materials with the corresponding absorption coefficients.
+const RoomMaterial kRoomMaterials[static_cast<size_t>(
+ MaterialName::kNumMaterialNames)] = {
+ {MaterialName::kTransparent,
+ // 31.25 62.5 125 250 500 1000 2000 4000 8000 Hz.
+ {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}},
+ {MaterialName::kAcousticCeilingTiles,
+ {0.672f, 0.675f, 0.700f, 0.660f, 0.720f, 0.920f, 0.880f, 0.750f, 1.000f}},
+ {MaterialName::kBrickBare,
+ {0.030f, 0.030f, 0.030f, 0.030f, 0.030f, 0.040f, 0.050f, 0.070f, 0.140f}},
+
+ {MaterialName::kBrickPainted,
+ {0.006f, 0.007f, 0.010f, 0.010f, 0.020f, 0.020f, 0.020f, 0.030f, 0.060f}},
+
+ {MaterialName::kConcreteBlockCoarse,
+ {0.360f, 0.360f, 0.360f, 0.440f, 0.310f, 0.290f, 0.390f, 0.250f, 0.500f}},
+
+ {MaterialName::kConcreteBlockPainted,
+ {0.092f, 0.090f, 0.100f, 0.050f, 0.060f, 0.070f, 0.090f, 0.080f, 0.160f}},
+
+ {MaterialName::kCurtainHeavy,
+ {0.073f, 0.106f, 0.140f, 0.350f, 0.550f, 0.720f, 0.700f, 0.650f, 1.000f}},
+
+ {MaterialName::kFiberGlassInsulation,
+ {0.193f, 0.220f, 0.220f, 0.820f, 0.990f, 0.990f, 0.990f, 0.990f, 1.000f}},
+
+ {MaterialName::kGlassThin,
+ {0.180f, 0.169f, 0.180f, 0.060f, 0.040f, 0.030f, 0.020f, 0.020f, 0.040f}},
+
+ {MaterialName::kGlassThick,
+ {0.350f, 0.350f, 0.350f, 0.250f, 0.180f, 0.120f, 0.070f, 0.040f, 0.080f}},
+
+ {MaterialName::kGrass,
+ {0.05f, 0.05f, 0.15f, 0.25f, 0.40f, 0.55f, 0.60f, 0.60f, 0.60f}},
+
+ {MaterialName::kLinoleumOnConcrete,
+ {0.020f, 0.020f, 0.020f, 0.030f, 0.030f, 0.030f, 0.030f, 0.020f, 0.040f}},
+
+ {MaterialName::kMarble,
+ {0.010f, 0.010f, 0.010f, 0.010f, 0.010f, 0.010f, 0.020f, 0.020f, 0.040f}},
+
+ {MaterialName::kMetal,
+ {0.030f, 0.035f, 0.04f, 0.04f, 0.05f, 0.05f, 0.05f, 0.07f, 0.09f}},
+
+ {MaterialName::kParquetOnConcrete,
+ {0.028f, 0.030f, 0.040f, 0.040f, 0.070f, 0.060f, 0.060f, 0.070f, 0.140f}},
+
+ {MaterialName::kPlasterRough,
+ {0.017f, 0.018f, 0.020f, 0.030f, 0.040f, 0.050f, 0.040f, 0.030f, 0.060f}},
+
+ {MaterialName::kPlasterSmooth,
+ {0.011f, 0.012f, 0.013f, 0.015f, 0.020f, 0.030f, 0.040f, 0.050f, 0.100f}},
+
+ {MaterialName::kPlywoodPanel,
+ {0.40f, 0.34f, 0.28f, 0.22f, 0.17f, 0.09f, 0.10f, 0.11f, 0.22f}},
+
+ {MaterialName::kPolishedConcreteOrTile,
+ {0.008f, 0.008f, 0.010f, 0.010f, 0.015f, 0.020f, 0.020f, 0.020f, 0.040f}},
+
+ {MaterialName::kSheetrock,
+ {0.290f, 0.279f, 0.290f, 0.100f, 0.050f, 0.040f, 0.070f, 0.090f, 0.180f}},
+
+ {MaterialName::kWaterOrIceSurface,
+ {0.006f, 0.006f, 0.008f, 0.008f, 0.013f, 0.015f, 0.020f, 0.025f, 0.050f}},
+
+ {MaterialName::kWoodCeiling,
+ {0.150f, 0.147f, 0.150f, 0.110f, 0.100f, 0.070f, 0.060f, 0.070f, 0.140f}},
+
+ {MaterialName::kWoodPanel,
+ {0.280f, 0.280f, 0.280f, 0.220f, 0.170f, 0.090f, 0.100f, 0.110f, 0.220f}},
+
+ {MaterialName::kUniform,
+ {0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f}}};
+
+// Frequency threshold for low pass filtering. This is the -3dB frequency.
+const float kCutoffFrequency = 800.0f;
+
+// Number of averaging bands for computing the average absorption coefficient.
+const int kNumAveragingBands = 3;
+
+// Average absorption coefficients are computed based on 3 octave bands (500Hz,
+// 1kHz, 2kHz) from the user specified materials. The 500Hz band has index 4.
+const int kStartingBand = 4;
+
+// Default scaling factor of the reverberation tail. This value is
+// multiplied by the user-defined factor in order to allow for the change of
+// gain in the reverb tail from the UI. Updates to the gain value are made to
+// match the previous reverb implementation's loudness.
+const float kDefaultReverbGain = 0.045f;
+
+// Constant used in Eyring's equation to compute RT60.
+const float kEyringConstant = 0.161f;
+
+// Computes RT60 time in the given frequency band according to Eyring.
+inline float ComputeRt60Eyring(float total_area, float mean_absorption,
+ size_t band, float volume) {
+ return kEyringConstant * volume /
+ (-total_area * std::log(1.0f - mean_absorption) +
+ 4.0f * kAirAbsorptionCoefficients[band] * volume);
+}
+
+inline std::vector<float> ComputeShoeBoxSurfaceAreas(
+ const RoomProperties& room_properties) {
+ const float room_width = room_properties.dimensions[0];
+ const float room_height = room_properties.dimensions[1];
+ const float room_depth = room_properties.dimensions[2];
+ const float left_right_area = room_height * room_depth;
+ const float top_bottom_area = room_width * room_depth;
+ const float front_back_area = room_width * room_height;
+ return std::vector<float>{left_right_area, left_right_area, top_bottom_area,
+ top_bottom_area, front_back_area, front_back_area};
+}
+
+// Generates average reflection coefficients for each room model surface.
+//
+// @param room_properties Struct containing properties of the shoe-box room
+// model.
+// @param coefficients Reflection coefficients for each surface.
+void GenerateReflectionCoefficients(const RoomProperties& room_properties,
+ float* coefficients) {
+ DCHECK(coefficients);
+ // Loop through all the surfaces and compute the average absorption
+ // coefficient for 3 bands (500Hz, 1kHz and 2kHz).
+ for (size_t surface = 0; surface < kNumRoomSurfaces; ++surface) {
+ const size_t material_index =
+ static_cast<size_t>(room_properties.material_names[surface]);
+ // Absorption coefficients in all bands for the current surface.
+ const auto& absorption_coefficients =
+ kRoomMaterials[material_index].absorption_coefficients;
+ // Compute average absorption coefficients for each surface.
+ float average_absorption_coefficient =
+ std::accumulate(std::begin(absorption_coefficients) + kStartingBand,
+ std::begin(absorption_coefficients) + kStartingBand +
+ kNumAveragingBands,
+ 0.0f) /
+ static_cast<float>(kNumAveragingBands);
+ // Compute a reflection coefficient for each surface.
+ coefficients[surface] =
+ std::min(1.0f, std::sqrt(1.0f - average_absorption_coefficient));
+ }
+}
+
+// Uses the Eyring's equation to estimate RT60 values (reverb time in seconds)
+// in |kNumReverbOctaveBands| octave bands, including the correction for air
+// absorption. The equation is applied as defined in:
+// https://arauacustica.com/files/publicaciones_relacionados/pdf_esp_26.pdf
+//
+
+void GenerateRt60Values(const RoomProperties& room_properties,
+ float* rt60_values) {
+ DCHECK(rt60_values);
+ // Compute the shoe-box room volume.
+ const float room_volume = room_properties.dimensions[0] *
+ room_properties.dimensions[1] *
+ room_properties.dimensions[2];
+ if (room_volume < std::numeric_limits<float>::epsilon()) {
+ // RT60 values will be all zeros, if the room volume is zero.
+ return;
+ }
+
+ // Compute surface areas of the shoe-box room.
+ const std::vector<float> all_surface_areas =
+ ComputeShoeBoxSurfaceAreas(room_properties);
+ const float total_area =
+ std::accumulate(all_surface_areas.begin(), all_surface_areas.end(), 0.0f);
+ DCHECK_GT(total_area, 0.0f);
+ // Loop through each band and compute the RT60 values.
+ for (size_t band = 0; band < kNumReverbOctaveBands; ++band) {
+ // Initialize the effective absorbing area.
+ float absorbing_area = 0.0f;
+ for (size_t surface = 0; surface < kNumRoomSurfaces; ++surface) {
+ const size_t material_index =
+ static_cast<size_t>(room_properties.material_names[surface]);
+ // Compute the effective absorbing area based on the absorption
+ // coefficients for the current band and all the surfaces.
+ absorbing_area +=
+ kRoomMaterials[material_index].absorption_coefficients[band] *
+ all_surface_areas[surface];
+ }
+ DCHECK_GT(absorbing_area, 0.0f);
+ const float mean_absorption = std::min(absorbing_area / total_area, 1.0f);
+
+ // Compute RT60 time in this band according to Eyring.
+ rt60_values[band] =
+ ComputeRt60Eyring(total_area, mean_absorption, band, room_volume);
+ }
+}
+
+// Modifies the RT60 values by the given |brightness_modifier| and
+// |time_scaler|.
+void ModifyRT60Values(float brightness_modifier, float time_scaler,
+ float* rt60_values) {
+ DCHECK(rt60_values);
+ for (size_t band = 0; band < kNumReverbOctaveBands; ++band) {
+ // Linearly scale calculated reverb times according to the user specified
+ // |brightness_modifier| and |time_scaler| values.
+ rt60_values[band] *=
+ (1.0f + brightness_modifier * static_cast<float>(band + 1) /
+ static_cast<float>(kNumReverbOctaveBands)) *
+ time_scaler;
+ }
+}
+
+} // namespace
+
+ReflectionProperties ComputeReflectionProperties(
+ const RoomProperties& room_properties) {
+ ReflectionProperties reflection_properties;
+ std::copy(std::begin(room_properties.position),
+ std::end(room_properties.position),
+ std::begin(reflection_properties.room_position));
+ std::copy(std::begin(room_properties.rotation),
+ std::end(room_properties.rotation),
+ std::begin(reflection_properties.room_rotation));
+ std::copy(std::begin(room_properties.dimensions),
+ std::end(room_properties.dimensions),
+ std::begin(reflection_properties.room_dimensions));
+ reflection_properties.cutoff_frequency = kCutoffFrequency;
+ GenerateReflectionCoefficients(room_properties,
+ reflection_properties.coefficients);
+ reflection_properties.gain = room_properties.reflection_scalar;
+ return reflection_properties;
+}
+
+ReverbProperties ComputeReverbProperties(
+ const RoomProperties& room_properties) {
+ ReverbProperties reverb_properties;
+ GenerateRt60Values(room_properties, reverb_properties.rt60_values);
+ ModifyRT60Values(room_properties.reverb_brightness,
+ room_properties.reverb_time, reverb_properties.rt60_values);
+ reverb_properties.gain = kDefaultReverbGain * room_properties.reverb_gain;
+ return reverb_properties;
+}
+
+ReverbProperties ComputeReverbPropertiesFromRT60s(const float* rt60_values,
+ float brightness_modifier,
+ float time_scalar,
+ float gain_multiplier) {
+ DCHECK(rt60_values);
+
+ ReverbProperties reverb_properties;
+ std::copy_n(rt60_values, kNumReverbOctaveBands,
+ reverb_properties.rt60_values);
+ ModifyRT60Values(brightness_modifier, time_scalar,
+ reverb_properties.rt60_values);
+ reverb_properties.gain = kDefaultReverbGain * gain_multiplier;
+ return reverb_properties;
+}
+
+float ComputeRoomEffectsGain(const WorldPosition& source_position,
+ const WorldPosition& room_position,
+ const WorldRotation& room_rotation,
+ const WorldPosition& room_dimensions) {
+ const float room_volume =
+ room_dimensions[0] * room_dimensions[1] * room_dimensions[2];
+ if (room_volume < std::numeric_limits<float>::epsilon()) {
+ // No room effects should be present when the room volume is zero.
+ return 0.0f;
+ }
+
+ // Compute the relative source position with respect to the room.
+ WorldPosition relative_source_position;
+ GetRelativeDirection(room_position, room_rotation, source_position,
+ &relative_source_position);
+ WorldPosition closest_position_in_room;
+ GetClosestPositionInAabb(relative_source_position, room_dimensions,
+ &closest_position_in_room);
+ // Shift the attenuation curve by 1.0f to avoid zero division.
+ const float distance_to_room =
+ 1.0f + (relative_source_position - closest_position_in_room).norm();
+ return 1.0f / (distance_to_room * distance_to_room);
+}
+
+RoomMaterial GetRoomMaterial(size_t material_index) {
+ DCHECK_LT(material_index,
+ static_cast<size_t>(MaterialName::kNumMaterialNames));
+ return kRoomMaterials[material_index];
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/platforms/common/room_effects_utils.h b/src/3rdparty/resonance-audio/platforms/common/room_effects_utils.h
new file mode 100644
index 000000000..edaf7849d
--- /dev/null
+++ b/src/3rdparty/resonance-audio/platforms/common/room_effects_utils.h
@@ -0,0 +1,83 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_PLATFORM_COMMON_ROOM_EFFECTS_UTILS_H_
+#define RESONANCE_AUDIO_PLATFORM_COMMON_ROOM_EFFECTS_UTILS_H_
+
+#include <vector>
+
+#include "api/resonance_audio_api.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+#include "platforms/common/room_properties.h"
+
+namespace vraudio {
+
+// Room material properties.
+struct RoomMaterial {
+ // Material surface integer identifier.
+ MaterialName name;
+
+ // An array of absorption coefficients defined in octave bands.
+ float absorption_coefficients[kNumReverbOctaveBands];
+};
+
+// Generates |ReflectionProperties| based on given |room_properties|.
+//
+// @param room_properties Room properties.
+ReflectionProperties ComputeReflectionProperties(
+ const RoomProperties& room_properties);
+
+// Generates |ReverbProperties| based on given |room_properties|.
+//
+// @param room_properties Room properties.
+ReverbProperties ComputeReverbProperties(const RoomProperties& room_properties);
+
+// Generates |ReverbProperties| by directly setting the RT60 values, subject
+// to modifications by |brightness_modifier| and |time_scaler|.
+//
+// @param rt60_values RT60 values.
+// @param brightness_modifier Modifier adjusting the brightness of reverb.
+// @param time_scalar Modifier scaling the reverb time.
+// @param gain_multiplier Modifier scaling the reverb gain.
+ReverbProperties ComputeReverbPropertiesFromRT60s(const float* rt60_values,
+ float brightness_modifier,
+ float time_scalar,
+ float gain_multiplier);
+
+// Calculates the gain value for |source_position| with respect to the given
+// room properties. The sound level for the room effects will remain the same
+// inside the room, otherwise, it will decrease with a linear ramp from the
+// closest point on the room.
+//
+// @param source_position World position of the source.
+// @param room_position Center position of the room.
+// @param room_rotation Orientation of the room.
+// @param room_dimensions Dimensions of the room..
+// @return Attenuation (gain) value in range [0.0f, 1.0f].
+float ComputeRoomEffectsGain(const WorldPosition& source_position,
+ const WorldPosition& room_position,
+ const WorldRotation& room_rotation,
+ const WorldPosition& room_dimensions);
+
+// Gets the room material properties from a material index.
+//
+// @param material_index Index of the material.
+RoomMaterial GetRoomMaterial(size_t material_index);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_PLATFORM_COMMON_ROOM_EFFECTS_UTILS_H_
diff --git a/src/3rdparty/resonance-audio/platforms/common/room_effects_utils_test.cc b/src/3rdparty/resonance-audio/platforms/common/room_effects_utils_test.cc
new file mode 100644
index 000000000..c742800ee
--- /dev/null
+++ b/src/3rdparty/resonance-audio/platforms/common/room_effects_utils_test.cc
@@ -0,0 +1,197 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "platforms/common/room_effects_utils.h"
+
+#include <memory>
+#include <string>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "platforms/common/room_properties.h"
+
+namespace vraudio {
+
+namespace {
+
+// Permitted error in the computed room effects coefficients against the
+// expected values. This value is 1e-4 as expected values are rounded for
+// readability.
+const float kCoefficientsEpsilon = 1e-4f;
+
+// Room position.
+const float kRoomPosition[3] = {10.0f, 10.0f, 10.0f};
+
+// Room dimensions (width x height x depth) of a medium size hall (shoe-box
+// model).
+const float kRoomDimensions[3] = {5.85f, 4.65f, 15.0f};
+
+// Reverb time adjustment parameters.
+const float kBrightnessModifier = 0.0f;
+
+const float kTimeModifier = 1.0f;
+
+// Expected set of reflection coefficients for given materials.
+const float kExpectedReflectionCoefficients[kNumRoomSurfaces] = {
+ 0.9381f, 0.9381f, 0.9679f, 0.9381f, 0.9381f, 0.9381f};
+
+// Expected -3dB cut-off frequency of the early reflections low-pass filter.
+const float kExpectedCutoffFrequency = 800.0f;
+
+// Expected reverb time values.
+const float kExpectedRt60Values[] = {0.47286f, 0.56928f, 0.69632f,
+ 0.89532f, 1.09418f, 1.90940f,
+ 1.67225f, 1.34293f, 0.56304f};
+
+// Expected reverb gain value.
+const float kExpectedGain = 0.045f;
+
+} // namespace
+
+class RoomEffectsUtilsTest : public ::testing::Test {
+ protected:
+ RoomEffectsUtilsTest() {}
+ // Virtual methods from ::testing::Test
+ ~RoomEffectsUtilsTest() override {}
+ void SetUp() override {
+ // Set up room properties.
+ room_properties_.position[0] = kRoomPosition[0];
+ room_properties_.position[1] = kRoomPosition[1];
+ room_properties_.position[2] = kRoomPosition[2];
+ room_properties_.dimensions[0] = kRoomDimensions[0];
+ room_properties_.dimensions[1] = kRoomDimensions[1];
+ room_properties_.dimensions[2] = kRoomDimensions[2];
+ // Set the material to 'Parquet on contrete' for the floor and 'Plywood
+ // panel' for the walls and the ceiling.
+ const std::vector<MaterialName> kMaterialNames = {
+ MaterialName::kPlywoodPanel, MaterialName::kPlywoodPanel,
+ MaterialName::kParquetOnConcrete, MaterialName::kPlywoodPanel,
+ MaterialName::kPlywoodPanel, MaterialName::kPlywoodPanel};
+ for (size_t i = 0; i < kNumRoomSurfaces; ++i) {
+ room_properties_.material_names[i] = kMaterialNames[i];
+ }
+ room_properties_.reverb_brightness = kBrightnessModifier;
+ room_properties_.reverb_time = kTimeModifier;
+ }
+ void TearDown() override {}
+
+ RoomProperties room_properties_;
+};
+
+// Tests if the ComputeReflectionProperties() function returns the same
+// reflection coefficient values as computed in MATLAB. Also, checks if other
+// ReflectionProperties data members are set up correctly.
+TEST_F(RoomEffectsUtilsTest, ComputeReflectionPropertiesTest) {
+ const auto reflection_properties =
+ ComputeReflectionProperties(room_properties_);
+
+ // Check if the cutoff frequency is correct.
+ EXPECT_NEAR(kExpectedCutoffFrequency, reflection_properties.cutoff_frequency,
+ kCoefficientsEpsilon);
+ // Check if the reflection coefficients for each surface are correct.
+ for (size_t i = 0; i < kNumRoomSurfaces; ++i) {
+ EXPECT_NEAR(kExpectedReflectionCoefficients[i],
+ reflection_properties.coefficients[i], kCoefficientsEpsilon);
+ }
+}
+
+// Tests if the ComputeReverbProperties() function returns the same reverb
+// time values as computed in MATLAB. Also, checks if other ReverbProperties
+// data members are set up correctly.
+TEST_F(RoomEffectsUtilsTest, ComputeReverbPropertiesTest) {
+ const auto reverb_properties = ComputeReverbProperties(room_properties_);
+
+ // Check if the reverb time values in the octave bands are correct.
+ for (size_t band = 0; band < kNumReverbOctaveBands; ++band) {
+ EXPECT_NEAR(kExpectedRt60Values[band], reverb_properties.rt60_values[band],
+ kCoefficientsEpsilon);
+ }
+
+ // Check the gain.
+ EXPECT_NEAR(kExpectedGain, reverb_properties.gain, kCoefficientsEpsilon);
+}
+
+// Tests if the ComputeReflectionProperties() and ComputeReverbProperties()
+// functions return reflection coefficients and RT60 values of 0 if there are no
+// materials set.
+TEST_F(RoomEffectsUtilsTest, ComputeRoomPropertiesNoMaterialsTest) {
+ for (size_t i = 0; i < kNumRoomSurfaces; ++i) {
+ room_properties_.material_names[i] = MaterialName::kTransparent;
+ }
+ const auto reflection_properties =
+ ComputeReflectionProperties(room_properties_);
+ const auto reverb_properties = ComputeReverbProperties(room_properties_);
+
+ // Check if the reflection coefficient values are near 0.
+ for (size_t surface = 0; surface < kNumRoomSurfaces; ++surface) {
+ EXPECT_NEAR(0.0f, reflection_properties.coefficients[surface],
+ kCoefficientsEpsilon);
+ }
+ // Check if the RT60 values are near 0.
+ for (size_t band = 0; band < kNumReverbOctaveBands; ++band) {
+ EXPECT_NEAR(0.0f, reverb_properties.rt60_values[band],
+ kCoefficientsEpsilon);
+ }
+}
+
+// Tests if the ComputeReverbProperties() function returns RT60 values of 0
+// seconds if all the materials are set to 'transparent' (full absorption).
+TEST_F(RoomEffectsUtilsTest, ComputeReverbPropertiesFullAbsorptionTest) {
+ for (size_t i = 0; i < kNumRoomSurfaces; ++i) {
+ room_properties_.material_names[i] = MaterialName::kTransparent;
+ }
+ const auto reverb_properties = ComputeReverbProperties(room_properties_);
+
+ // Check if the RT60 values are near 0.
+ for (size_t band = 0; band < kNumReverbOctaveBands; ++band) {
+ EXPECT_NEAR(0.0f, reverb_properties.rt60_values[band],
+ kCoefficientsEpsilon);
+ }
+ // Check the gain.
+ EXPECT_NEAR(kExpectedGain, reverb_properties.gain, kCoefficientsEpsilon);
+}
+
+// Tests the room effects gain computation against the pre-computed result when
+// source is inside the room.
+TEST_F(RoomEffectsUtilsTest, ComputeRoomEffectsGainInsideRoomTest) {
+ const WorldPosition kListenerPosition(0.0f, 0.5f, 0.0f);
+ const WorldPosition kSourcePosition(0.5f, 0.3f, 0.2f);
+ const WorldPosition kRoomPosition(0.5f, 0.5f, 0.5f);
+ const WorldRotation kRoomRotation = WorldRotation();
+ const WorldPosition kRoomDimensions(1.0f, 1.0f, 1.0f);
+ const float kExpectedGain = 1.0f;
+
+ const float gain = ComputeRoomEffectsGain(kSourcePosition, kRoomPosition,
+ kRoomRotation, kRoomDimensions);
+ EXPECT_NEAR(kExpectedGain, gain, kEpsilonFloat);
+}
+
+// Tests the room effects gain computation against the pre-computed result when
+// source is outside the room.
+TEST_F(RoomEffectsUtilsTest, ComputeRoomEffectsGainOutsideRoomTest) {
+ const WorldPosition kListenerPosition(0.0f, 0.5f, 0.0f);
+ const WorldPosition kSourcePosition(2.0f, 0.5f, 0.5f);
+ const WorldPosition kRoomPosition(0.5f, 0.5f, 0.5f);
+ const WorldRotation kRoomRotation = WorldRotation();
+ const WorldPosition kRoomDimensions(1.0f, 1.0f, 1.0f);
+ const float kExpectedGain = 0.25f;
+
+ const float gain = ComputeRoomEffectsGain(kSourcePosition, kRoomPosition,
+ kRoomRotation, kRoomDimensions);
+ EXPECT_NEAR(kExpectedGain, gain, kEpsilonFloat);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/platforms/common/room_properties.h b/src/3rdparty/resonance-audio/platforms/common/room_properties.h
new file mode 100644
index 000000000..55056c3bf
--- /dev/null
+++ b/src/3rdparty/resonance-audio/platforms/common/room_properties.h
@@ -0,0 +1,123 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef RESONANCE_AUDIO_PLATFORM_COMMON_ROOM_PROPERTIES_H_
+#define RESONANCE_AUDIO_PLATFORM_COMMON_ROOM_PROPERTIES_H_
+
+namespace vraudio {
+
+// Room surface material names, used to set room properties.
+// Note that this enum is C-compatible by design to be used across external
+// C/C++ and C# implementations.
+enum MaterialName {
+ kTransparent = 0,
+ kAcousticCeilingTiles,
+ kBrickBare,
+ kBrickPainted,
+ kConcreteBlockCoarse,
+ kConcreteBlockPainted,
+ kCurtainHeavy,
+ kFiberGlassInsulation,
+ kGlassThin,
+ kGlassThick,
+ kGrass,
+ kLinoleumOnConcrete,
+ kMarble,
+ kMetal,
+ kParquetOnConcrete,
+ kPlasterRough,
+ kPlasterSmooth,
+ kPlywoodPanel,
+ kPolishedConcreteOrTile,
+ kSheetrock,
+ kWaterOrIceSurface,
+ kWoodCeiling,
+ kWoodPanel,
+ kUniform,
+ kNumMaterialNames
+};
+
+// Acoustic room properties. This struct can be used to describe an acoustic
+// environment with a given geometry and surface properties.
+// Note that this struct is C-compatible by design to be used across external
+// C/C++ and C# implementations.
+struct RoomProperties {
+ // Constructs |RoomProperties| with the default values.
+ RoomProperties()
+ : position{0.0f, 0.0f, 0.0f},
+ rotation{0.0f, 0.0f, 0.0f, 1.0f},
+ dimensions{0.0f, 0.0f, 0.0f},
+ material_names{MaterialName::kTransparent, MaterialName::kTransparent,
+ MaterialName::kTransparent, MaterialName::kTransparent,
+ MaterialName::kTransparent, MaterialName::kTransparent},
+ reflection_scalar(1.0f),
+ reverb_gain(1.0f),
+ reverb_time(1.0f),
+ reverb_brightness(0.0f) {}
+
+ // Center position of the room in world space, uses right-handed coordinate
+ // system.
+ float position[3];
+
+ // Rotation (quaternion) of the room in world space, uses right-handed
+ // coordinate system.
+ float rotation[4];
+
+ // Size of the shoebox room in world space, uses right-handed coordinate
+ // system.
+ float dimensions[3];
+
+ // Material name of each surface of the shoebox room in this order:
+ // [0] (-)ive x-axis wall (left)
+ // [1] (+)ive x-axis wall (right)
+ // [2] (-)ive y-axis wall (bottom)
+ // [3] (+)ive y-axis wall (top)
+ // [4] (-)ive z-axis wall (front)
+ // [5] (+)ive z-axis wall (back)
+ MaterialName material_names[6];
+
+ // User defined uniform scaling factor for all reflection coefficients.
+ float reflection_scalar;
+
+ // User defined reverb tail gain multiplier.
+ float reverb_gain;
+
+ // Adjusts the reverberation time across all frequency bands. RT60 values
+ // are multiplied by this factor. Has no effect when set to 1.0f.
+ float reverb_time;
+
+ // Controls the slope of a line from the lowest to the highest RT60 values
+ // (increases high frequency RT60s when positive, decreases when negative).
+ // Has no effect when set to 0.0f.
+ float reverb_brightness;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_PLATFORM_COMMON_ROOM_PROPERTIES_H_
diff --git a/src/3rdparty/resonance-audio/platforms/common/utils.cc b/src/3rdparty/resonance-audio/platforms/common/utils.cc
new file mode 100644
index 000000000..2b3e34ac2
--- /dev/null
+++ b/src/3rdparty/resonance-audio/platforms/common/utils.cc
@@ -0,0 +1,59 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "platforms/common/utils.h"
+
+namespace vraudio {
+
+void FlipZAxis(Eigen::Matrix4f* matrix) {
+ // This operation is equivalent to:
+ // matrix = flipZ * matrix * flipZ
+ // where flipZ is the scale(1, 1, -1) matrix.
+ (*matrix)(0, 2) *= -1.0f;
+ (*matrix)(1, 2) *= -1.0f;
+ (*matrix)(2, 0) *= -1.0f;
+ (*matrix)(2, 1) *= -1.0f;
+ (*matrix)(2, 3) *= -1.0f;
+ (*matrix)(3, 2) *= -1.0f;
+}
+
+Eigen::Quaternionf GetQuaternion(const Eigen::Matrix4f& matrix) {
+ const Eigen::Matrix3f rotation_matrix = matrix.block(0, 0, 3, 3);
+ Eigen::Quaternionf quaternion(rotation_matrix);
+ return quaternion.normalized();
+}
+
+Eigen::Vector3f GetPosition(const Eigen::Matrix4f& matrix) {
+ return Eigen::Vector3f(matrix.col(3).head<3>());
+}
+
+Eigen::Matrix4f GetTransformMatrix(const Eigen::Vector3f& position,
+ const Eigen::Vector3f& forward,
+ const Eigen::Vector3f& up) {
+ Eigen::Matrix4f transform_matrix;
+ // Compose the homogeneous vectors for the transformation matrix.
+ const Eigen::Vector3f right = up.cross(forward);
+ const Eigen::Vector4f position_4(position.x(), position.y(), position.z(),
+ 1.0f);
+ const Eigen::Vector4f forward_4(forward.x(), forward.y(), forward.z(), 0.0f);
+ const Eigen::Vector4f up_4(up.x(), up.y(), up.z(), 0.0f);
+ const Eigen::Vector4f right_4(right.x(), right.y(), right.z(), 0.0f);
+ // Fill in the transformation matrix.
+ transform_matrix << right_4, up_4, forward_4, position_4;
+ return transform_matrix;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/platforms/common/utils.h b/src/3rdparty/resonance-audio/platforms/common/utils.h
new file mode 100644
index 000000000..5273f437f
--- /dev/null
+++ b/src/3rdparty/resonance-audio/platforms/common/utils.h
@@ -0,0 +1,55 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_PLATFORM_COMMON_UTILS_H_
+#define RESONANCE_AUDIO_PLATFORM_COMMON_UTILS_H_
+
+#include "Eigen/Dense"
+
+namespace vraudio {
+
+// Flips the z-axis of a transformation matrix, which effectively allows
+// switching between the left-handed and right-handed coordinate systems.
+//
+// @param matrix 4x4 transformation matrix.
+// @return 4x4 transformation matrix with the z-axis flipped.
+void FlipZAxis(Eigen::Matrix4f* matrix);
+
+// Returns the position vector of a transformation matrix.
+//
+// @param matrix 4x4 transformation matrix.
+// @return 3D position vector.
+Eigen::Vector3f GetPosition(const Eigen::Matrix4f& matrix);
+
+// Returns the rotation quaternion of a transformation matrix.
+//
+// @param matrix 4x4 transformation matrix.
+// @return Quaternion representation of the rotation.
+Eigen::Quaternionf GetQuaternion(const Eigen::Matrix4f& matrix);
+
+// Returns a transformation matrix from position, forward & up vectors.
+//
+// @param position Position vector.
+// @param forward Forward direction vector describing rotation.
+// @param up Up direction vector describing rotation.
+// @return 4x4 transformation matrix.
+Eigen::Matrix4f GetTransformMatrix(const Eigen::Vector3f& position,
+ const Eigen::Vector3f& forward,
+ const Eigen::Vector3f& up);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_PLATFORM_COMMON_UTILS_H_
diff --git a/src/3rdparty/resonance-audio/platforms/common/utils_test.cc b/src/3rdparty/resonance-audio/platforms/common/utils_test.cc
new file mode 100644
index 000000000..40992027e
--- /dev/null
+++ b/src/3rdparty/resonance-audio/platforms/common/utils_test.cc
@@ -0,0 +1,107 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "platforms/common/utils.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+// Tests that flipping the z-axis of an arbitrary transformation matrix using
+// the |FlipZAxis| method gives the expected transformation matrix.
+TEST(UtilsTest, FlipZAxisTest) {
+ // Test input values for an arbitrary 4x4 matrix.
+ const float kInput[] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f,
+ 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f,
+ 13.0f, 14.0f, 15.0f, 16.0f};
+ // Z-axis flip matrix is the TRS matrix of Scale(1, 1, -1).
+ const auto kFlipZMatrix((Eigen::Matrix4f() << 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f)
+ .finished());
+
+ // Compute the expected transformation matrix for given |kInput|.
+ const auto expected = kFlipZMatrix * Eigen::Matrix4f(kInput) * kFlipZMatrix;
+ // Compute the flipped transformation matrix of given |kInput|.
+ Eigen::Matrix4f output(kInput);
+ FlipZAxis(&output);
+
+ // Verify that the output matrix has expected number of rows & columns.
+ EXPECT_EQ(expected.rows(), output.rows());
+ EXPECT_EQ(expected.cols(), output.cols());
+ // Compare equality for all the elements in the resulting matrices.
+ const size_t num_cols = output.cols();
+ const size_t num_rows = output.rows();
+ for (size_t col = 0; col < num_cols; ++col) {
+ for (size_t row = 0; row < num_rows; ++row) {
+ EXPECT_EQ(expected(row, col), output(row, col));
+ }
+ }
+}
+
+// Tests that the |GetTransformMatrix| method gives the expected transformation
+// matrix, in which the properties remain unchanged when returned via the
+// |GetPosition| and |GetQuaternion| methods.
+TEST(UtilsTest, GetTransformMatrixAndBackTest) {
+ // Arbitrary position.
+ const Eigen::Vector3f kInputPosition(1.0f, 5.0f, -10.0f);
+ // Arbitrary directions calculated from the orientation of euler angles
+ // (45, 90, -90) in degrees.
+ const Eigen::Vector3f kInputForward(kInverseSqrtTwo, -kInverseSqrtTwo, 0.0f);
+ const Eigen::Vector3f kInputUp(0.0f, 0.0f, -1.0f);
+ // Expected values for the transformation matrix from the given
+ // |kInputPosition|, |kInputForward| & |kInputUp| vectors.
+ const float kExpectedTransform[] = {
+ -kInverseSqrtTwo, -kInverseSqrtTwo, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f,
+ kInverseSqrtTwo, -kInverseSqrtTwo, 0.0f, 0.0f, 1.0f, 5.0f, -10.0f, 1.0f};
+
+ // Calculate the transform matrix.
+ const auto output_transform =
+ GetTransformMatrix(kInputPosition, kInputForward, kInputUp);
+
+ // Test if the resulting transformation matrix has the expected values.
+ const size_t num_cols = output_transform.cols();
+ const size_t num_rows = output_transform.rows();
+ const Eigen::Matrix4f expected_transform(kExpectedTransform);
+ for (size_t col = 0; col < num_cols; ++col) {
+ for (size_t row = 0; row < num_rows; ++row) {
+ EXPECT_EQ(expected_transform(row, col), output_transform(row, col));
+ }
+ }
+
+ // Verify that the position vector remains unchanged.
+ const auto output_position = GetPosition(output_transform);
+ EXPECT_TRUE(kInputPosition.isApprox(output_position, kEpsilonFloat));
+
+ // Verify that the forward & up direction vectors remain unchanged.
+ const Eigen::Vector3f kForward(0.0f, 0.0f, 1.0f);
+ const Eigen::Vector3f kUp(0.0f, 1.0f, 0.0f);
+ const auto output_quaternion = GetQuaternion(output_transform);
+
+ const auto output_forward = (output_quaternion * kForward).normalized();
+ EXPECT_TRUE(kInputForward.isApprox(output_forward, kEpsilonFloat));
+
+ const auto output_up = (output_quaternion * kUp).normalized();
+ EXPECT_TRUE(kInputUp.isApprox(output_up, kEpsilonFloat));
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/qt_attribution.json b/src/3rdparty/resonance-audio/qt_attribution.json
new file mode 100644
index 000000000..6f56457cb
--- /dev/null
+++ b/src/3rdparty/resonance-audio/qt_attribution.json
@@ -0,0 +1,18 @@
+{
+ "Id": "resonance-audio",
+ "Name": "Resonance Audio",
+ "QDocModule": "qtspatialaudio",
+ "Description": "3D audio support.",
+ "QtUsage": "Used to implement spatial audio.",
+ "SecurityCritical": true,
+
+ "Homepage": "https://resonance-audio.github.io/resonance-audio/",
+ "Version": "e225aedb5ec7",
+ "DownloadLocation":
+ "https://github.com/resonance-audio/resonance-audio/tree/e225aedb5ec76ca6a0fe7079c0b84dbcbb490553",
+
+ "CopyrightFile": "COPYRIGHTS",
+ "License": "Apache License 2.0",
+ "LicenseId": "Apache-2.0",
+ "LicenseFile": "LICENSE"
+}
diff --git a/src/3rdparty/resonance-audio/resonance_audio/CMakeLists.txt b/src/3rdparty/resonance-audio/resonance_audio/CMakeLists.txt
new file mode 100644
index 000000000..b52e62526
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/CMakeLists.txt
@@ -0,0 +1,335 @@
+# Copyright 2018 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS-IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Build PFFFT.
+set(PFFFT_DIR "${PROJECT_SOURCE_DIR}/third_party/pffft/" CACHE PATH "Path to pffft library")
+set(PFFFT_INCLUDE_DIR ${PFFFT_DIR})
+set(PFFFT_SOURCE
+ ${PFFFT_DIR}/fftpack.c
+ ${PFFFT_DIR}/fftpack.h
+ ${PFFFT_DIR}/pffft.c
+ ${PFFFT_DIR}/pffft.h
+ )
+add_library(PffftObj OBJECT ${PFFFT_SOURCE})
+target_include_directories(PffftObj PRIVATE ${PFFFT_INCLUDE_DIR}/)
+
+if (UNIX)
+ set_target_properties(PffftObj PROPERTIES COMPILE_FLAGS -fPIC)
+endif (UNIX)
+
+# Build SADIE HRTF database.
+set(SADIE_HRTFS_DIR "${PROJECT_SOURCE_DIR}/third_party/SADIE_hrtf_database/generated/" CACHE PATH "Path to SADIE_hrtf_database library")
+set(SADIE_HRTFS_INCLUDE_DIR ${SADIE_HRTFS_DIR})
+set(SADIE_HRTFS_SOURCE
+ ${SADIE_HRTFS_DIR}/hrtf_assets.cc
+ ${SADIE_HRTFS_DIR}/hrtf_assets.h
+ )
+add_library(SadieHrtfsObj OBJECT ${SADIE_HRTFS_SOURCE})
+
+# Build Resonance Audio.
+set(RA_SOURCES
+ ${PROJECT_SOURCE_DIR}/platforms/common/utils.cc
+ ${PROJECT_SOURCE_DIR}/platforms/common/utils.h
+ ${RA_SOURCE_DIR}/ambisonics/ambisonic_binaural_decoder.h
+ ${RA_SOURCE_DIR}/ambisonics/ambisonic_binaural_decoder.cc
+ ${RA_SOURCE_DIR}/ambisonics/ambisonic_codec.h
+ ${RA_SOURCE_DIR}/ambisonics/ambisonic_codec_impl.h
+ ${RA_SOURCE_DIR}/ambisonics/ambisonic_lookup_table.cc
+ ${RA_SOURCE_DIR}/ambisonics/ambisonic_lookup_table.h
+ ${RA_SOURCE_DIR}/ambisonics/ambisonic_spread_coefficients.h
+ ${RA_SOURCE_DIR}/ambisonics/associated_legendre_polynomials_generator.cc
+ ${RA_SOURCE_DIR}/ambisonics/associated_legendre_polynomials_generator.h
+ ${RA_SOURCE_DIR}/ambisonics/foa_rotator.cc
+ ${RA_SOURCE_DIR}/ambisonics/foa_rotator.h
+ ${RA_SOURCE_DIR}/ambisonics/hoa_rotator.cc
+ ${RA_SOURCE_DIR}/ambisonics/hoa_rotator.h
+ ${RA_SOURCE_DIR}/ambisonics/stereo_from_soundfield_converter.cc
+ ${RA_SOURCE_DIR}/ambisonics/stereo_from_soundfield_converter.h
+ ${RA_SOURCE_DIR}/ambisonics/utils.h
+ ${RA_SOURCE_DIR}/api/binaural_surround_renderer.cc
+ ${RA_SOURCE_DIR}/api/binaural_surround_renderer.h
+ ${RA_SOURCE_DIR}/api/resonance_audio_api.cc
+ ${RA_SOURCE_DIR}/api/resonance_audio_api.h
+ ${RA_SOURCE_DIR}/base/aligned_allocator.h
+ ${RA_SOURCE_DIR}/base/audio_buffer.cc
+ ${RA_SOURCE_DIR}/base/audio_buffer.h
+ ${RA_SOURCE_DIR}/base/channel_view.cc
+ ${RA_SOURCE_DIR}/base/channel_view.h
+ ${RA_SOURCE_DIR}/base/constants_and_types.h
+ ${RA_SOURCE_DIR}/base/integral_types.h
+ ${RA_SOURCE_DIR}/base/logging.h
+ ${RA_SOURCE_DIR}/base/misc_math.cc
+ ${RA_SOURCE_DIR}/base/misc_math.h
+ ${RA_SOURCE_DIR}/base/object_transform.h
+ ${RA_SOURCE_DIR}/base/simd_macros.h
+ ${RA_SOURCE_DIR}/base/simd_utils.cc
+ ${RA_SOURCE_DIR}/base/simd_utils.h
+ ${RA_SOURCE_DIR}/base/source_parameters.h
+ ${RA_SOURCE_DIR}/base/spherical_angle.cc
+ ${RA_SOURCE_DIR}/base/spherical_angle.h
+ ${RA_SOURCE_DIR}/base/unique_ptr_wrapper.h
+ ${RA_SOURCE_DIR}/config/global_config.h
+ ${RA_SOURCE_DIR}/config/source_config.cc
+ ${RA_SOURCE_DIR}/config/source_config.h
+ ${RA_SOURCE_DIR}/dsp/biquad_filter.cc
+ ${RA_SOURCE_DIR}/dsp/biquad_filter.h
+ ${RA_SOURCE_DIR}/dsp/channel_converter.cc
+ ${RA_SOURCE_DIR}/dsp/channel_converter.h
+ ${RA_SOURCE_DIR}/dsp/circular_buffer.cc
+ ${RA_SOURCE_DIR}/dsp/circular_buffer.h
+ ${RA_SOURCE_DIR}/dsp/delay_filter.cc
+ ${RA_SOURCE_DIR}/dsp/delay_filter.h
+ ${RA_SOURCE_DIR}/dsp/distance_attenuation.cc
+ ${RA_SOURCE_DIR}/dsp/distance_attenuation.h
+ ${RA_SOURCE_DIR}/dsp/fft_manager.cc
+ ${RA_SOURCE_DIR}/dsp/fft_manager.h
+ ${RA_SOURCE_DIR}/dsp/filter_coefficient_generators.cc
+ ${RA_SOURCE_DIR}/dsp/filter_coefficient_generators.h
+ ${RA_SOURCE_DIR}/dsp/fir_filter.cc
+ ${RA_SOURCE_DIR}/dsp/fir_filter.h
+ ${RA_SOURCE_DIR}/dsp/gain.cc
+ ${RA_SOURCE_DIR}/dsp/gain.h
+ ${RA_SOURCE_DIR}/dsp/gain_mixer.cc
+ ${RA_SOURCE_DIR}/dsp/gain_mixer.h
+ ${RA_SOURCE_DIR}/dsp/gain_processor.cc
+ ${RA_SOURCE_DIR}/dsp/gain_processor.h
+ ${RA_SOURCE_DIR}/dsp/mixer.cc
+ ${RA_SOURCE_DIR}/dsp/mixer.h
+ ${RA_SOURCE_DIR}/dsp/mono_pole_filter.cc
+ ${RA_SOURCE_DIR}/dsp/mono_pole_filter.h
+ ${RA_SOURCE_DIR}/dsp/multi_channel_iir.cc
+ ${RA_SOURCE_DIR}/dsp/multi_channel_iir.h
+ ${RA_SOURCE_DIR}/dsp/near_field_processor.cc
+ ${RA_SOURCE_DIR}/dsp/near_field_processor.h
+ ${RA_SOURCE_DIR}/dsp/occlusion_calculator.cc
+ ${RA_SOURCE_DIR}/dsp/occlusion_calculator.h
+ ${RA_SOURCE_DIR}/dsp/partitioned_fft_filter.cc
+ ${RA_SOURCE_DIR}/dsp/partitioned_fft_filter.h
+ ${RA_SOURCE_DIR}/dsp/reflection.h
+ ${RA_SOURCE_DIR}/dsp/reflections_processor.cc
+ ${RA_SOURCE_DIR}/dsp/reflections_processor.h
+ ${RA_SOURCE_DIR}/dsp/resampler.cc
+ ${RA_SOURCE_DIR}/dsp/resampler.h
+ ${RA_SOURCE_DIR}/dsp/reverb_onset_compensator.cc
+ ${RA_SOURCE_DIR}/dsp/reverb_onset_compensator.h
+ ${RA_SOURCE_DIR}/dsp/reverb_onset_update_processor.cc
+ ${RA_SOURCE_DIR}/dsp/reverb_onset_update_processor.h
+ ${RA_SOURCE_DIR}/dsp/sh_hrir_creator.cc
+ ${RA_SOURCE_DIR}/dsp/sh_hrir_creator.h
+ ${RA_SOURCE_DIR}/dsp/shoe_box_room.cc
+ ${RA_SOURCE_DIR}/dsp/shoe_box_room.h
+ ${RA_SOURCE_DIR}/dsp/spectral_reverb.cc
+ ${RA_SOURCE_DIR}/dsp/spectral_reverb.h
+ ${RA_SOURCE_DIR}/dsp/spectral_reverb_constants_and_tables.h
+ ${RA_SOURCE_DIR}/dsp/stereo_panner.cc
+ ${RA_SOURCE_DIR}/dsp/stereo_panner.h
+ ${RA_SOURCE_DIR}/dsp/utils.cc
+ ${RA_SOURCE_DIR}/dsp/utils.h
+ ${RA_SOURCE_DIR}/graph/ambisonic_binaural_decoder_node.cc
+ ${RA_SOURCE_DIR}/graph/ambisonic_binaural_decoder_node.h
+ ${RA_SOURCE_DIR}/graph/ambisonic_mixing_encoder_node.cc
+ ${RA_SOURCE_DIR}/graph/ambisonic_mixing_encoder_node.h
+ ${RA_SOURCE_DIR}/graph/binaural_surround_renderer_impl.cc
+ ${RA_SOURCE_DIR}/graph/binaural_surround_renderer_impl.h
+ ${RA_SOURCE_DIR}/graph/buffered_source_node.cc
+ ${RA_SOURCE_DIR}/graph/buffered_source_node.h
+ ${RA_SOURCE_DIR}/graph/foa_rotator_node.cc
+ ${RA_SOURCE_DIR}/graph/foa_rotator_node.h
+ ${RA_SOURCE_DIR}/graph/gain_mixer_node.cc
+ ${RA_SOURCE_DIR}/graph/gain_mixer_node.h
+ ${RA_SOURCE_DIR}/graph/gain_node.cc
+ ${RA_SOURCE_DIR}/graph/gain_node.h
+ ${RA_SOURCE_DIR}/graph/graph_manager.cc
+ ${RA_SOURCE_DIR}/graph/graph_manager.h
+ ${RA_SOURCE_DIR}/graph/graph_manager_config.h
+ ${RA_SOURCE_DIR}/graph/hoa_rotator_node.cc
+ ${RA_SOURCE_DIR}/graph/hoa_rotator_node.h
+ ${RA_SOURCE_DIR}/graph/mixer_node.cc
+ ${RA_SOURCE_DIR}/graph/mixer_node.h
+ ${RA_SOURCE_DIR}/graph/mono_from_soundfield_node.cc
+ ${RA_SOURCE_DIR}/graph/mono_from_soundfield_node.h
+ ${RA_SOURCE_DIR}/graph/near_field_effect_node.cc
+ ${RA_SOURCE_DIR}/graph/near_field_effect_node.h
+ ${RA_SOURCE_DIR}/graph/occlusion_node.cc
+ ${RA_SOURCE_DIR}/graph/occlusion_node.h
+ ${RA_SOURCE_DIR}/graph/reflections_node.cc
+ ${RA_SOURCE_DIR}/graph/reflections_node.h
+ ${RA_SOURCE_DIR}/graph/resonance_audio_api_impl.cc
+ ${RA_SOURCE_DIR}/graph/resonance_audio_api_impl.h
+ ${RA_SOURCE_DIR}/graph/reverb_node.cc
+ ${RA_SOURCE_DIR}/graph/reverb_node.h
+ ${RA_SOURCE_DIR}/graph/source_graph_config.h
+ ${RA_SOURCE_DIR}/graph/source_parameters_manager.cc
+ ${RA_SOURCE_DIR}/graph/source_parameters_manager.h
+ ${RA_SOURCE_DIR}/graph/stereo_mixing_panner_node.cc
+ ${RA_SOURCE_DIR}/graph/stereo_mixing_panner_node.h
+ ${RA_SOURCE_DIR}/graph/system_settings.h
+ ${RA_SOURCE_DIR}/node/node.h
+ ${RA_SOURCE_DIR}/node/processing_node.cc
+ ${RA_SOURCE_DIR}/node/processing_node.h
+ ${RA_SOURCE_DIR}/node/publisher_node.h
+ ${RA_SOURCE_DIR}/node/sink_node.cc
+ ${RA_SOURCE_DIR}/node/sink_node.h
+ ${RA_SOURCE_DIR}/node/source_node.cc
+ ${RA_SOURCE_DIR}/node/source_node.h
+ ${RA_SOURCE_DIR}/node/subscriber_node.h
+ ${PROJECT_SOURCE_DIR}/platforms/common/room_effects_utils.cc
+ ${PROJECT_SOURCE_DIR}/platforms/common/room_effects_utils.h
+ ${RA_SOURCE_DIR}/utils/buffer_crossfader.cc
+ ${RA_SOURCE_DIR}/utils/buffer_crossfader.h
+ ${RA_SOURCE_DIR}/utils/buffer_partitioner.cc
+ ${RA_SOURCE_DIR}/utils/buffer_partitioner.h
+ ${RA_SOURCE_DIR}/utils/buffer_unpartitioner.cc
+ ${RA_SOURCE_DIR}/utils/buffer_unpartitioner.h
+ ${RA_SOURCE_DIR}/utils/lockless_task_queue.cc
+ ${RA_SOURCE_DIR}/utils/lockless_task_queue.h
+ ${RA_SOURCE_DIR}/utils/planar_interleaved_conversion.cc
+ ${RA_SOURCE_DIR}/utils/planar_interleaved_conversion.h
+ ${RA_SOURCE_DIR}/utils/pseudoinverse.h
+ ${RA_SOURCE_DIR}/utils/sample_type_conversion.cc
+ ${RA_SOURCE_DIR}/utils/sample_type_conversion.h
+ ${RA_SOURCE_DIR}/utils/semi_lockless_fifo.h
+ ${RA_SOURCE_DIR}/utils/sum_and_difference_processor.cc
+ ${RA_SOURCE_DIR}/utils/sum_and_difference_processor.h
+ ${RA_SOURCE_DIR}/utils/threadsafe_fifo.h
+ ${RA_SOURCE_DIR}/utils/wav.cc
+ ${RA_SOURCE_DIR}/utils/wav.h
+ ${RA_SOURCE_DIR}/utils/wav_reader.cc
+ ${RA_SOURCE_DIR}/utils/wav_reader.h
+ )
+
+add_library(ResonanceAudioObj OBJECT ${RA_SOURCES})
+target_include_directories(ResonanceAudioObj PRIVATE ${PROJECT_SOURCE_DIR}/resonance_audio/)
+target_include_directories(ResonanceAudioObj PRIVATE ${EIGEN3_INCLUDE_DIR}/)
+target_include_directories(ResonanceAudioObj PRIVATE ${PFFFT_INCLUDE_DIR}/)
+
+
+if (BUILD_RESONANCE_AUDIO_API)
+ # Build shared library
+ add_library(ResonanceAudioShared SHARED $<TARGET_OBJECTS:ResonanceAudioObj>
+ $<TARGET_OBJECTS:SadieHrtfsObj>
+ $<TARGET_OBJECTS:PffftObj>)
+
+ # Build static library
+ add_library(ResonanceAudioStatic STATIC $<TARGET_OBJECTS:ResonanceAudioObj>
+ $<TARGET_OBJECTS:SadieHrtfsObj>
+ $<TARGET_OBJECTS:PffftObj>)
+
+ set(RA_INSTALL_DIR "${INSTALL_DIR}/resonance_audio/")
+ install(TARGETS ResonanceAudioStatic ResonanceAudioShared
+ LIBRARY DESTINATION ${RA_INSTALL_DIR}/lib
+ ARCHIVE DESTINATION ${RA_INSTALL_DIR}/lib
+ RUNTIME DESTINATION ${RA_INSTALL_DIR}/lib
+ PUBLIC_HEADER DESTINATION ${RA_INSTALL_DIR}/include)
+
+ set(RA_PUBLIC_HEADERS
+ ${RA_SOURCE_DIR}/api/binaural_surround_renderer.h
+ ${RA_SOURCE_DIR}/api/resonance_audio_api.h
+ )
+ install(FILES ${RA_PUBLIC_HEADERS}
+ DESTINATION ${RA_INSTALL_DIR}/include)
+endif (BUILD_RESONANCE_AUDIO_API)
+
+
+if (BUILD_RESONANCE_AUDIO_TESTS)
+ set(RA_TESTS
+ ${RA_SOURCE_DIR}/ambisonics/ambisonic_binaural_decoder_test.cc
+ ${RA_SOURCE_DIR}/ambisonics/ambisonic_codec_impl_test.cc
+ ${RA_SOURCE_DIR}/ambisonics/ambisonic_lookup_table_test.cc
+ ${RA_SOURCE_DIR}/ambisonics/associated_legendre_polynomials_generator_test.cc
+ ${RA_SOURCE_DIR}/ambisonics/foa_rotator_test.cc
+ ${RA_SOURCE_DIR}/ambisonics/hoa_rotator_test.cc
+ ${RA_SOURCE_DIR}/ambisonics/stereo_from_soundfield_converter_test.cc
+ ${RA_SOURCE_DIR}/ambisonics/utils_test.cc
+ ${RA_SOURCE_DIR}/base/aligned_allocator_test.cc
+ ${RA_SOURCE_DIR}/base/audio_buffer_test.cc
+ ${RA_SOURCE_DIR}/base/channel_view_test.cc
+ ${RA_SOURCE_DIR}/base/misc_math_test.cc
+ ${RA_SOURCE_DIR}/base/simd_utils_test.cc
+ ${RA_SOURCE_DIR}/base/spherical_angle_test.cc
+ ${RA_SOURCE_DIR}/dsp/biquad_filter_test.cc
+ ${RA_SOURCE_DIR}/dsp/channel_converter_test.cc
+ ${RA_SOURCE_DIR}/dsp/circular_buffer_test.cc
+ ${RA_SOURCE_DIR}/dsp/delay_filter_test.cc
+ ${RA_SOURCE_DIR}/dsp/distance_attenuation_test.cc
+ ${RA_SOURCE_DIR}/dsp/fft_manager_test.cc
+ ${RA_SOURCE_DIR}/dsp/filter_coefficient_generators_test.cc
+ ${RA_SOURCE_DIR}/dsp/fir_filter_test.cc
+ ${RA_SOURCE_DIR}/dsp/gain_mixer_test.cc
+ ${RA_SOURCE_DIR}/dsp/gain_processor_test.cc
+ ${RA_SOURCE_DIR}/dsp/gain_test.cc
+ ${RA_SOURCE_DIR}/dsp/mixer_test.cc
+ ${RA_SOURCE_DIR}/dsp/mono_pole_filter_test.cc
+ ${RA_SOURCE_DIR}/dsp/multi_channel_iir_test.cc
+ ${RA_SOURCE_DIR}/dsp/occlusion_calculator_test.cc
+ ${RA_SOURCE_DIR}/dsp/partitioned_fft_filter_test.cc
+ ${RA_SOURCE_DIR}/dsp/reflections_processor_test.cc
+ ${RA_SOURCE_DIR}/dsp/resampler_test.cc
+ ${RA_SOURCE_DIR}/dsp/shoe_box_room_test.cc
+ ${RA_SOURCE_DIR}/dsp/spectral_reverb_test.cc
+ ${RA_SOURCE_DIR}/dsp/stereo_panner_test.cc
+ ${RA_SOURCE_DIR}/dsp/utils_test.cc
+ ${RA_SOURCE_DIR}/graph/ambisonic_mixing_encoder_node_test.cc
+ ${RA_SOURCE_DIR}/graph/binaural_surround_renderer_impl_test.cc
+ ${RA_SOURCE_DIR}/graph/occlusion_node_test.cc
+ ${RA_SOURCE_DIR}/graph/gain_mixer_node_test.cc
+ ${RA_SOURCE_DIR}/graph/gain_node_test.cc
+ ${RA_SOURCE_DIR}/graph/mixer_node_test.cc
+ ${RA_SOURCE_DIR}/graph/source_parameters_manager_test.cc
+ ${RA_SOURCE_DIR}/node/audio_nodes_test.cc
+ ${RA_SOURCE_DIR}/node/node_test.cc
+ ${PROJECT_SOURCE_DIR}/platforms/common/room_effects_utils_test.cc
+ ${RA_SOURCE_DIR}/utils/buffer_crossfader_test.cc
+ ${RA_SOURCE_DIR}/utils/buffer_partitioner_test.cc
+ ${RA_SOURCE_DIR}/utils/buffer_unpartitioner_test.cc
+ ${RA_SOURCE_DIR}/utils/lockless_task_queue_test.cc
+ ${RA_SOURCE_DIR}/utils/planar_interleaved_conversion_test.cc
+ ${RA_SOURCE_DIR}/utils/pseudoinverse_test.cc
+ ${RA_SOURCE_DIR}/utils/sample_type_conversion_test.cc
+ ${RA_SOURCE_DIR}/utils/sum_and_difference_processor_test.cc
+ ${RA_SOURCE_DIR}/utils/test_util.cc
+ ${RA_SOURCE_DIR}/utils/test_util.h
+ ${RA_SOURCE_DIR}/utils/test_util_test.cc
+ )
+
+ # Unit Tests target.
+ add_executable(ResonanceAudioUnitTests ${RA_TESTS}
+ $<TARGET_OBJECTS:gtest>
+ $<TARGET_OBJECTS:ResonanceAudioObj>
+ $<TARGET_OBJECTS:SadieHrtfsObj>
+ $<TARGET_OBJECTS:PffftObj>)
+
+ target_include_directories(ResonanceAudioUnitTests PRIVATE "${EIGEN3_INCLUDE_DIR}/")
+ target_include_directories(ResonanceAudioUnitTests PRIVATE "${PFFFT_INCLUDE_DIR}/")
+ target_include_directories(ResonanceAudioUnitTests PRIVATE "${PROJECT_SOURCE_DIR}/resonance_audio/")
+ target_include_directories(ResonanceAudioUnitTests PRIVATE "${GTEST_DIR}/googlemock/")
+ target_include_directories(ResonanceAudioUnitTests PRIVATE "${GTEST_DIR}/googlemock/include")
+ target_include_directories(ResonanceAudioUnitTests PRIVATE "${GTEST_DIR}/googletest/")
+ target_include_directories(ResonanceAudioUnitTests PRIVATE "${GTEST_DIR}/googletest/include")
+
+ if (NOT WIN32)
+ find_package(Threads REQUIRED)
+ target_link_libraries(ResonanceAudioUnitTests pthread)
+ endif (NOT WIN32)
+
+ add_test(NAME runResonanceAudioUnitTest COMMAND $<TARGET_FILE:ResonanceAudioUnitTests>)
+
+ add_custom_command(
+ TARGET ResonanceAudioUnitTests
+ COMMENT "Run resonance audio tests"
+ POST_BUILD
+ COMMAND ResonanceAudioUnitTests
+ )
+endif (BUILD_RESONANCE_AUDIO_TESTS)
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_binaural_decoder.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_binaural_decoder.cc
new file mode 100644
index 000000000..2a278389b
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_binaural_decoder.cc
@@ -0,0 +1,80 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/ambisonic_binaural_decoder.h"
+
+#include "ambisonics/utils.h"
+#include "base/constants_and_types.h"
+
+
+namespace vraudio {
+
+AmbisonicBinauralDecoder::AmbisonicBinauralDecoder(const AudioBuffer& sh_hrirs,
+ size_t frames_per_buffer,
+ FftManager* fft_manager)
+ : fft_manager_(fft_manager),
+ freq_input_(kNumMonoChannels, NextPowTwo(frames_per_buffer) * 2),
+ filtered_input_(kNumMonoChannels, frames_per_buffer) {
+ CHECK(fft_manager_);
+ CHECK_NE(frames_per_buffer, 0U);
+ const size_t num_channels = sh_hrirs.num_channels();
+ const size_t filter_size = sh_hrirs.num_frames();
+ CHECK_NE(num_channels, 0U);
+ CHECK_NE(filter_size, 0U);
+ sh_hrir_filters_.reserve(num_channels);
+ for (size_t i = 0; i < num_channels; ++i) {
+ sh_hrir_filters_.emplace_back(
+ new PartitionedFftFilter(filter_size, frames_per_buffer, fft_manager_));
+ sh_hrir_filters_[i]->SetTimeDomainKernel(sh_hrirs[i]);
+ }
+}
+
+void AmbisonicBinauralDecoder::Process(const AudioBuffer& input,
+ AudioBuffer* output) {
+
+ DCHECK(output);
+ DCHECK_EQ(kNumStereoChannels, output->num_channels());
+ DCHECK_EQ(input.num_frames(), output->num_frames());
+ DCHECK_EQ(input.num_channels(), sh_hrir_filters_.size());
+
+ output->Clear();
+
+ AudioBuffer::Channel* freq_input_channel = &freq_input_[0];
+ AudioBuffer::Channel* filtered_input_channel = &filtered_input_[0];
+ AudioBuffer::Channel* output_channel_0 = &(*output)[0];
+ AudioBuffer::Channel* output_channel_1 = &(*output)[1];
+ for (size_t channel = 0; channel < input.num_channels(); ++channel) {
+ const int degree = GetPeriphonicAmbisonicDegreeForChannel(channel);
+ fft_manager_->FreqFromTimeDomain(input[channel], freq_input_channel);
+ sh_hrir_filters_[channel]->Filter(*freq_input_channel);
+ sh_hrir_filters_[channel]->GetFilteredSignal(filtered_input_channel);
+ if (degree < 0) {
+ // Degree is negative: spherical harmonic is asymetric.
+ // So add contributions to the left channel and subtract from the right
+ // channel.
+ *output_channel_0 += *filtered_input_channel;
+ *output_channel_1 -= *filtered_input_channel;
+
+ } else {
+ // Degree is zero or positive: spherical harmonic is symetric.
+ // So add contributions to both left and right channels.
+ *output_channel_0 += *filtered_input_channel;
+ *output_channel_1 += *filtered_input_channel;
+ }
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_binaural_decoder.h b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_binaural_decoder.h
new file mode 100644
index 000000000..98a861234
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_binaural_decoder.h
@@ -0,0 +1,71 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_AMBISONICS_AMBISONIC_BINAURAL_DECODER_H_
+#define RESONANCE_AUDIO_AMBISONICS_AMBISONIC_BINAURAL_DECODER_H_
+
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "dsp/fft_manager.h"
+#include "dsp/partitioned_fft_filter.h"
+
+namespace vraudio {
+
+// Decodes an Ambisonic sound field, of an arbitrary order, to binaural stereo,
+// by performing convolution in the spherical harmonics domain. The order (hence
+// the number of channels) of the input must match the order (hence the number
+// of channels) of the spherical harmonic-encoded Head Related Impulse Responses
+// (HRIRs). Assumes that HRIRs are symmetric with respect to the sagittal plane.
+class AmbisonicBinauralDecoder {
+ public:
+ // Constructs an |AmbisonicBinauralDecoder| from an |AudioBuffer| containing
+ // spherical harmonic representation of HRIRs. The order of spherical
+ // harmonic-encoded HRIRs (hence the number of channels) must match the order
+ // of the Ambisonic sound field input.
+ //
+ // @param sh_hrirs |AudioBuffer| containing time-domain spherical harmonic
+ // encoded symmetric HRIRs.
+ // @param frames_per_buffer Number of frames in each input/output buffer.
+ // @param fft_manager Pointer to a manager to perform FFT transformations.
+ AmbisonicBinauralDecoder(const AudioBuffer& sh_hrirs,
+ size_t frames_per_buffer, FftManager* fft_manager);
+
+ // Processes an Ambisonic sound field input and outputs a binaurally decoded
+ // stereo buffer.
+ //
+ // @param input Input buffer to be processed.
+ // @param output Pointer to a stereo output buffer.
+ void Process(const AudioBuffer& input, AudioBuffer* output);
+
+ private:
+ // Manager for all FFT related functionality (not owned).
+ FftManager* const fft_manager_;
+
+ // Spherical Harmonic HRIR filter kernels.
+ std::vector<std::unique_ptr<PartitionedFftFilter>> sh_hrir_filters_;
+
+ // Frequency domain representation of the input signal.
+ PartitionedFftFilter::FreqDomainBuffer freq_input_;
+
+ // Temporary audio buffer to store the convolution output for asymmetric or
+ // symmetric spherical harmonic HRIR.
+ AudioBuffer filtered_input_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_AMBISONICS_AMBISONIC_BINAURAL_DECODER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_binaural_decoder_test.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_binaural_decoder_test.cc
new file mode 100644
index 000000000..ca2b0dfca
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_binaural_decoder_test.cc
@@ -0,0 +1,162 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/ambisonic_binaural_decoder.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "dsp/fft_manager.h"
+
+namespace vraudio {
+
+namespace {
+
+// Generates sample audio data where the first sample is 0 and each consecutive
+// sample is incremented by 0.001. Then it moves to the next channel and
+// continues to increment the samples.
+std::vector<std::vector<float>> GenerateAudioData(size_t num_channels,
+ size_t num_samples) {
+ std::vector<std::vector<float>> input_data(num_channels,
+ std::vector<float>(num_samples));
+ float sample_value = 0.0f;
+ const float increment = 0.001f;
+ for (size_t channel = 0; channel < num_channels; ++channel) {
+ for (size_t sample = 0; sample < num_samples; ++sample) {
+ input_data[channel][sample] = sample_value;
+ sample_value += increment;
+ }
+ }
+ return input_data;
+}
+
+// Number of frames in each audio input/output buffer.
+const size_t kFramesPerBuffer = 18;
+
+} // namespace
+
+// Tests whether binaural docoding of Ambisonic input using short HRIR filters
+// (shorter than the number of frames per buffer) gives correct results.
+TEST(AmbisonicBinauralDecoderTest, ShortFilterTest) {
+ const std::vector<std::vector<float>> kInputData =
+ GenerateAudioData(kNumFirstOrderAmbisonicChannels, kFramesPerBuffer);
+ const std::vector<float> kExpectedOutputLeft = {
+ 0.0042840000f, 0.0087780003f, 0.013486000f, 0.018412000f, 0.023560001f,
+ 0.028934000f, 0.034538001f, 0.040376000f, 0.046452001f, 0.052770000f,
+ 0.059333999f, 0.066147998f, 0.073215999f, 0.080541998f, 0.088129997f,
+ 0.095983997f, 0.10410800f, 0.10638600f};
+ const std::vector<float> kExpectedOutputRight = {
+ 0.0036720000f, 0.0074840002f, 0.011438000f, 0.015536000f, 0.019780001f,
+ 0.024172001f, 0.028713999f, 0.033408001f, 0.038256001f, 0.043260001f,
+ 0.048422001f, 0.053743999f, 0.059227999f, 0.064875998f, 0.070689999f,
+ 0.076672003f, 0.082823999f, 0.084252000f};
+ const std::vector<std::vector<float>> kHrirDataShort =
+ GenerateAudioData(kNumFirstOrderAmbisonicChannels, kFramesPerBuffer - 1);
+
+ AudioBuffer sh_hrirs(kHrirDataShort.size(), kHrirDataShort[0].size());
+ sh_hrirs = kHrirDataShort;
+
+ AudioBuffer input(kInputData.size(), kInputData[0].size());
+ input = kInputData;
+
+ AudioBuffer output(kNumStereoChannels, kInputData[0].size());
+ output.Clear();
+
+ FftManager fft_manager(kFramesPerBuffer);
+ AmbisonicBinauralDecoder decoder(sh_hrirs, kFramesPerBuffer, &fft_manager);
+ decoder.Process(input, &output);
+
+ for (size_t sample = 0; sample < kFramesPerBuffer; ++sample) {
+ EXPECT_NEAR(kExpectedOutputLeft[sample], output[0][sample], kEpsilonFloat);
+ EXPECT_NEAR(kExpectedOutputRight[sample], output[1][sample], kEpsilonFloat);
+ }
+}
+
+// Tests whether binaural docoding of Ambisonic input using HRIR filters
+// with the same size as frames per buffer gives correct results.
+TEST(AmbisonicBinauralDecoderTest, SameSizeFilterTest) {
+ const std::vector<std::vector<float>> kInputData =
+ GenerateAudioData(kNumFirstOrderAmbisonicChannels, kFramesPerBuffer);
+ const std::vector<float> kExpectedOutputLeft = {
+ 0.0045360001f, 0.0092879999f, 0.014260001f, 0.019455999f, 0.024879999f,
+ 0.030536000f, 0.036428001f, 0.042560000f, 0.048935998f, 0.055560000f,
+ 0.062436000f, 0.069568001f, 0.076959997f, 0.084615998f, 0.092540003f,
+ 0.10073600f, 0.10920800f, 0.11796000f};
+ const std::vector<float> kExpectedOutputRight = {
+ 0.0038880000f, 0.0079199998f, 0.012098000f, 0.016424000f, 0.020900000f,
+ 0.025528001f, 0.030309999f, 0.035248000f, 0.040344000f, 0.045600001f,
+ 0.051018000f, 0.056600001f, 0.062348001f, 0.068264000f, 0.074349999f,
+ 0.080608003f, 0.087040000f, 0.093648002f};
+ const std::vector<std::vector<float>> kHrirDataSame =
+ GenerateAudioData(kNumFirstOrderAmbisonicChannels, kFramesPerBuffer);
+
+ AudioBuffer sh_hrirs(kHrirDataSame.size(), kHrirDataSame[0].size());
+ sh_hrirs = kHrirDataSame;
+
+ AudioBuffer input(kInputData.size(), kInputData[0].size());
+ input = kInputData;
+
+ AudioBuffer output(kNumStereoChannels, kInputData[0].size());
+ output.Clear();
+
+ FftManager fft_manager(kFramesPerBuffer);
+ AmbisonicBinauralDecoder decoder(sh_hrirs, kFramesPerBuffer, &fft_manager);
+ decoder.Process(input, &output);
+
+ for (size_t sample = 0; sample < kFramesPerBuffer; ++sample) {
+ EXPECT_NEAR(kExpectedOutputLeft[sample], output[0][sample], kEpsilonFloat);
+ EXPECT_NEAR(kExpectedOutputRight[sample], output[1][sample], kEpsilonFloat);
+ }
+}
+
+// Tests whether binaural docoding of Ambisonic input using long HRIR filters
+// (longer than the number of frames per buffer) gives correct results.
+TEST(AmbisonicBinauralDecoderTest, LongSizeFilterTest) {
+ const std::vector<std::vector<float>> kInputData =
+ GenerateAudioData(kNumFirstOrderAmbisonicChannels, kFramesPerBuffer);
+ const std::vector<float> kExpectedOutputLeft = {
+ 0.0047880001f, 0.0097980006f, 0.015034000f, 0.020500001f, 0.026200000f,
+ 0.032138001f, 0.038318001f, 0.044744000f, 0.051419999f, 0.058350001f,
+ 0.065537997f, 0.072987996f, 0.080704004f, 0.088689998f, 0.096950002f,
+ 0.10548800f, 0.11430800f, 0.12341400f};
+ const std::vector<float> kExpectedOutputRight = {
+ 00.0041040001f, 0.0083560003f, 0.012758000f, 0.017312000f, 0.022020001f,
+ 0.026883999f, 0.031906001f, 0.037087999f, 0.042431999f, 0.047940001f,
+ 0.053613998f, 0.059455998f, 0.065467998f, 0.071652003f, 0.078010000f,
+ 0.084543996f, 0.091256000f, 0.098148003f};
+ const std::vector<std::vector<float>> kHrirDataLong =
+ GenerateAudioData(kNumFirstOrderAmbisonicChannels, kFramesPerBuffer + 1);
+
+ AudioBuffer sh_hrirs(kHrirDataLong.size(), kHrirDataLong[0].size());
+ sh_hrirs = kHrirDataLong;
+
+ AudioBuffer input(kInputData.size(), kInputData[0].size());
+ input = kInputData;
+
+ AudioBuffer output(kNumStereoChannels, kInputData[0].size());
+ output.Clear();
+
+ FftManager fft_manager(kFramesPerBuffer);
+ AmbisonicBinauralDecoder decoder(sh_hrirs, kFramesPerBuffer, &fft_manager);
+ decoder.Process(input, &output);
+
+ for (size_t sample = 0; sample < kFramesPerBuffer; ++sample) {
+ EXPECT_NEAR(kExpectedOutputLeft[sample], output[0][sample], kEpsilonFloat);
+ EXPECT_NEAR(kExpectedOutputRight[sample], output[1][sample], kEpsilonFloat);
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_codec.h b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_codec.h
new file mode 100644
index 000000000..546a88470
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_codec.h
@@ -0,0 +1,62 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_AMBISONICS_AMBISONIC_CODEC_H_
+#define RESONANCE_AUDIO_AMBISONICS_AMBISONIC_CODEC_H_
+
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "base/spherical_angle.h"
+
+namespace vraudio {
+// An encoder/decoder for ambisonic sound fields. It supports variable ambisonic
+// order, ACN channel sequencing and SN3D normalization.
+class AmbisonicCodec {
+ public:
+ virtual ~AmbisonicCodec() {}
+ // Encodes the given buffer of decoded vectors (unencoded data).
+ //
+ // @param input |AudioBuffer| of decoded (unencoded) data.
+ // @param output |AudioBuffer| to store the encoded data.
+ virtual void EncodeBuffer(const AudioBuffer& input, AudioBuffer* output) = 0;
+
+ // Decodes the given |AudioBuffer| of planar (non-interleaved) encoded data.
+ //
+ // @param input |AudioBuffer| of encoded data.
+ // @param output |AudioBuffer| to store the decoded data.
+ virtual void DecodeBuffer(const AudioBuffer& input, AudioBuffer* output) = 0;
+
+ // Returns the maximum periphonic ambisonic order that this codec supports.
+ virtual int ambisonic_order() const = 0;
+
+ // Returns the number of angles for this codec.
+ virtual size_t num_angles() const = 0;
+
+ // Returns the maximum number of spherical harmonics computed by this codec.
+ virtual size_t num_spherical_harmonics() const = 0;
+
+ // Returns the spherical angles currently used to define the encoder/decoder
+ // matrices.
+ virtual const std::vector<SphericalAngle>& angles() const = 0;
+
+ // Sets the spherical angles used to build the encoder/decoder matrices.
+ virtual void set_angles(const std::vector<SphericalAngle>& angles) = 0;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_AMBISONICS_AMBISONIC_CODEC_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_codec_impl.h b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_codec_impl.h
new file mode 100644
index 000000000..03a83140f
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_codec_impl.h
@@ -0,0 +1,295 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_AMBISONICS_AMBISONIC_CODEC_IMPL_H_
+#define RESONANCE_AUDIO_AMBISONICS_AMBISONIC_CODEC_IMPL_H_
+
+#include <cmath>
+#include <vector>
+
+#include "Eigen/Dense"
+#include "ambisonics/ambisonic_codec.h"
+#include "ambisonics/associated_legendre_polynomials_generator.h"
+#include "ambisonics/utils.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/spherical_angle.h"
+#include "utils/pseudoinverse.h"
+
+namespace vraudio {
+// An encoder/decoder for ambisonic sound fields. It supports variable ambisonic
+// order, ACN channel sequencing and SN3D normalization.
+//
+// @tparam NumAngles Used to fix the number of angles to be encoded/decoded at
+// compile-time; use |Eigen::Dynamic| to indicate run-time variability.
+// @tparam NumSphericalHarmonics Used to fix the number of spherical harmonic
+// components at compile time; use |Eigen::Dynamic| to indicate run-time
+// variability.
+template <int NumAngles = Eigen::Dynamic,
+ int NumSphericalHarmonics = Eigen::Dynamic>
+class AmbisonicCodecImpl : public AmbisonicCodec {
+ public:
+ // Spherical harmonics encoder matrix.
+ typedef Eigen::Matrix<float, NumSphericalHarmonics, NumAngles> EncoderMatrix;
+ // Spherical harmonics decoder matrix.
+ typedef Eigen::Matrix<float, NumAngles, NumSphericalHarmonics> DecoderMatrix;
+ // Spherical harmonics encoding of a frame or collection of frames (i.e., a
+ // vector of spherical harmonics).
+ typedef Eigen::Matrix<float, NumSphericalHarmonics, 1> EncodedVector;
+ // Decoded sequence of values for each angle / mono frame (i.e., a vector with
+ // a decoded mono frame for each angle).
+ typedef Eigen::Matrix<float, NumAngles, 1> DecodedVector;
+
+ // Creates a codec with the given |ambisonic_order| and spherical |angles| to
+ // compute encoder/decoder matrices.
+ AmbisonicCodecImpl(int ambisonic_order,
+ const std::vector<SphericalAngle>& angles);
+
+ // Implements |AmbisonicCodec|.
+ void EncodeBuffer(const AudioBuffer& input, AudioBuffer* output) override;
+ void DecodeBuffer(const AudioBuffer& input, AudioBuffer* output) override;
+ int ambisonic_order() const override;
+ size_t num_angles() const override;
+ size_t num_spherical_harmonics() const override;
+ const std::vector<SphericalAngle>& angles() const override;
+ void set_angles(const std::vector<SphericalAngle>& angles) override;
+
+ // Encodes the given vector.
+
+ void Encode(const Eigen::Ref<const Eigen::MatrixXf> decoded_vector,
+ Eigen::Ref<Eigen::MatrixXf> encoded_vector);
+
+ // Decodes the given vector.
+
+ void Decode(const Eigen::Ref<const Eigen::MatrixXf> encoded_vector,
+ Eigen::Ref<Eigen::MatrixXf> decoded_vector);
+
+ // Gets the ambisonic sound field encoder matrix.
+ const Eigen::Ref<const Eigen::MatrixXf> GetEncoderMatrix();
+
+ // Gets the ambisonic sound field decoder matrix.
+ const Eigen::Ref<const Eigen::MatrixXf> GetDecoderMatrix();
+
+ // Necessary due to Eigen's alignment requirements on some platforms.
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+
+ private:
+ // Returns the unnormalized spherical harmonic
+ // Y_degree^order(azimuth, elevation).
+ float UnnormalizedSphericalHarmonic(int degree, int order,
+ const SphericalAngle& angle) const;
+
+ // The maximum-ordered ambisonic sound field handled by this codec. In the
+ // case of a periphonic codec, this is the order of the ambisonic sound field.
+ const int ambisonic_order_;
+ // Spherical angles used to compute spherical harmonics. For example, for a
+ // decoder, virtual loudspeaker positions; for an encoder, the position(s) of
+ // virtual sources relative to the listener.
+ std::vector<SphericalAngle> angles_;
+ // Current spherical harmonics encoder matrix if encoder_matrix_invalid_ is
+ // false.
+ EncoderMatrix encoder_matrix_;
+ // True if encoder_matrix_ needs to be recomputed.
+ bool encoder_matrix_invalid_;
+ // Current spherical harmonics decoder matrix if encoder_matrix_invalid_ is
+ // false.
+ DecoderMatrix decoder_matrix_;
+ // True if decoder_matrix_ needs to be recomputed.
+ bool decoder_matrix_invalid_;
+ // The associated Legendre polynomial generator for this codec.
+ AssociatedLegendrePolynomialsGenerator alp_generator_;
+ // Temporary storage for associated Legendre polynomials generated.
+ std::vector<float> associated_legendre_polynomials_temp_;
+};
+
+template <int NumAngles, int NumSphericalHarmonics>
+AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::AmbisonicCodecImpl(
+ int ambisonic_order, const std::vector<SphericalAngle>& angles)
+ : ambisonic_order_(ambisonic_order),
+ alp_generator_(ambisonic_order, false, false) {
+ DCHECK_GE(ambisonic_order_, 0);
+ set_angles(angles);
+}
+
+template <int NumAngles, int NumSphericalHarmonics>
+void AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::Encode(
+ const Eigen::Ref<const Eigen::MatrixXf> decoded_vector,
+ Eigen::Ref<Eigen::MatrixXf> encoded_vector) {
+ encoded_vector.noalias() = GetEncoderMatrix() * decoded_vector;
+}
+
+template <int NumAngles, int NumSphericalHarmonics>
+void AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::Decode(
+ const Eigen::Ref<const Eigen::MatrixXf> encoded_vector,
+ Eigen::Ref<Eigen::MatrixXf> decoded_vector) {
+ decoded_vector.noalias() = GetDecoderMatrix() * encoded_vector;
+}
+
+template <int NumAngles, int NumSphericalHarmonics>
+void AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::EncodeBuffer(
+ const AudioBuffer& input, AudioBuffer* output) {
+ CHECK(output);
+ CHECK_EQ(input.num_channels(), num_angles());
+ CHECK_EQ(output->num_channels(), num_spherical_harmonics());
+ CHECK_EQ(input.num_frames(), output->num_frames());
+
+ Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic,
+ Eigen::RowMajor>,
+ Eigen::Aligned>
+ unencoded_buffer(&input[0][0], num_angles(), output->GetChannelStride());
+
+ Eigen::Map<
+ Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>,
+ Eigen::Aligned>
+ encoded_buffer(&(*output)[0][0], num_spherical_harmonics(),
+ input.GetChannelStride());
+
+ encoded_buffer.noalias() = GetEncoderMatrix() * unencoded_buffer;
+}
+
+template <int NumAngles, int NumSphericalHarmonics>
+void AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::DecodeBuffer(
+ const AudioBuffer& input, AudioBuffer* output) {
+ CHECK(output);
+ CHECK_EQ(input.num_channels(), num_spherical_harmonics());
+ CHECK_EQ(output->num_channels(), num_angles());
+ CHECK_EQ(input.num_frames(), output->num_frames());
+
+ Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic,
+ Eigen::RowMajor>,
+ Eigen::Aligned>
+ encoded_buffer(&input[0][0], num_spherical_harmonics(),
+ input.GetChannelStride());
+
+ Eigen::Map<
+ Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>,
+ Eigen::Aligned>
+ decoded_buffer(&(*output)[0][0], num_angles(),
+ output->GetChannelStride());
+
+ decoded_buffer.noalias() = GetDecoderMatrix() * encoded_buffer;
+}
+
+template <int NumAngles, int NumSphericalHarmonics>
+const Eigen::Ref<const Eigen::MatrixXf>
+AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::GetEncoderMatrix() {
+ if (encoder_matrix_invalid_) {
+ encoder_matrix_ = EncoderMatrix(
+ GetNumPeriphonicComponents(ambisonic_order_), angles_.size());
+ for (int col = 0; col < encoder_matrix_.cols(); col++) {
+ const SphericalAngle& angle = angles_[col];
+ associated_legendre_polynomials_temp_ =
+ alp_generator_.Generate(std::sin(angle.elevation()));
+ // Compute the actual spherical harmonics using the generated polynomials.
+ for (int degree = 0; degree <= ambisonic_order_; degree++) {
+ for (int order = -degree; order <= degree; order++) {
+ const int row = AcnSequence(degree, order);
+ if (row == -1) {
+ // Skip this spherical harmonic.
+ continue;
+ }
+ encoder_matrix_(row, col) =
+ Sn3dNormalization(degree, order) *
+ UnnormalizedSphericalHarmonic(degree, order, angle);
+ }
+ }
+ }
+ encoder_matrix_invalid_ = false;
+ }
+ return encoder_matrix_;
+}
+
+template <int NumAngles, int NumSphericalHarmonics>
+const Eigen::Ref<const Eigen::MatrixXf>
+AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::GetDecoderMatrix() {
+ if (decoder_matrix_invalid_) {
+ decoder_matrix_ = Pseudoinverse<Eigen::MatrixXf>(GetEncoderMatrix());
+ // Condition number of the encoding/decoding matrices. We use the fact that
+ // the decoding matrix is already a (pseudo)-inverse of the encoding matrix.
+ const float condition_number =
+ static_cast<float>(GetEncoderMatrix().norm() * decoder_matrix_.norm());
+ const float num_rows = static_cast<float>(GetEncoderMatrix().rows());
+ const float num_cols = static_cast<float>(GetEncoderMatrix().cols());
+ if (condition_number >
+ 1.0f / (std::max(num_rows, num_cols) * kEpsilonFloat)) {
+ LOG(WARNING) << "Ambisonic decoding matrix is ill-conditioned. Results "
+ << "may be inaccurate.";
+ }
+ decoder_matrix_invalid_ = false;
+ }
+ return decoder_matrix_;
+}
+
+template <int NumAngles, int NumSphericalHarmonics>
+int AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::ambisonic_order()
+ const {
+ return ambisonic_order_;
+}
+
+template <int NumAngles, int NumSphericalHarmonics>
+size_t AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::num_angles()
+ const {
+ return angles_.size();
+}
+
+template <int NumAngles, int NumSphericalHarmonics>
+size_t AmbisonicCodecImpl<
+ NumAngles, NumSphericalHarmonics>::num_spherical_harmonics() const {
+ // Return the worst-case scenario (the number of coefficients for a
+ // periphonic sound field).
+ return GetNumPeriphonicComponents(ambisonic_order_);
+}
+
+template <int NumAngles, int NumSphericalHarmonics>
+const std::vector<SphericalAngle>&
+AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::angles() const {
+ return angles_;
+}
+
+template <int NumAngles, int NumSphericalHarmonics>
+void AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::set_angles(
+ const std::vector<SphericalAngle>& angles) {
+ CHECK_GT(angles.size(), 0);
+
+ angles_ = angles;
+ encoder_matrix_invalid_ = decoder_matrix_invalid_ = true;
+}
+
+template <int NumAngles, int NumSphericalHarmonics>
+float AmbisonicCodecImpl<NumAngles, NumSphericalHarmonics>::
+ UnnormalizedSphericalHarmonic(int degree, int order,
+ const SphericalAngle& angle) const {
+ const float last_term =
+ (order >= 0) ? std::cos(static_cast<float>(order) * angle.azimuth())
+ : std::sin(static_cast<float>(-order) * angle.azimuth());
+ return associated_legendre_polynomials_temp_[alp_generator_.GetIndex(
+ degree, std::abs(order))] *
+ last_term;
+}
+
+// Codec for a single source.
+template <int NumSphericalHarmonics = Eigen::Dynamic>
+using MonoAmbisonicCodec = AmbisonicCodecImpl<1, NumSphericalHarmonics>;
+
+// Codec for a N-speaker first-order periphonic setup.
+template <int NumAngles>
+using FirstOrderPeriphonicAmbisonicCodec =
+ AmbisonicCodecImpl<NumAngles, GetNumPeriphonicComponentsStatic<1>::value>;
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_AMBISONICS_AMBISONIC_CODEC_IMPL_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_codec_impl_test.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_codec_impl_test.cc
new file mode 100644
index 000000000..c7f0900be
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_codec_impl_test.cc
@@ -0,0 +1,354 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/ambisonic_codec_impl.h"
+
+#include <cmath>
+#include <memory>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+namespace {
+
+const int kCubeNumVertices = 8;
+const float kCubeVertices[kCubeNumVertices][3] = {
+ {-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, -1.0f}, {-1.0f, 1.0f, 1.0f},
+ {1.0f, 1.0f, 1.0f}, {-1.0f, -1.0f, -1.0f}, {1.0f, -1.0f, -1.0f},
+ {-1.0f, -1.0f, 1.0f}, {1.0f, -1.0f, 1.0f}};
+
+// Generates a third-order B-format encoder matrix.
+AmbisonicCodecImpl<>::EncoderMatrix GenerateThirdOrderBFormatEncoderMatrix(
+ const SphericalAngle& spherical_angle) {
+ const size_t kNumSphericalHarmonics = GetNumPeriphonicComponents(3);
+ const size_t kNumAngles = 1;
+ AmbisonicCodecImpl<>::EncoderMatrix encoder_matrix(kNumSphericalHarmonics,
+ kNumAngles);
+ const float azimuth_rad = spherical_angle.azimuth();
+ const float elevation_rad = spherical_angle.elevation();
+ encoder_matrix << 1.0f, std::cos(elevation_rad) * std::sin(azimuth_rad),
+ std::sin(elevation_rad), std::cos(elevation_rad) * std::cos(azimuth_rad),
+ kSqrtThree / 2.0f * std::cos(elevation_rad) * std::cos(elevation_rad) *
+ std::sin(2.0f * azimuth_rad),
+ kSqrtThree / 2.0f * std::sin(2.0f * elevation_rad) *
+ std::sin(azimuth_rad),
+ 0.5f * (3.0f * std::sin(elevation_rad) * std::sin(elevation_rad) - 1.0f),
+ kSqrtThree / 2.0f * std::sin(2.0f * elevation_rad) *
+ std::cos(azimuth_rad),
+ kSqrtThree / 2.0f * std::cos(elevation_rad) * std::cos(elevation_rad) *
+ std::cos(2.0f * azimuth_rad),
+ std::sqrt(5.0f / 8.0f) * IntegerPow(std::cos(elevation_rad), 3) *
+ std::sin(3.0f * azimuth_rad),
+ std::sqrt(15.0f) / 2.0f * std::sin(elevation_rad) *
+ std::cos(elevation_rad) * std::cos(elevation_rad) *
+ std::sin(2.0f * azimuth_rad),
+ std::sqrt(3.0f / 8.0f) * std::cos(elevation_rad) *
+ (5.0f * std::sin(elevation_rad) * std::sin(elevation_rad) - 1.0f) *
+ std::sin(azimuth_rad),
+ 0.5f * std::sin(elevation_rad) *
+ (5.0f * std::sin(elevation_rad) * std::sin(elevation_rad) - 3.0f),
+ std::sqrt(3.0f / 8.0f) * std::cos(elevation_rad) *
+ (5.0f * std::sin(elevation_rad) * std::sin(elevation_rad) - 1.0f) *
+ std::cos(azimuth_rad),
+ std::sqrt(15.0f) / 2.0f * std::sin(elevation_rad) *
+ std::cos(elevation_rad) * std::cos(elevation_rad) *
+ std::cos(2.0f * azimuth_rad),
+ std::sqrt(5.0f / 8.0f) * IntegerPow(std::cos(elevation_rad), 3) *
+ std::cos(3.0f * azimuth_rad);
+ return encoder_matrix;
+}
+
+class AmbisonicCodecTest : public ::testing::Test {
+ protected:
+ AmbisonicCodecTest() {
+ for (int i = 0; i < kCubeNumVertices; i++) {
+ const float azimuth_rad =
+ atan2f(kCubeVertices[i][2], kCubeVertices[i][0]);
+ const float elevation_rad =
+ std::asin(kCubeVertices[i][1] /
+ std::sqrt(kCubeVertices[i][0] * kCubeVertices[i][0] +
+ kCubeVertices[i][1] * kCubeVertices[i][1] +
+ kCubeVertices[i][2] * kCubeVertices[i][2]));
+
+ cube_angles_.push_back(SphericalAngle(azimuth_rad, elevation_rad));
+ }
+
+ codec_first_order_cube_ = std::unique_ptr<AmbisonicCodecImpl<>>(
+ new AmbisonicCodecImpl<>(1, cube_angles_));
+ }
+
+ std::unique_ptr<AmbisonicCodecImpl<>> codec_first_order_cube_;
+ std::vector<SphericalAngle> cube_angles_;
+};
+
+// Tests that encoder and decoder matrices are ~inverses of each other.
+TEST_F(AmbisonicCodecTest, EncoderDecoderIdentity) {
+ const auto encoder_matrix = codec_first_order_cube_->GetEncoderMatrix();
+ const auto decoder_matrix = codec_first_order_cube_->GetDecoderMatrix();
+
+ const auto should_be_identity = encoder_matrix * decoder_matrix;
+ EXPECT_TRUE(should_be_identity.isApprox(
+ decltype(should_be_identity)::Identity(should_be_identity.rows(),
+ should_be_identity.cols()),
+ kEpsilonFloat))
+ << "Matrix should be within " << kEpsilonFloat
+ << " of an identity matrix: \n"
+ << should_be_identity;
+}
+
+// Tests that Encode and Decode are ~inverse operations.
+TEST_F(AmbisonicCodecTest, EncodeDecodeIsInverse) {
+ AmbisonicCodecImpl<>::DecodedVector unencoded_vector(8);
+ unencoded_vector << 1, 2, 3, 4, 5, 6, 7, 8;
+ AmbisonicCodecImpl<>::EncodedVector encoded_vector(4);
+ codec_first_order_cube_->Encode(unencoded_vector, encoded_vector);
+ AmbisonicCodecImpl<>::DecodedVector decoded_vector(8);
+ codec_first_order_cube_->Decode(encoded_vector, decoded_vector);
+
+ EXPECT_TRUE(unencoded_vector.isApprox(decoded_vector, kEpsilonFloat))
+ << "Decoded vector should be within " << kEpsilonFloat << " of: \n"
+ << unencoded_vector;
+}
+
+// Tests that EncodeBuffer and Decode (over a buffer) are ~inverse operations.
+TEST_F(AmbisonicCodecTest, EncodeBufferDecodeVectorsIsInverse) {
+ const int kNumElements = 256;
+ const int kNumSphericalHarmonics = GetNumPeriphonicComponentsStatic<1>::value;
+
+ AudioBuffer unencoded_buffer(kCubeNumVertices, kNumElements);
+ // Produce a buffer of successive numbers between -1 and 1.
+ for (int element = 0; element < kNumElements; ++element) {
+ for (int angle = 0; angle < kCubeNumVertices; ++angle) {
+ unencoded_buffer[angle][element] =
+ static_cast<float>(element * kCubeNumVertices + angle) /
+ (0.5f * kNumElements * kCubeNumVertices) -
+ 1.0f;
+ }
+ }
+
+ AudioBuffer encoded_buffer(kNumSphericalHarmonics, kNumElements);
+ codec_first_order_cube_->EncodeBuffer(unencoded_buffer, &encoded_buffer);
+
+ // Verify the encoded buffer and decoded vectors are ~identical.
+ for (int element = 0; element < kNumElements; ++element) {
+ AmbisonicCodecImpl<>::EncodedVector encoded_vector(kNumSphericalHarmonics);
+ for (int harmonic = 0; harmonic < kNumSphericalHarmonics; ++harmonic) {
+ encoded_vector[harmonic] = encoded_buffer[harmonic][element];
+ }
+ AmbisonicCodecImpl<>::DecodedVector decoded_vector(kCubeNumVertices);
+ codec_first_order_cube_->Decode(encoded_vector, decoded_vector);
+ for (int angle = 0; angle < kCubeNumVertices; ++angle) {
+ EXPECT_NEAR(unencoded_buffer[angle][element], decoded_vector[angle],
+ kEpsilonFloat);
+ }
+ }
+}
+
+// Tests that Encode (over a buffer) and DecodeBuffer are ~inverse operations.
+TEST_F(AmbisonicCodecTest, EncodeVectorsDecodeBufferIsInverse) {
+ const int kNumElements = 256;
+ const int kNumSphericalHarmonics = GetNumPeriphonicComponentsStatic<1>::value;
+
+ AudioBuffer unencoded_buffer(kCubeNumVertices, kNumElements);
+ // Produce a buffer of successive numbers between -1 and 1.
+ for (int element = 0; element < kNumElements; ++element) {
+ for (int angle = 0; angle < kCubeNumVertices; ++angle) {
+ unencoded_buffer[angle][element] =
+ static_cast<float>(element * kCubeNumVertices + angle) /
+ (0.5f * kNumElements * kCubeNumVertices) -
+ 1.0f;
+ }
+ }
+
+ AudioBuffer encoded_buffer(kNumSphericalHarmonics, kNumElements);
+ // Produce the encoded version of unencoded_buffer.
+ for (int element = 0; element < kNumElements; ++element) {
+ AmbisonicCodecImpl<>::DecodedVector unencoded_vector(kCubeNumVertices);
+ for (int angle = 0; angle < kCubeNumVertices; ++angle) {
+ unencoded_vector[angle] = unencoded_buffer[angle][element];
+ }
+ AmbisonicCodecImpl<>::EncodedVector encoded_vector(kNumSphericalHarmonics);
+ codec_first_order_cube_->Encode(unencoded_vector, encoded_vector);
+ for (int harmonic = 0; harmonic < kNumSphericalHarmonics; ++harmonic) {
+ encoded_buffer[harmonic][element] = encoded_vector[harmonic];
+ }
+ }
+
+ AudioBuffer decoded_buffer(kCubeNumVertices, kNumElements);
+ codec_first_order_cube_->DecodeBuffer(encoded_buffer, &decoded_buffer);
+ // Verify the decoded buffer and unencoded buffer are ~identical.
+ for (int element = 0; element < kNumElements; ++element) {
+ for (int angle = 0; angle < kCubeNumVertices; ++angle) {
+ EXPECT_NEAR(unencoded_buffer[angle][element],
+ decoded_buffer[angle][element], kEpsilonFloat);
+ }
+ }
+}
+
+// Tests that EncodeBuffer and DecodeBuffer are ~inverse operations.
+TEST_F(AmbisonicCodecTest, EncodeBufferDecodeBufferIsInverse) {
+ const int kNumElements = 256;
+ const int kNumSphericalHarmonics = GetNumPeriphonicComponentsStatic<1>::value;
+
+ AudioBuffer unencoded_buffer(kCubeNumVertices, kNumElements);
+ // Produce a buffer of successive numbers between -1 and 1.
+ for (int element = 0; element < kNumElements; ++element) {
+ for (int angle = 0; angle < kCubeNumVertices; ++angle) {
+ unencoded_buffer[angle][element] =
+ static_cast<float>(element * kCubeNumVertices + angle) /
+ (0.5f * kNumElements * kCubeNumVertices) -
+ 1.0f;
+ }
+ }
+
+
+
+ AudioBuffer encoded_buffer(kNumSphericalHarmonics, kNumElements);
+ // Produce the encoded version of unencoded_buffer.
+ for (int element = 0; element < kNumElements; ++element) {
+ AmbisonicCodecImpl<>::DecodedVector unencoded_vector(kCubeNumVertices);
+ for (int angle = 0; angle < kCubeNumVertices; ++angle) {
+ unencoded_vector[angle] = unencoded_buffer[angle][element];
+ }
+ AmbisonicCodecImpl<>::EncodedVector encoded_vector(kNumSphericalHarmonics);
+ codec_first_order_cube_->Encode(unencoded_vector, encoded_vector);
+ for (int harmonic = 0; harmonic < kNumSphericalHarmonics; ++harmonic) {
+ encoded_buffer[harmonic][element] = encoded_vector[harmonic];
+ }
+ }
+
+ AudioBuffer decoded_buffer(kCubeNumVertices, kNumElements);
+ codec_first_order_cube_->DecodeBuffer(encoded_buffer, &decoded_buffer);
+ // Verify the decoded buffer and unencoded buffer are ~identical.
+ for (int element = 0; element < kNumElements; ++element) {
+ for (int angle = 0; angle < kCubeNumVertices; ++angle) {
+ EXPECT_NEAR(unencoded_buffer[angle][element],
+ decoded_buffer[angle][element], kEpsilonFloat);
+ }
+ }
+}
+
+// Tests that third-order encoding coefficients produced by Codec are correct.
+TEST_F(AmbisonicCodecTest, ThirdOrderEncodingCoefficients) {
+ const int kNumSphericalHarmonics = GetNumPeriphonicComponentsStatic<3>::value;
+
+ const float kAzimuthStart = -180.0f;
+ const float kAzimuthStop = 180.0f;
+ const float kAzimuthStep = 10.0f;
+ for (float azimuth = kAzimuthStart; azimuth <= kAzimuthStop;
+ azimuth += kAzimuthStep) {
+ const float kElevationStart = -90.0f;
+ const float kElevationStop = 90.0f;
+ const float kElevationStep = 10.0f;
+ for (float elevation = kElevationStart; elevation <= kElevationStop;
+ elevation += kElevationStep) {
+ const int kAmbisonicOrder = 3;
+
+ const SphericalAngle kSphericalAngle =
+ SphericalAngle::FromDegrees(azimuth, elevation);
+ AmbisonicCodecImpl<> mono_codec(kAmbisonicOrder, {kSphericalAngle});
+
+ AmbisonicCodecImpl<>::DecodedVector kUnencodedVector(1);
+ kUnencodedVector << 0.12345f;
+ AmbisonicCodecImpl<>::EncodedVector codec_encoded_vector(
+ kNumSphericalHarmonics);
+ mono_codec.Encode(kUnencodedVector, codec_encoded_vector);
+ const AmbisonicCodecImpl<>::EncoderMatrix
+ expected_b_format_encoder_matrix =
+ GenerateThirdOrderBFormatEncoderMatrix(kSphericalAngle);
+ const AmbisonicCodecImpl<>::EncodedVector expected_encoded_vector =
+ static_cast<AmbisonicCodecImpl<>::EncodedVector>(
+ expected_b_format_encoder_matrix * kUnencodedVector);
+ ASSERT_TRUE(
+ codec_encoded_vector.isApprox(expected_encoded_vector, kEpsilonFloat))
+ << "Codec encoded vector: \n"
+ << codec_encoded_vector << "\n should be within " << kEpsilonFloat
+ << " of expected: \n"
+ << expected_encoded_vector;
+ }
+ }
+}
+
+// Tests that Sphere16 decoding matrix (ACN/SN3D) coefficients produced by
+// Codec
+// are correct.
+TEST_F(AmbisonicCodecTest, Sphere16DecoderCoefficients) {
+ // Expected decoder coefficients for the Sphere16 setup (ACN/SN3D) obtained
+ // using //matlab/ambix/ambdecodematrix.m.
+ const std::vector<float> kExpectedDecoderCoefficients{
+ 0.0625000000000001f, 0.0625000000000000f, 0.0625000000000000f,
+ 0.0625000000000000f, 0.0625000000000000f, 0.0625000000000000f,
+ 0.0625000000000000f, 0.0625000000000000f, 0.0625000000000000f,
+ 0.0625000000000000f, 0.0625000000000000f, 0.0625000000000000f,
+ 0.0625000000000000f, 0.0625000000000000f, 0.0625000000000000f,
+ 0.0625000000000000f, -1.94257951433956e-17f, 0.128971506904330f,
+ 0.182393254223798f, 0.128971506904330f, 1.73262494006158e-16f,
+ -0.128971506904330f, -0.182393254223798f, -0.128971506904330f,
+ -2.79581233842125e-16f, 0.116817928199481f, -1.55203460683255e-16f,
+ -0.116817928199482f, 0.0826027491940168f, 0.0826027491940169f,
+ -0.0826027491940164f, -0.0826027491940165f, -0.0440164745323702f,
+ 0.0440164745323702f, -0.0440164745323703f, 0.0440164745323702f,
+ -0.0440164745323703f, 0.0440164745323702f, -0.0440164745323702f,
+ 0.0440164745323703f, 0.146497161016369f, 0.146497161016369f,
+ 0.146497161016369f, 0.146497161016369f, -0.146497161016369f,
+ -0.146497161016369f, -0.146497161016369f, -0.146497161016369f,
+ 0.182393254223799f, 0.128971506904330f, 7.26758979552257e-17f,
+ -0.128971506904330f, -0.182393254223798f, -0.128971506904330f,
+ -5.02371557522155e-17f, 0.128971506904330f, 0.116817928199482f,
+ 6.83999538850465e-17f, -0.116817928199482f, -3.47728677633385e-17f,
+ 0.0826027491940167f, -0.0826027491940166f, -0.0826027491940166f,
+ 0.0826027491940166f};
+ const int kAmbisonicOrder = 1;
+ const int kNumChannels = 4;
+ const int kNumVirtualSpeakers = 16;
+ std::vector<SphericalAngle> sphere16_angles{
+ SphericalAngle::FromDegrees(0.0f, -13.6f),
+ SphericalAngle::FromDegrees(45.0f, 13.6f),
+ SphericalAngle::FromDegrees(90.0f, -13.6f),
+ SphericalAngle::FromDegrees(135.0f, 13.6f),
+ SphericalAngle::FromDegrees(180.0f, -13.6f),
+ SphericalAngle::FromDegrees(-135.0f, 13.6f),
+ SphericalAngle::FromDegrees(-90.0f, -13.6f),
+ SphericalAngle::FromDegrees(-45.0f, 13.6f),
+ SphericalAngle::FromDegrees(0.0f, 51.5f),
+ SphericalAngle::FromDegrees(90.0f, 51.5f),
+ SphericalAngle::FromDegrees(180.0f, 51.5f),
+ SphericalAngle::FromDegrees(-90.0f, 51.5f),
+ SphericalAngle::FromDegrees(45.0f, -51.5f),
+ SphericalAngle::FromDegrees(135.0f, -51.5f),
+ SphericalAngle::FromDegrees(-135.0f, -51.5f),
+ SphericalAngle::FromDegrees(-45.0f, -51.5f)};
+
+ std::unique_ptr<AmbisonicCodecImpl<>> codec_first_order_sphere16(
+ new AmbisonicCodecImpl<>(kAmbisonicOrder, sphere16_angles));
+ const AmbisonicCodecImpl<>::DecoderMatrix decoder_matrix =
+ codec_first_order_sphere16->GetDecoderMatrix();
+ // Check if the size of the decoding matrix is correct.
+ ASSERT_EQ(decoder_matrix.size(), kNumVirtualSpeakers * kNumChannels);
+ // Check each coefficient against MATLAB.
+ for (size_t i = 0; i < kExpectedDecoderCoefficients.size(); ++i) {
+ EXPECT_NEAR(kExpectedDecoderCoefficients[i], decoder_matrix(i),
+ kEpsilonFloat);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_lookup_table.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_lookup_table.cc
new file mode 100644
index 000000000..2a2bc1b68
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_lookup_table.cc
@@ -0,0 +1,231 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/ambisonic_lookup_table.h"
+
+#include "ambisonics/ambisonic_spread_coefficients.h"
+#include "ambisonics/associated_legendre_polynomials_generator.h"
+#include "ambisonics/utils.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+
+
+namespace vraudio {
+
+namespace {
+
+// Number of azimuth angles to store in the pre-computed encoder lookup table
+// (for 0 - 90 degrees using 1 degree increments).
+const size_t kNumAzimuths = 91;
+
+// Number of elevation angles to store in the pre-computed encoder lookup table
+// (for 0 - 90 degrees using 1 degree increments).
+const size_t kNumElevations = 91;
+
+// Number of Cartesian axes for which to compute spherical harmonic symmetries.
+const size_t kNumAxes = 3;
+
+// Minimum angular source spreads at different orders for which we start to
+// apply spread gain correction coefficients. Array index corresponds to
+// ambisonic order. For more information about sound source spread control,
+// please refer to the Matlab code and the corresponding paper.
+const int kMinSpreads[kMaxSupportedAmbisonicOrder + 1] = {361, 54, 40, 31};
+
+// Spread coefficients are stored sequentially in a 1-d table. Therefore, to
+// access coefficients for the required ambisonic order we need to apply offsets
+// which are equal to the total number of coefficients for previous orders. For
+// example, the total number of coefficient in each ambisonic order 'n' is
+// equal to the number of unique orders multiplied by number of unique spreads,
+// i.e.: (n + 1) * (360 - kMinSpreads[n] + 1).
+const int kSpreadCoeffOffsets[kMaxSupportedAmbisonicOrder + 1] = {0, 1, 615,
+ 1578};
+
+size_t GetEncoderTableIndex(size_t i, size_t j, size_t k, size_t width,
+ size_t depth) {
+ return i * width * depth + j * depth + k;
+}
+
+size_t GetSymmetriesTableIndex(size_t i, size_t j, size_t width) {
+ return i * width + j;
+}
+
+int GetSpreadTableIndex(int ambisonic_order, float source_spread_deg) {
+ // Number of unique ambisonic orders in the sound field. For example, in a
+ // first order sound field we have components of order 0 and 1.
+ const int num_unique_orders = ambisonic_order + 1;
+ return kSpreadCoeffOffsets[ambisonic_order] +
+ num_unique_orders * (static_cast<int>(source_spread_deg) -
+ kMinSpreads[ambisonic_order]);
+}
+
+} // namespace
+
+AmbisonicLookupTable::AmbisonicLookupTable(int max_ambisonic_order)
+ : max_ambisonic_order_(max_ambisonic_order),
+ max_num_coeffs_in_table_(
+ GetNumPeriphonicComponents(max_ambisonic_order_) - 1),
+ encoder_table_(kNumAzimuths * kNumElevations * max_num_coeffs_in_table_),
+ symmetries_table_(kNumAxes * max_num_coeffs_in_table_) {
+ DCHECK_GE(max_ambisonic_order_, 0);
+ DCHECK_LE(max_ambisonic_order_, kMaxSupportedAmbisonicOrder);
+ ComputeEncoderTable();
+ ComputeSymmetriesTable();
+}
+
+void AmbisonicLookupTable::GetEncodingCoeffs(
+ int ambisonic_order, const SphericalAngle& source_direction,
+ float source_spread_deg, std::vector<float>* encoding_coeffs) const {
+
+ DCHECK_GE(ambisonic_order, 0);
+ DCHECK_LE(ambisonic_order, kMaxSupportedAmbisonicOrder);
+ DCHECK_GE(source_spread_deg, 0.0f);
+ DCHECK_LE(source_spread_deg, 360.0f);
+ DCHECK(encoding_coeffs);
+ // Raw coefficients are stored only for ambisonic orders 1 and up, since 0th
+ // order raw coefficient is always 1.
+ const size_t num_raw_coeffs = GetNumPeriphonicComponents(ambisonic_order) - 1;
+ // The actual number of returned Ambisonic coefficients is therefore
+ // |num_raw_coeffs + 1|.
+ DCHECK_EQ(encoding_coeffs->size(), num_raw_coeffs + 1);
+ DCHECK_GE(source_direction.azimuth(), -kPi);
+ DCHECK_LE(source_direction.azimuth(), kTwoPi);
+ DCHECK_GE(source_direction.elevation(), -kHalfPi);
+ DCHECK_LE(source_direction.elevation(), kHalfPi);
+ const int azimuth_deg =
+ source_direction.azimuth() < kPi
+ ? static_cast<int>(source_direction.azimuth() * kDegreesFromRadians)
+ : static_cast<int>(source_direction.azimuth() * kDegreesFromRadians) -
+ 360;
+ const int elevation_deg =
+ static_cast<int>(source_direction.elevation() * kDegreesFromRadians);
+ const size_t abs_azimuth_deg = static_cast<size_t>(std::abs(azimuth_deg));
+ const size_t azimuth_idx =
+ abs_azimuth_deg > 90 ? 180 - abs_azimuth_deg : abs_azimuth_deg;
+ const size_t elevation_idx = static_cast<size_t>(std::abs(elevation_deg));
+ (*encoding_coeffs)[0] = 1.0f;
+ for (size_t raw_coeff_idx = 0; raw_coeff_idx < num_raw_coeffs;
+ ++raw_coeff_idx) {
+ // Variable to hold information about spherical harmonic phase flip. 1 means
+ // no flip; -1 means 180 degrees flip.
+ float flip = 1.0f;
+ // The following three 'if' statements implement the logic to correct the
+ // phase of the current spherical harmonic, depending on which quadrant the
+ // sound source is located in. For more information, please see the Matlab
+ // code and the corresponding paper.
+ if (azimuth_deg < 0) {
+ flip = symmetries_table_[GetSymmetriesTableIndex(
+ 0, raw_coeff_idx, max_num_coeffs_in_table_)];
+ }
+ if (elevation_deg < 0) {
+ flip *= symmetries_table_[GetSymmetriesTableIndex(
+ 1, raw_coeff_idx, max_num_coeffs_in_table_)];
+ }
+ if (abs_azimuth_deg > 90) {
+ flip *= symmetries_table_[GetSymmetriesTableIndex(
+ 2, raw_coeff_idx, max_num_coeffs_in_table_)];
+ }
+ const size_t encoder_table_idx =
+ GetEncoderTableIndex(azimuth_idx, elevation_idx, raw_coeff_idx,
+ kNumElevations, max_num_coeffs_in_table_);
+ (*encoding_coeffs)[raw_coeff_idx + 1] =
+ encoder_table_[encoder_table_idx] * flip;
+ }
+
+ // If the spread is more than min. theoretical spread for the given
+ // |ambisonic_order|, multiply the encoding coefficients by the required
+ // spread control gains from the |kSpreadCoeffs| lookup table.
+ if (source_spread_deg >= kMinSpreads[ambisonic_order]) {
+ const int spread_table_idx =
+ GetSpreadTableIndex(ambisonic_order, source_spread_deg);
+ (*encoding_coeffs)[0] *= kSpreadCoeffs[spread_table_idx];
+ for (size_t coeff = 1; coeff < encoding_coeffs->size(); ++coeff) {
+ const int current_coefficient_degree =
+ GetPeriphonicAmbisonicOrderForChannel(coeff);
+ (*encoding_coeffs)[coeff] *=
+ kSpreadCoeffs[spread_table_idx + current_coefficient_degree];
+ }
+ }
+}
+
+void AmbisonicLookupTable::ComputeEncoderTable() {
+
+ // Associated Legendre polynomial generator.
+ AssociatedLegendrePolynomialsGenerator alp_generator(
+ max_ambisonic_order_, /*condon_shortley_phase=*/false,
+ /*compute_negative_order=*/false);
+ // Temporary storage for associated Legendre polynomials generated.
+ std::vector<float> temp_associated_legendre_polynomials;
+ for (size_t azimuth_idx = 0; azimuth_idx < kNumAzimuths; ++azimuth_idx) {
+ for (size_t elevation_idx = 0; elevation_idx < kNumElevations;
+ ++elevation_idx) {
+ const SphericalAngle angle(
+ static_cast<float>(azimuth_idx) * kRadiansFromDegrees,
+ static_cast<float>(elevation_idx) * kRadiansFromDegrees);
+ temp_associated_legendre_polynomials =
+ alp_generator.Generate(std::sin(angle.elevation()));
+ // First spherical harmonic is always equal 1 for all angles so we do not
+ // need to compute and store it.
+ for (int degree = 1; degree <= max_ambisonic_order_; ++degree) {
+ for (int order = -degree; order <= degree; ++order) {
+ const size_t alp_index =
+ alp_generator.GetIndex(degree, std::abs(order));
+ const float alp_value =
+ temp_associated_legendre_polynomials[alp_index];
+ const size_t raw_coeff_idx = AcnSequence(degree, order) - 1;
+ const size_t encoder_table_idx =
+ GetEncoderTableIndex(azimuth_idx, elevation_idx, raw_coeff_idx,
+ kNumElevations, max_num_coeffs_in_table_);
+ encoder_table_[encoder_table_idx] =
+ Sn3dNormalization(degree, order) *
+ UnnormalizedSphericalHarmonic(alp_value, order, angle.azimuth());
+ }
+ }
+ }
+ }
+}
+
+void AmbisonicLookupTable::ComputeSymmetriesTable() {
+
+ for (int degree = 1; degree <= max_ambisonic_order_; ++degree) {
+ for (int order = -degree; order <= degree; ++order) {
+ const size_t raw_coeff_idx = AcnSequence(degree, order) - 1;
+ // Symmetry wrt the left-right axis (Y).
+ symmetries_table_[GetSymmetriesTableIndex(0, raw_coeff_idx,
+ max_num_coeffs_in_table_)] =
+ order < 0 ? -1.0f : 1.0f;
+ // Symmetry wrt the up-down axis (Z).
+ symmetries_table_[GetSymmetriesTableIndex(1, raw_coeff_idx,
+ max_num_coeffs_in_table_)] =
+ static_cast<float>(IntegerPow(-1, degree + order));
+ // Symmetry wrt the front-back axis (X).
+ symmetries_table_[GetSymmetriesTableIndex(2, raw_coeff_idx,
+ max_num_coeffs_in_table_)] =
+ order < 0 ? static_cast<float>(-IntegerPow(-1, std::abs(order)))
+ : static_cast<float>(IntegerPow(-1, order));
+ }
+ }
+}
+
+float AmbisonicLookupTable::UnnormalizedSphericalHarmonic(float alp_value,
+ int order,
+ float azimuth_rad) {
+ const float horizontal_term =
+ (order >= 0) ? std::cos(static_cast<float>(order) * azimuth_rad)
+ : std::sin(static_cast<float>(-order) * azimuth_rad);
+ return alp_value * horizontal_term;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_lookup_table.h b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_lookup_table.h
new file mode 100644
index 000000000..096068d0a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_lookup_table.h
@@ -0,0 +1,92 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_AMBISONICS_AMBISONIC_LOOKUP_TABLE_H_
+#define RESONANCE_AUDIO_AMBISONICS_AMBISONIC_LOOKUP_TABLE_H_
+
+#include <vector>
+
+#include "base/spherical_angle.h"
+
+namespace vraudio {
+
+// Represents a lookup table for encoding of Ambisonic periphonic sound fields.
+// Supports arbitrary Ambisonic order and uses AmbiX convention (ACN channel
+// sequencing, SN3D normalization).
+class AmbisonicLookupTable {
+ public:
+ // Creates Ambisonic (AmbiX) encoder lookup table given the
+ // |max_ambisonic_order| used by the client. AmbiX convention uses ACN channel
+ // sequencing and SN3D normalization.
+ explicit AmbisonicLookupTable(int max_ambisonic_order);
+
+ // Gets spherical harmonic encoding coefficients for a given order and
+ // writes them to |encoding_coeffs|.
+ //
+ // @param ambisonic_order Ambisonic order of the encoded sound source.
+ // @param source_direction Direction of a sound source in spherical
+ // coordinates.
+ // @param source_spread_deg Encoded sound source angular spread in degrees.
+ // @param encoding_coeffs Pointer to a vector of Ambisonic encoding
+ // coefficients.
+ void GetEncodingCoeffs(int ambisonic_order,
+ const SphericalAngle& source_direction,
+ float source_spread_deg,
+ std::vector<float>* encoding_coeffs) const;
+
+ private:
+ // Computes a lookup table of encoding coefficients for one quadrant of the
+ // sphere.
+ void ComputeEncoderTable();
+
+ // Computes a table of spherical harmonics symmetry information for all
+ // cartesian axes. Value of 1 indicates that the current spherical harmonic is
+ // symmetric with respect to the current axis. Value of -1 indicates that the
+ // current spherical harmonic is anti-symmetric with respect to the current
+ // axis.
+ void ComputeSymmetriesTable();
+
+ // Returns the unnormalized spherical harmonic coefficient:
+ // Y_degree^order(azimuth, elevation).
+ //
+ // @param alp_value Associated Legendre polynomial for the given degree and
+ // order evaluated at sin elevation angle: P_degree^order(sin(elevation)).
+ // @param order Order of the Associated Legendre polynomial.
+ // @param azimuth_rad Azimuth angle in radians.
+ // @return Unnormalized spherical harmonic coefficient.
+ float UnnormalizedSphericalHarmonic(float alp_value, int order,
+ float azimuth_rad);
+
+ // Ambisonic order.
+ const int max_ambisonic_order_;
+
+ // Maximum number of coefficients to be stored in the lookup table equal to
+ // the number of Ambisonic channels for |max_ambisonic_order_| - 1. This is
+ // because we do not need to store the coefficient for the first spherical
+ // harmonic coefficient as it is always 1.
+ const size_t max_num_coeffs_in_table_;
+
+ // Lookup table for storing Ambisonic encoding coefficients for one quadrant
+ // of the sphere.
+ std::vector<float> encoder_table_;
+
+ // Lookup table for storing information about spherical harmonic symmetries.
+ std::vector<float> symmetries_table_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_AMBISONICS_AMBISONIC_LOOKUP_TABLE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_lookup_table_test.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_lookup_table_test.cc
new file mode 100644
index 000000000..8f0c9c9a6
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_lookup_table_test.cc
@@ -0,0 +1,240 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/ambisonic_lookup_table.h"
+
+#include <cmath>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "ambisonics/utils.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+namespace {
+
+const int kSourceAmbisonicOrder0 = 0;
+const int kSourceAmbisonicOrder1 = 1;
+const int kSourceAmbisonicOrder2 = 2;
+const int kSourceAmbisonicOrder3 = 3;
+
+// Minimum angular source spread of 0 ensures that no gain correction
+// coefficients should be applied to the Ambisonic encoding coefficients.
+const float kMinSpreadDeg = 0.0f;
+
+} // namespace
+
+class AmbisonicLookupTableTest : public ::testing::TestWithParam<int> {
+ protected:
+ AmbisonicLookupTableTest()
+ : lookup_table_(kMaxSupportedAmbisonicOrder),
+ source_ambisonic_order_(GetParam()),
+ encoding_coeffs_(GetNumPeriphonicComponents(source_ambisonic_order_)) {}
+
+ // Tests whether GetEncodingCoeffs() method returns correct coefficients.
+ void TestEncodingCoefficients() {
+ for (size_t i = 0; i < kSourceDirections.size(); ++i) {
+ const std::vector<float> kExpectedCoeffs =
+ GenerateExpectedCoeffs(kSourceDirections[i]);
+ lookup_table_.GetEncodingCoeffs(source_ambisonic_order_,
+ kSourceDirections[i], kMinSpreadDeg,
+ &encoding_coeffs_);
+ for (size_t j = 0; j < encoding_coeffs_.size(); ++j) {
+ EXPECT_NEAR(kExpectedCoeffs[j], encoding_coeffs_[j], kEpsilonFloat);
+ }
+ }
+ }
+
+ // Tests whether changing the source spread results in correct gain changes in
+ // pressure and velocity ambisonic encoding coefficients.
+ void TestSpreadEnergy() {
+ // Choose an artbitrary source direction from |kSourceDirections|.
+ const SphericalAngle source_direction = kSourceDirections.back();
+ lookup_table_.GetEncodingCoeffs(source_ambisonic_order_, source_direction,
+ kMinSpreadDeg, &encoding_coeffs_);
+ float current_pressure_energy = GetPressureEnergy();
+ float current_velocity_energy = GetVelocityEnergy();
+ for (int spread_deg = 1; spread_deg <= 360; ++spread_deg) {
+ lookup_table_.GetEncodingCoeffs(source_ambisonic_order_, source_direction,
+ static_cast<float>(spread_deg),
+ &encoding_coeffs_);
+ float new_pressure_energy = GetPressureEnergy();
+ float new_velocity_energy = GetVelocityEnergy();
+ EXPECT_TRUE(new_pressure_energy >= current_pressure_energy);
+ EXPECT_TRUE(new_velocity_energy <= current_velocity_energy);
+ current_pressure_energy = new_pressure_energy;
+ current_velocity_energy = new_velocity_energy;
+ }
+ }
+
+ private:
+ // Generates expected ambisonic encoding coefficients for ambisonic orders 0
+ // to 3, according to http://ambisonics.ch/standards/channels/index.
+ std::vector<float> GenerateExpectedCoeffs(const SphericalAngle& angle) {
+ const float a = angle.azimuth();
+ const float e = angle.elevation();
+ return std::vector<float>{
+ 1.0f,
+ std::cos(e) * std::sin(a),
+ std::sin(e),
+ std::cos(e) * std::cos(a),
+ kSqrtThree / 2.0f * std::cos(e) * std::cos(e) * std::sin(2.0f * a),
+ kSqrtThree / 2.0f * std::sin(2.0f * e) * std::sin(a),
+ 0.5f * (3.0f * std::sin(e) * std::sin(e) - 1.0f),
+ kSqrtThree / 2.0f * std::sin(2.0f * e) * std::cos(a),
+ kSqrtThree / 2.0f * std::cos(e) * std::cos(e) * std::cos(2.0f * a),
+ std::sqrt(5.0f / 8.0f) * IntegerPow(std::cos(e), 3) *
+ std::sin(3.0f * a),
+ std::sqrt(15.0f) / 2.0f * std::sin(e) * std::cos(e) * std::cos(e) *
+ std::sin(2.0f * a),
+ std::sqrt(3.0f / 8.0f) * std::cos(e) *
+ (5.0f * std::sin(e) * std::sin(e) - 1.0f) * std::sin(a),
+ 0.5f * std::sin(e) * (5.0f * std::sin(e) * std::sin(e) - 3.0f),
+ std::sqrt(3.0f / 8.0f) * std::cos(e) *
+ (5.0f * std::sin(e) * std::sin(e) - 1.0f) * std::cos(a),
+ std::sqrt(15.0f) / 2.0f * std::sin(e) * std::cos(e) * std::cos(e) *
+ std::cos(2.0f * a),
+ std::sqrt(5.0f / 8.0f) * IntegerPow(std::cos(e), 3) *
+ std::cos(3.0f * a)};
+ }
+
+ // Computes energy of the pressure channel (Ambisonic channel 0).
+ float GetPressureEnergy() {
+ return encoding_coeffs_[0] * encoding_coeffs_[0];
+ }
+
+ // Computes total energy of all the velocity channels (Ambisonic channel 1 and
+ // above).
+ float GetVelocityEnergy() {
+ float velocity_energy = 0.0f;
+ for (size_t i = 1; i < encoding_coeffs_.size(); ++i) {
+ velocity_energy += encoding_coeffs_[i] * encoding_coeffs_[i];
+ }
+ return velocity_energy;
+ }
+
+ // Source angles corresponding to each Cartesian axis direction as well as
+ // some arbitrary directions (full degrees) in each quadrant of the sphere.
+ const std::vector<SphericalAngle> kSourceDirections = {
+ {SphericalAngle::FromDegrees(0.0f, 0.0f)} /* Front */,
+ {SphericalAngle::FromDegrees(90.0f, 0.0f)} /* Left */,
+ {SphericalAngle::FromDegrees(180.0f, 0.0f)} /* Back */,
+ {SphericalAngle::FromDegrees(-90.0f, 0.0f)} /* Right */,
+ {SphericalAngle::FromDegrees(0.0f, 90.0f)} /* Up */,
+ {SphericalAngle::FromDegrees(0.0f, -90.0f)} /* Down */,
+ {SphericalAngle::FromDegrees(10.0f, 20.0f)} /* Left-Top-Front */,
+ {SphericalAngle::FromDegrees(-30.0f, 40.0f)} /* Right-Top-Front */,
+ {SphericalAngle::FromDegrees(50.0f, -60.0f)} /* Left-Bottom-Front */,
+ {SphericalAngle::FromDegrees(290.0f, -80.0f)} /* Right-Bottom-Front */,
+ {SphericalAngle::FromDegrees(110.0f, 5.0f)} /* Left-Top-Back */,
+ {SphericalAngle::FromDegrees(-120.0f, 15.0f)} /* Right-Top-Back */,
+ {SphericalAngle::FromDegrees(130.0f, -25.0f)} /* Left-Bottom-Back */,
+ {SphericalAngle::FromDegrees(220.0f, -35.0f)} /* Right-Bottom-Back */,
+ };
+
+ AmbisonicLookupTable lookup_table_;
+ const int source_ambisonic_order_;
+ std::vector<float> encoding_coeffs_;
+};
+
+// Tests whether GetEncodingCoeffs() method returns correct coefficients.
+TEST_P(AmbisonicLookupTableTest, GetEncodingCoeffsTest) {
+ TestEncodingCoefficients();
+}
+
+// Tests whether changing the source spread results in correct gain changes in
+// pressure and velocity ambisonic encoding coefficients. For example,
+// increasing of the source spread should result in overall monotonic increase
+// of energy in the pressure channel and overall monotonic decrease in the
+// velocity channels.
+TEST_P(AmbisonicLookupTableTest, SpreadEnergyTest) { TestSpreadEnergy(); }
+
+INSTANTIATE_TEST_CASE_P(TestParameters, AmbisonicLookupTableTest,
+ testing::Values(kSourceAmbisonicOrder0,
+ kSourceAmbisonicOrder1,
+ kSourceAmbisonicOrder2,
+ kSourceAmbisonicOrder3));
+
+class PreComputedCoeffsTest : public ::testing::Test {
+ protected:
+ // Tests whether the lookup table returns correct coefficients for sources
+ // with arbitrary ambisonic order, direction and spread.
+ void TestSpreadCoefficients() {
+ // Test spread coefficients for each config.
+ for (const auto& config : kConfigs) {
+ const int source_ambisonic_order = config.source_ambisonic_order;
+ const SphericalAngle& source_direction = config.source_direction;
+ const float source_spread_deg = config.source_spread_deg;
+ const std::vector<float>& expected_coeffs = config.expected_coefficients;
+ std::vector<float> encoding_coeffs(
+ GetNumPeriphonicComponents(source_ambisonic_order));
+ AmbisonicLookupTable lookup_table(kMaxSupportedAmbisonicOrder);
+ lookup_table.GetEncodingCoeffs(source_ambisonic_order, source_direction,
+ source_spread_deg, &encoding_coeffs);
+ for (size_t i = 0; i < encoding_coeffs.size(); ++i) {
+ EXPECT_NEAR(expected_coeffs[i], encoding_coeffs[i], kEpsilonFloat);
+ }
+ }
+ }
+
+ private:
+ struct TestConfig {
+ int source_ambisonic_order;
+ SphericalAngle source_direction;
+ float source_spread_deg;
+ std::vector<float> expected_coefficients;
+ };
+
+ const std::vector<TestConfig> kConfigs = {
+ // Zeroth order sound source.
+ {0 /* ambisonic order */,
+ SphericalAngle::FromDegrees(0.0f, 0.0f) /* source direction */,
+ 120.0f /* source spread */,
+ {1.0f} /* expected coefficients */},
+
+ // First order sound source.
+ {1 /* ambisonic order */,
+ SphericalAngle::FromDegrees(36.0f, 45.0f) /* source direction */,
+ 70.0f /* source spread */,
+ {1.20046f, 0.310569f, 0.528372f, 0.427462f}
+ /* expected coefficients */},
+
+ // Second order sound source.
+ {2 /* ambisonic order */,
+ SphericalAngle::FromDegrees(55.0f, -66.0f) /* source direction */,
+ 41.0f /* source spread */,
+ {1.038650f, 0.337027f, -0.924096f, 0.2359899f, 0.124062f, -0.485807f,
+ 0.6928289f, -0.340166f, -0.045155f}
+ /* expected coefficients */},
+
+ // Third order sound source.
+ {3 /* ambisonic order */,
+ SphericalAngle::FromDegrees(-13.0f, 90.0f) /* source direction */,
+ 32.0f /* source spread */,
+ {1.03237f, 0.0f, 1.02119f, 0.0f, 0.0f, 0.0f, 0.990433f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.898572f, 0.0f, 0.0f, 0.0f}
+ /* expected coefficients */}};
+};
+
+// Tests whether the lookup table returns correct coefficients for sources with
+// arbitrary ambisonic order, direction and spread. The expected coefficients
+// have been pre-computed using Matlab.
+TEST_F(PreComputedCoeffsTest, SpreadCoefficientsTest) {
+ TestSpreadCoefficients();
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_spread_coefficients.h b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_spread_coefficients.h
new file mode 100644
index 000000000..0c245b87f
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/ambisonic_spread_coefficients.h
@@ -0,0 +1,610 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_AMBISONICS_AMBISONIC_SPREAD_COEFFICIENTS_H_
+#define RESONANCE_AUDIO_AMBISONICS_AMBISONIC_SPREAD_COEFFICIENTS_H_
+
+namespace vraudio {
+
+// Lookup table with gain coefficients to control Ambisonic sound source spread
+// at different orders, up to and including order 3. For information on how the
+// spread control coefficients have been derived please refer to Matlab code as
+// well as the corresponding paper.
+static const float kSpreadCoeffs[5089] = {
+ 1.0f, 1.0019f, 0.998092f, 1.01441f, 0.985357f,
+ 1.02704f, 0.972176f, 1.03975f, 0.958555f, 1.05252f,
+ 0.944499f, 1.06532f, 0.930017f, 1.07813f, 0.915119f,
+ 1.09091f, 0.899819f, 1.10365f, 0.884131f, 1.11631f,
+ 0.868073f, 1.12886f, 0.851665f, 1.14128f, 0.834929f,
+ 1.15354f, 0.817888f, 1.16561f, 0.800569f, 1.17747f,
+ 0.782999f, 1.1891f, 0.76521f, 1.20046f, 0.747231f,
+ 1.21155f, 0.729095f, 1.22234f, 0.710837f, 1.23281f,
+ 0.692489f, 1.24296f, 0.674086f, 1.25276f, 0.655664f,
+ 1.26221f, 0.637256f, 1.27131f, 0.618896f, 1.28003f,
+ 0.600619f, 1.28839f, 0.582456f, 1.29637f, 0.564439f,
+ 1.30399f, 0.546597f, 1.31123f, 0.528959f, 1.31811f,
+ 0.511551f, 1.32463f, 0.494397f, 1.3308f, 0.47752f,
+ 1.33663f, 0.46094f, 1.34212f, 0.444677f, 1.34729f,
+ 0.428746f, 1.35214f, 0.413161f, 1.3567f, 0.397935f,
+ 1.36096f, 0.383079f, 1.36495f, 0.3686f, 1.36867f,
+ 0.354505f, 1.37215f, 0.340799f, 1.37538f, 0.327486f,
+ 1.37839f, 0.314566f, 1.38118f, 0.302039f, 1.38378f,
+ 0.289906f, 1.38618f, 0.278163f, 1.38841f, 0.266808f,
+ 1.39047f, 0.255835f, 1.39238f, 0.24524f, 1.39414f,
+ 0.235017f, 1.39576f, 0.225159f, 1.39726f, 0.21566f,
+ 1.39864f, 0.206511f, 1.39991f, 0.197704f, 1.40108f,
+ 0.189232f, 1.40215f, 0.181084f, 1.40314f, 0.173254f,
+ 1.40405f, 0.165731f, 1.40488f, 0.158507f, 1.40565f,
+ 0.151572f, 1.40635f, 0.144918f, 1.40699f, 0.138535f,
+ 1.40758f, 0.132414f, 1.40812f, 0.126546f, 1.40861f,
+ 0.120923f, 1.40906f, 0.115536f, 1.40947f, 0.110376f,
+ 1.40985f, 0.105436f, 1.4102f, 0.100705f, 1.41051f,
+ 0.0961781f, 1.4108f, 0.0918459f, 1.41107f, 0.087701f,
+ 1.41131f, 0.0837362f, 1.41153f, 0.0799443f, 1.41173f,
+ 0.0763185f, 1.41191f, 0.0728519f, 1.41208f, 0.069538f,
+ 1.41223f, 0.0663707f, 1.41237f, 0.0633438f, 1.41249f,
+ 0.0604515f, 1.41261f, 0.0576881f, 1.41271f, 0.0550481f,
+ 1.41281f, 0.0525264f, 1.4129f, 0.0501178f, 1.41298f,
+ 0.0478176f, 1.41305f, 0.045621f, 1.41312f, 0.0435236f,
+ 1.41318f, 0.0415211f, 1.41323f, 0.0396092f, 1.41328f,
+ 0.0377841f, 1.41333f, 0.0360419f, 1.41337f, 0.034379f,
+ 1.4134f, 0.0327918f, 1.41344f, 0.031277f, 1.41347f,
+ 0.0298315f, 1.4135f, 0.028452f, 1.41352f, 0.0271356f,
+ 1.41355f, 0.0258796f, 1.41357f, 0.0246811f, 1.41359f,
+ 0.0235377f, 1.41361f, 0.0224468f, 1.41362f, 0.0214061f,
+ 1.41364f, 0.0204132f, 1.41365f, 0.0194661f, 1.41366f,
+ 0.0185626f, 1.41367f, 0.0177008f, 1.41368f, 0.0168787f,
+ 1.41369f, 0.0160946f, 1.4137f, 0.0153468f, 1.41371f,
+ 0.0146335f, 1.41372f, 0.0139531f, 1.41372f, 0.0133043f,
+ 1.41373f, 0.0126855f, 1.41373f, 0.0120953f, 1.41374f,
+ 0.0115325f, 1.41374f, 0.0109958f, 1.41375f, 0.0104839f,
+ 1.41375f, 0.00999584f, 1.41375f, 0.00953039f, 1.41376f,
+ 0.00908654f, 1.41376f, 0.0086633f, 1.41376f, 0.00825972f,
+ 1.41376f, 0.00787488f, 1.41376f, 0.00750793f, 1.41377f,
+ 0.00715803f, 1.41377f, 0.0068244f, 1.41377f, 0.00650628f,
+ 1.41377f, 0.00620296f, 1.41377f, 0.00591376f, 1.41377f,
+ 0.00563801f, 1.41377f, 0.00537509f, 1.41378f, 0.00512441f,
+ 1.41378f, 0.0048854f, 1.41378f, 0.00465752f, 1.41378f,
+ 0.00444026f, 1.41378f, 0.00423311f, 1.41378f, 0.00403562f,
+ 1.41378f, 0.00384732f, 1.41378f, 0.0036678f, 1.41378f,
+ 0.00349665f, 1.41378f, 0.00333348f, 1.41378f, 0.00317791f,
+ 1.41378f, 0.00302959f, 1.41378f, 0.00288819f, 1.41378f,
+ 0.00275338f, 1.41378f, 0.00262486f, 1.41378f, 0.00250233f,
+ 1.41378f, 0.00238552f, 1.41378f, 0.00227415f, 1.41378f,
+ 0.00216798f, 1.41378f, 0.00206677f, 1.41378f, 0.00197027f,
+ 1.41378f, 0.00187828f, 1.41378f, 0.00179058f, 1.41378f,
+ 0.00170697f, 1.41378f, 0.00162727f, 1.41378f, 0.00155128f,
+ 1.41378f, 0.00147884f, 1.41378f, 0.00140979f, 1.41378f,
+ 0.00134395f, 1.41378f, 0.00128119f, 1.41378f, 0.00122136f,
+ 1.41378f, 0.00116432f, 1.41378f, 0.00110994f, 1.41378f,
+ 0.0010581f, 1.41378f, 0.00100869f, 1.41378f, 0.000961575f,
+ 1.41378f, 0.000916665f, 1.41378f, 0.000873851f, 1.41378f,
+ 0.000833036f, 1.41378f, 0.000794127f, 1.41378f, 0.000757035f,
+ 1.41378f, 0.000721675f, 1.41378f, 0.000687966f, 1.41378f,
+ 0.000655831f, 1.41378f, 0.000625197f, 1.41378f, 0.000595994f,
+ 1.41378f, 0.000568154f, 1.41378f, 0.000541615f, 1.41378f,
+ 0.000516315f, 1.41378f, 0.000492197f, 1.41378f, 0.000469205f,
+ 1.41378f, 0.000447287f, 1.41378f, 0.000426393f, 1.41378f,
+ 0.000406474f, 1.41378f, 0.000387486f, 1.41378f, 0.000369385f,
+ 1.41378f, 0.000352129f, 1.41378f, 0.00033568f, 1.41378f,
+ 0.000319998f, 1.41378f, 0.00030505f, 1.41378f, 0.000290799f,
+ 1.41378f, 0.000277214f, 1.41378f, 0.000264264f, 1.41378f,
+ 0.000251918f, 1.41378f, 0.00024015f, 1.41378f, 0.000228931f,
+ 1.41378f, 0.000218236f, 1.41378f, 0.000208041f, 1.41378f,
+ 0.000198322f, 1.41378f, 0.000189056f, 1.41378f, 0.000180224f,
+ 1.41378f, 0.000171805f, 1.41378f, 0.000163778f, 1.41378f,
+ 0.000156127f, 1.41378f, 0.000148833f, 1.41378f, 0.00014188f,
+ 1.41378f, 0.000135252f, 1.41378f, 0.000128933f, 1.41378f,
+ 0.000122909f, 1.41378f, 0.000117167f, 1.41378f, 0.000111693f,
+ 1.41378f, 0.000106475f, 1.41378f, 0.000101501f, 1.41378f,
+ 9.67588e-05f, 1.41378f, 9.22384e-05f, 1.41378f, 8.79291e-05f,
+ 1.41378f, 8.38211e-05f, 1.41378f, 7.99051e-05f, 1.41378f,
+ 7.6172e-05f, 1.41378f, 7.26133e-05f, 1.41378f, 6.92209e-05f,
+ 1.41378f, 6.5987e-05f, 1.41378f, 6.29041e-05f, 1.41378f,
+ 5.99653e-05f, 1.41378f, 5.71637e-05f, 1.41378f, 5.44931e-05f,
+ 1.41378f, 5.19472e-05f, 1.41378f, 4.95202e-05f, 1.41378f,
+ 4.72067e-05f, 1.41378f, 4.50012e-05f, 1.41378f, 4.28988e-05f,
+ 1.41378f, 4.08946e-05f, 1.41378f, 3.8984e-05f, 1.41378f,
+ 3.71627e-05f, 1.41378f, 3.54264e-05f, 1.41378f, 3.37713e-05f,
+ 1.41378f, 3.21935e-05f, 1.41378f, 3.06895e-05f, 1.41378f,
+ 2.92557e-05f, 1.41378f, 2.78888e-05f, 1.41378f, 2.65859e-05f,
+ 1.41378f, 2.53438e-05f, 1.41378f, 2.41597e-05f, 1.41378f,
+ 2.3031e-05f, 1.41378f, 2.1955e-05f, 1.41378f, 2.09292e-05f,
+ 1.41378f, 1.99514e-05f, 1.41378f, 1.90193e-05f, 1.41378f,
+ 1.81307e-05f, 1.41378f, 1.72837e-05f, 1.41378f, 1.64762e-05f,
+ 1.41378f, 1.57064e-05f, 1.41378f, 1.49726e-05f, 1.41378f,
+ 1.42731e-05f, 1.41378f, 1.36062e-05f, 1.41378f, 1.29706e-05f,
+ 1.41378f, 1.23646e-05f, 1.41378f, 1.17869e-05f, 1.41378f,
+ 1.12362e-05f, 1.41378f, 1.07113e-05f, 1.41378f, 1.02108e-05f,
+ 1.41378f, 9.73377e-06f, 1.41378f, 9.27901e-06f, 1.41378f,
+ 8.84549e-06f, 1.41378f, 8.43223e-06f, 1.41378f, 8.03827e-06f,
+ 1.41378f, 7.66273e-06f, 1.41378f, 7.30472e-06f, 1.41378f,
+ 6.96344e-06f, 1.41378f, 6.63811e-06f, 1.41378f, 6.32798e-06f,
+ 1.41378f, 6.03233e-06f, 1.41378f, 5.7505e-06f, 1.41378f,
+ 5.48184e-06f, 1.41378f, 5.22572e-06f, 1.41378f, 4.98158e-06f,
+ 1.41378f, 4.74884e-06f, 1.41378f, 4.52697e-06f, 1.41378f,
+ 4.31547e-06f, 1.41378f, 4.11385e-06f, 1.41378f, 3.92165e-06f,
+ 1.41378f, 3.73843e-06f, 1.41378f, 3.56377e-06f, 1.41378f,
+ 3.39727e-06f, 1.41378f, 3.23855e-06f, 1.41378f, 3.08724e-06f,
+ 1.41378f, 2.94301e-06f, 1.41378f, 2.80551e-06f, 1.41378f,
+ 2.67443e-06f, 1.41378f, 2.54948e-06f, 1.41378f, 2.43037e-06f,
+ 1.41378f, 2.31682e-06f, 1.41378f, 2.20858e-06f, 1.41378f,
+ 2.1054e-06f, 1.41378f, 2.00703e-06f, 1.41378f, 1.91326e-06f,
+ 1.41378f, 1.82388e-06f, 1.41378f, 1.73866e-06f, 1.41378f,
+ 1.65743e-06f, 1.41378f, 1.58e-06f, 1.41378f, 1.50618e-06f,
+ 1.41378f, 1.43581e-06f, 1.41378f, 1.36873e-06f, 1.41378f,
+ 1.30478e-06f, 1.41378f, 1.24382e-06f, 1.41378f, 1.18571e-06f,
+ 1.01483f, 1.00465f, 0.970383f, 1.03865f, 1.01155f,
+ 0.9215f, 1.06314f, 1.0179f, 0.869372f, 1.08819f,
+ 1.02356f, 0.813901f, 1.11364f, 1.02839f, 0.755004f,
+ 1.13935f, 1.03225f, 0.692625f, 1.16513f, 1.03497f,
+ 0.626735f, 1.19076f, 1.0364f, 0.557346f, 1.21602f,
+ 1.03638f, 0.484517f, 1.24065f, 1.03474f, 0.408362f,
+ 1.2644f, 1.03133f, 0.329059f, 1.28697f, 1.02601f,
+ 0.246853f, 1.3081f, 1.01866f, 0.162061f, 1.3275f,
+ 1.00918f, 0.0750718f, 1.34495f, 0.99753f, 0.0f,
+ 1.36175f, 0.984802f, 0.0f, 1.37869f, 0.971629f,
+ 0.0f, 1.39575f, 0.958015f, 0.0f, 1.4129f,
+ 0.943967f, 0.0f, 1.43008f, 0.929493f, 0.0f,
+ 1.44728f, 0.914603f, 0.0f, 1.46444f, 0.899312f,
+ 0.0f, 1.48154f, 0.883633f, 0.0f, 1.49853f,
+ 0.867584f, 0.0f, 1.51538f, 0.851185f, 0.0f,
+ 1.53206f, 0.834458f, 0.0f, 1.54851f, 0.817427f,
+ 0.0f, 1.56472f, 0.800117f, 0.0f, 1.58064f,
+ 0.782558f, 0.0f, 1.59624f, 0.764779f, 0.0f,
+ 1.6115f, 0.74681f, 0.0f, 1.62638f, 0.728685f,
+ 0.0f, 1.64086f, 0.710436f, 0.0f, 1.65492f,
+ 0.692098f, 0.0f, 1.66854f, 0.673706f, 0.0f,
+ 1.6817f, 0.655294f, 0.0f, 1.69439f, 0.636896f,
+ 0.0f, 1.7066f, 0.618548f, 0.0f, 1.71831f,
+ 0.600281f, 0.0f, 1.72953f, 0.582128f, 0.0f,
+ 1.74025f, 0.564121f, 0.0f, 1.75047f, 0.546289f,
+ 0.0f, 1.76019f, 0.528661f, 0.0f, 1.76943f,
+ 0.511262f, 0.0f, 1.77819f, 0.494118f, 0.0f,
+ 1.78647f, 0.477251f, 0.0f, 1.79429f, 0.46068f,
+ 0.0f, 1.80166f, 0.444426f, 0.0f, 1.8086f,
+ 0.428504f, 0.0f, 1.81511f, 0.412928f, 0.0f,
+ 1.82123f, 0.397711f, 0.0f, 1.82695f, 0.382863f,
+ 0.0f, 1.8323f, 0.368392f, 0.0f, 1.8373f,
+ 0.354306f, 0.0f, 1.84196f, 0.340607f, 0.0f,
+ 1.84631f, 0.327301f, 0.0f, 1.85035f, 0.314388f,
+ 0.0f, 1.8541f, 0.301869f, 0.0f, 1.85758f,
+ 0.289743f, 0.0f, 1.86081f, 0.278006f, 0.0f,
+ 1.8638f, 0.266657f, 0.0f, 1.86657f, 0.255691f,
+ 0.0f, 1.86912f, 0.245102f, 0.0f, 1.87149f,
+ 0.234885f, 0.0f, 1.87367f, 0.225033f, 0.0f,
+ 1.87568f, 0.215538f, 0.0f, 1.87753f, 0.206394f,
+ 0.0f, 1.87924f, 0.197593f, 0.0f, 1.8808f,
+ 0.189125f, 0.0f, 1.88225f, 0.180982f, 0.0f,
+ 1.88357f, 0.173156f, 0.0f, 1.88479f, 0.165638f,
+ 0.0f, 1.88591f, 0.158418f, 0.0f, 1.88694f,
+ 0.151487f, 0.0f, 1.88788f, 0.144836f, 0.0f,
+ 1.88874f, 0.138457f, 0.0f, 1.88953f, 0.132339f,
+ 0.0f, 1.89025f, 0.126475f, 0.0f, 1.89091f,
+ 0.120855f, 0.0f, 1.89152f, 0.115471f, 0.0f,
+ 1.89207f, 0.110314f, 0.0f, 1.89258f, 0.105376f,
+ 0.0f, 1.89305f, 0.100649f, 0.0f, 1.89347f,
+ 0.0961239f, 0.0f, 1.89386f, 0.0917941f, 0.0f,
+ 1.89421f, 0.0876516f, 0.0f, 1.89453f, 0.083689f,
+ 0.0f, 1.89483f, 0.0798993f, 0.0f, 1.8951f,
+ 0.0762755f, 0.0f, 1.89534f, 0.0728108f, 0.0f,
+ 1.89557f, 0.0694989f, 0.0f, 1.89577f, 0.0663333f,
+ 0.0f, 1.89596f, 0.0633081f, 0.0f, 1.89613f,
+ 0.0604174f, 0.0f, 1.89628f, 0.0576555f, 0.0f,
+ 1.89642f, 0.0550171f, 0.0f, 1.89655f, 0.0524968f,
+ 0.0f, 1.89667f, 0.0500896f, 0.0f, 1.89678f,
+ 0.0477907f, 0.0f, 1.89687f, 0.0455953f, 0.0f,
+ 1.89696f, 0.0434991f, 0.0f, 1.89704f, 0.0414977f,
+ 0.0f, 1.89712f, 0.0395869f, 0.0f, 1.89718f,
+ 0.0377628f, 0.0f, 1.89724f, 0.0360216f, 0.0f,
+ 1.8973f, 0.0343596f, 0.0f, 1.89735f, 0.0327733f,
+ 0.0f, 1.8974f, 0.0312594f, 0.0f, 1.89744f,
+ 0.0298146f, 0.0f, 1.89748f, 0.0284359f, 0.0f,
+ 1.89751f, 0.0271203f, 0.0f, 1.89754f, 0.025865f,
+ 0.0f, 1.89757f, 0.0246672f, 0.0f, 1.8976f,
+ 0.0235244f, 0.0f, 1.89762f, 0.0224342f, 0.0f,
+ 1.89764f, 0.021394f, 0.0f, 1.89766f, 0.0204017f,
+ 0.0f, 1.89768f, 0.0194551f, 0.0f, 1.8977f,
+ 0.0185521f, 0.0f, 1.89771f, 0.0176908f, 0.0f,
+ 1.89773f, 0.0168692f, 0.0f, 1.89774f, 0.0160856f,
+ 0.0f, 1.89775f, 0.0153381f, 0.0f, 1.89776f,
+ 0.0146252f, 0.0f, 1.89777f, 0.0139453f, 0.0f,
+ 1.89778f, 0.0132968f, 0.0f, 1.89778f, 0.0126783f,
+ 0.0f, 1.89779f, 0.0120885f, 0.0f, 1.8978f,
+ 0.011526f, 0.0f, 1.8978f, 0.0109896f, 0.0f,
+ 1.89781f, 0.010478f, 0.0f, 1.89781f, 0.00999021f,
+ 0.0f, 1.89782f, 0.00952502f, 0.0f, 1.89782f,
+ 0.00908142f, 0.0f, 1.89782f, 0.00865842f, 0.0f,
+ 1.89783f, 0.00825506f, 0.0f, 1.89783f, 0.00787044f,
+ 0.0f, 1.89783f, 0.00750369f, 0.0f, 1.89784f,
+ 0.00715399f, 0.0f, 1.89784f, 0.00682055f, 0.0f,
+ 1.89784f, 0.00650262f, 0.0f, 1.89784f, 0.00619947f,
+ 0.0f, 1.89784f, 0.00591042f, 0.0f, 1.89785f,
+ 0.00563483f, 0.0f, 1.89785f, 0.00537206f, 0.0f,
+ 1.89785f, 0.00512152f, 0.0f, 1.89785f, 0.00488265f,
+ 0.0f, 1.89785f, 0.0046549f, 0.0f, 1.89785f,
+ 0.00443776f, 0.0f, 1.89785f, 0.00423073f, 0.0f,
+ 1.89785f, 0.00403334f, 0.0f, 1.89785f, 0.00384516f,
+ 0.0f, 1.89785f, 0.00366574f, 0.0f, 1.89785f,
+ 0.00349468f, 0.0f, 1.89786f, 0.0033316f, 0.0f,
+ 1.89786f, 0.00317612f, 0.0f, 1.89786f, 0.00302788f,
+ 0.0f, 1.89786f, 0.00288656f, 0.0f, 1.89786f,
+ 0.00275183f, 0.0f, 1.89786f, 0.00262338f, 0.0f,
+ 1.89786f, 0.00250092f, 0.0f, 1.89786f, 0.00238417f,
+ 0.0f, 1.89786f, 0.00227287f, 0.0f, 1.89786f,
+ 0.00216676f, 0.0f, 1.89786f, 0.0020656f, 0.0f,
+ 1.89786f, 0.00196916f, 0.0f, 1.89786f, 0.00187722f,
+ 0.0f, 1.89786f, 0.00178957f, 0.0f, 1.89786f,
+ 0.00170601f, 0.0f, 1.89786f, 0.00162635f, 0.0f,
+ 1.89786f, 0.00155041f, 0.0f, 1.89786f, 0.00147801f,
+ 0.0f, 1.89786f, 0.00140899f, 0.0f, 1.89786f,
+ 0.00134319f, 0.0f, 1.89786f, 0.00128047f, 0.0f,
+ 1.89786f, 0.00122067f, 0.0f, 1.89786f, 0.00116366f,
+ 0.0f, 1.89786f, 0.00110932f, 0.0f, 1.89786f,
+ 0.00105751f, 0.0f, 1.89786f, 0.00100812f, 0.0f,
+ 1.89786f, 0.000961033f, 0.0f, 1.89786f, 0.000916148f,
+ 0.0f, 1.89786f, 0.000873358f, 0.0f, 1.89786f,
+ 0.000832566f, 0.0f, 1.89786f, 0.000793679f, 0.0f,
+ 1.89786f, 0.000756608f, 0.0f, 1.89786f, 0.000721268f,
+ 0.0f, 1.89786f, 0.000687578f, 0.0f, 1.89786f,
+ 0.000655462f, 0.0f, 1.89786f, 0.000624845f, 0.0f,
+ 1.89786f, 0.000595658f, 0.0f, 1.89786f, 0.000567834f,
+ 0.0f, 1.89786f, 0.00054131f, 0.0f, 1.89786f,
+ 0.000516024f, 0.0f, 1.89786f, 0.000491919f, 0.0f,
+ 1.89786f, 0.000468941f, 0.0f, 1.89786f, 0.000447035f,
+ 0.0f, 1.89786f, 0.000426152f, 0.0f, 1.89786f,
+ 0.000406245f, 0.0f, 1.89786f, 0.000387268f, 0.0f,
+ 1.89786f, 0.000369177f, 0.0f, 1.89786f, 0.000351931f,
+ 0.0f, 1.89786f, 0.000335491f, 0.0f, 1.89786f,
+ 0.000319818f, 0.0f, 1.89786f, 0.000304878f, 0.0f,
+ 1.89786f, 0.000290635f, 0.0f, 1.89786f, 0.000277058f,
+ 0.0f, 1.89786f, 0.000264115f, 0.0f, 1.89786f,
+ 0.000251776f, 0.0f, 1.89786f, 0.000240014f, 0.0f,
+ 1.89786f, 0.000228802f, 0.0f, 1.89786f, 0.000218113f,
+ 0.0f, 1.89786f, 0.000207923f, 0.0f, 1.89786f,
+ 0.00019821f, 0.0f, 1.89786f, 0.00018895f, 0.0f,
+ 1.89786f, 0.000180123f, 0.0f, 1.89786f, 0.000171708f,
+ 0.0f, 1.89786f, 0.000163686f, 0.0f, 1.89786f,
+ 0.000156039f, 0.0f, 1.89786f, 0.000148749f, 0.0f,
+ 1.89786f, 0.0001418f, 0.0f, 1.89786f, 0.000135175f,
+ 0.0f, 1.89786f, 0.00012886f, 0.0f, 1.89786f,
+ 0.00012284f, 0.0f, 1.89786f, 0.000117101f, 0.0f,
+ 1.89786f, 0.00011163f, 0.0f, 1.89786f, 0.000106415f,
+ 0.0f, 1.89786f, 0.000101444f, 0.0f, 1.89786f,
+ 9.67043e-05f, 0.0f, 1.89786f, 9.21864e-05f, 0.0f,
+ 1.89786f, 8.78795e-05f, 0.0f, 1.89786f, 8.37739e-05f,
+ 0.0f, 1.89786f, 7.98601e-05f, 0.0f, 1.89786f,
+ 7.61291e-05f, 0.0f, 1.89786f, 7.25724e-05f, 0.0f,
+ 1.89786f, 6.91819e-05f, 0.0f, 1.89786f, 6.59498e-05f,
+ 0.0f, 1.89786f, 6.28686e-05f, 0.0f, 1.89786f,
+ 5.99315e-05f, 0.0f, 1.89786f, 5.71315e-05f, 0.0f,
+ 1.89786f, 5.44624e-05f, 0.0f, 1.89786f, 5.19179e-05f,
+ 0.0f, 1.89786f, 4.94923e-05f, 0.0f, 1.89786f,
+ 4.71801e-05f, 0.0f, 1.89786f, 4.49758e-05f, 0.0f,
+ 1.89786f, 4.28746e-05f, 0.0f, 1.89786f, 4.08715e-05f,
+ 0.0f, 1.89786f, 3.8962e-05f, 0.0f, 1.89786f,
+ 3.71417e-05f, 0.0f, 1.89786f, 3.54065e-05f, 0.0f,
+ 1.89786f, 3.37523e-05f, 0.0f, 1.89786f, 3.21754e-05f,
+ 0.0f, 1.89786f, 3.06722e-05f, 0.0f, 1.89786f,
+ 2.92392e-05f, 0.0f, 1.89786f, 2.78731e-05f, 0.0f,
+ 1.89786f, 2.65709e-05f, 0.0f, 1.89786f, 2.53295e-05f,
+ 0.0f, 1.89786f, 2.41461e-05f, 0.0f, 1.89786f,
+ 2.3018e-05f, 0.0f, 1.89786f, 2.19426e-05f, 0.0f,
+ 1.89786f, 2.09175e-05f, 0.0f, 1.89786f, 1.99402e-05f,
+ 0.0f, 1.89786f, 1.90086e-05f, 0.0f, 1.89786f,
+ 1.81205e-05f, 0.0f, 1.89786f, 1.72739e-05f, 0.0f,
+ 1.89786f, 1.64669e-05f, 0.0f, 1.89786f, 1.56975e-05f,
+ 0.0f, 1.89786f, 1.49642e-05f, 0.0f, 1.89786f,
+ 1.4265e-05f, 0.0f, 1.89786f, 1.35986e-05f, 0.0f,
+ 1.89786f, 1.29632e-05f, 0.0f, 1.89786f, 1.23576e-05f,
+ 0.0f, 1.89786f, 1.17802e-05f, 0.0f, 1.89786f,
+ 1.12299e-05f, 0.0f, 1.89786f, 1.07052e-05f, 0.0f,
+ 1.89786f, 1.02051e-05f, 0.0f, 1.89786f, 9.72828e-06f,
+ 0.0f, 1.89786f, 9.27378e-06f, 0.0f, 1.89786f,
+ 8.84051e-06f, 0.0f, 1.89786f, 8.42748e-06f, 0.0f,
+ 1.89786f, 8.03374e-06f, 0.0f, 1.89786f, 7.65841e-06f,
+ 0.0f, 1.89786f, 7.3006e-06f, 0.0f, 1.89786f,
+ 6.95952e-06f, 0.0f, 1.89786f, 6.63437e-06f, 0.0f,
+ 1.89786f, 6.32441e-06f, 0.0f, 1.89786f, 6.02893e-06f,
+ 0.0f, 1.89786f, 5.74726e-06f, 0.0f, 1.89786f,
+ 5.47875e-06f, 0.0f, 1.89786f, 5.22278e-06f, 0.0f,
+ 1.89786f, 4.97877e-06f, 0.0f, 1.89786f, 4.74616e-06f,
+ 0.0f, 1.89786f, 4.52442e-06f, 0.0f, 1.89786f,
+ 4.31304e-06f, 0.0f, 1.89786f, 4.11153e-06f, 0.0f,
+ 1.89786f, 3.91944e-06f, 0.0f, 1.89786f, 3.73632e-06f,
+ 0.0f, 1.89786f, 3.56176e-06f, 0.0f, 1.89786f,
+ 3.39536e-06f, 0.0f, 1.89786f, 3.23672e-06f, 0.0f,
+ 1.89786f, 3.0855e-06f, 0.0f, 1.89786f, 2.94135e-06f,
+ 0.0f, 1.89786f, 2.80393e-06f, 0.0f, 1.89786f,
+ 2.67293e-06f, 0.0f, 1.89786f, 2.54805e-06f, 0.0f,
+ 1.89786f, 2.429e-06f, 0.0f, 1.89786f, 2.31552e-06f,
+ 0.0f, 1.89786f, 2.20734e-06f, 0.0f, 1.89786f,
+ 2.10421e-06f, 0.0f, 1.89786f, 2.0059e-06f, 0.0f,
+ 1.89786f, 1.91218e-06f, 0.0f, 1.89786f, 1.82285e-06f,
+ 0.0f, 1.89786f, 1.73768e-06f, 0.0f, 1.89786f,
+ 1.6565e-06f, 0.0f, 1.89786f, 1.57911e-06f, 0.0f,
+ 1.89786f, 1.50533e-06f, 0.0f, 1.89786f, 1.435e-06f,
+ 0.0f, 1.89786f, 1.36796e-06f, 0.0f, 1.89786f,
+ 1.30405e-06f, 0.0f, 1.89786f, 1.24312e-06f, 0.0f,
+ 1.89786f, 1.18504e-06f, 0.0f, 1.00324f, 1.00216f,
+ 0.999152f, 0.990038f, 1.03237f, 1.02119f, 0.990433f,
+ 0.898572f, 1.06269f, 1.04023f, 0.979161f, 0.799806f,
+ 1.094f, 1.05895f, 0.964976f, 0.693603f, 1.126f,
+ 1.07701f, 0.947526f, 0.57989f, 1.15835f, 1.09398f,
+ 0.926474f, 0.45869f, 1.19059f, 1.10944f, 0.901512f,
+ 0.330158f, 1.22223f, 1.12289f, 0.87237f, 0.194621f,
+ 1.25268f, 1.13384f, 0.838839f, 0.0526136f, 1.28199f,
+ 1.14236f, 0.801199f, 0.0f, 1.31207f, 1.15021f,
+ 0.760839f, 0.0f, 1.34301f, 1.15742f, 0.717799f,
+ 0.0f, 1.37465f, 1.16386f, 0.671999f, 0.0f,
+ 1.40681f, 1.16935f, 0.623371f, 0.0f, 1.43929f,
+ 1.17374f, 0.571868f, 0.0f, 1.47185f, 1.17684f,
+ 0.517465f, 0.0f, 1.50423f, 1.17846f, 0.460174f,
+ 0.0f, 1.53613f, 1.17844f, 0.400043f, 0.0f,
+ 1.56725f, 1.17657f, 0.337165f, 0.0f, 1.59725f,
+ 1.1727f, 0.271688f, 0.0f, 1.62577f, 1.16665f,
+ 0.203815f, 0.0f, 1.65245f, 1.15828f, 0.133806f,
+ 0.0f, 1.67697f, 1.14751f, 0.0619832f, 0.0f,
+ 1.69901f, 1.13426f, 0.0f, 0.0f, 1.72022f,
+ 1.11979f, 0.0f, 0.0f, 1.74163f, 1.10481f,
+ 0.0f, 0.0f, 1.76318f, 1.08933f, 0.0f,
+ 0.0f, 1.78484f, 1.07336f, 0.0f, 0.0f,
+ 1.80655f, 1.0569f, 0.0f, 0.0f, 1.82827f,
+ 1.03997f, 0.0f, 0.0f, 1.84995f, 1.02258f,
+ 0.0f, 0.0f, 1.87155f, 1.00475f, 0.0f,
+ 0.0f, 1.89302f, 0.986504f, 0.0f, 0.0f,
+ 1.91431f, 0.967857f, 0.0f, 0.0f, 1.93537f,
+ 0.948837f, 0.0f, 0.0f, 1.95615f, 0.929471f,
+ 0.0f, 0.0f, 1.97662f, 0.90979f, 0.0f,
+ 0.0f, 1.99674f, 0.889823f, 0.0f, 0.0f,
+ 2.01645f, 0.869607f, 0.0f, 0.0f, 2.03572f,
+ 0.849175f, 0.0f, 0.0f, 2.05452f, 0.828565f,
+ 0.0f, 0.0f, 2.07282f, 0.807816f, 0.0f,
+ 0.0f, 2.09058f, 0.786964f, 0.0f, 0.0f,
+ 2.10779f, 0.766051f, 0.0f, 0.0f, 2.12441f,
+ 0.745115f, 0.0f, 0.0f, 2.14044f, 0.724196f,
+ 0.0f, 0.0f, 2.15586f, 0.703332f, 0.0f,
+ 0.0f, 2.17065f, 0.682561f, 0.0f, 0.0f,
+ 2.18482f, 0.661921f, 0.0f, 0.0f, 2.19836f,
+ 0.641445f, 0.0f, 0.0f, 2.21128f, 0.621169f,
+ 0.0f, 0.0f, 2.22356f, 0.601125f, 0.0f,
+ 0.0f, 2.23523f, 0.581341f, 0.0f, 0.0f,
+ 2.24629f, 0.561847f, 0.0f, 0.0f, 2.25675f,
+ 0.542667f, 0.0f, 0.0f, 2.26663f, 0.523826f,
+ 0.0f, 0.0f, 2.27594f, 0.505344f, 0.0f,
+ 0.0f, 2.28471f, 0.487239f, 0.0f, 0.0f,
+ 2.29294f, 0.469528f, 0.0f, 0.0f, 2.30066f,
+ 0.452225f, 0.0f, 0.0f, 2.30789f, 0.435342f,
+ 0.0f, 0.0f, 2.31465f, 0.418888f, 0.0f,
+ 0.0f, 2.32097f, 0.40287f, 0.0f, 0.0f,
+ 2.32686f, 0.387294f, 0.0f, 0.0f, 2.33234f,
+ 0.372164f, 0.0f, 0.0f, 2.33745f, 0.357481f,
+ 0.0f, 0.0f, 2.34219f, 0.343246f, 0.0f,
+ 0.0f, 2.34659f, 0.329458f, 0.0f, 0.0f,
+ 2.35066f, 0.316113f, 0.0f, 0.0f, 2.35444f,
+ 0.303208f, 0.0f, 0.0f, 2.35794f, 0.290738f,
+ 0.0f, 0.0f, 2.36117f, 0.278698f, 0.0f,
+ 0.0f, 2.36415f, 0.26708f, 0.0f, 0.0f,
+ 2.36691f, 0.255878f, 0.0f, 0.0f, 2.36945f,
+ 0.245082f, 0.0f, 0.0f, 2.37179f, 0.234685f,
+ 0.0f, 0.0f, 2.37394f, 0.224677f, 0.0f,
+ 0.0f, 2.37592f, 0.215048f, 0.0f, 0.0f,
+ 2.37775f, 0.20579f, 0.0f, 0.0f, 2.37942f,
+ 0.196891f, 0.0f, 0.0f, 2.38096f, 0.188342f,
+ 0.0f, 0.0f, 2.38237f, 0.180132f, 0.0f,
+ 0.0f, 2.38367f, 0.172251f, 0.0f, 0.0f,
+ 2.38486f, 0.164689f, 0.0f, 0.0f, 2.38595f,
+ 0.157435f, 0.0f, 0.0f, 2.38694f, 0.150479f,
+ 0.0f, 0.0f, 2.38786f, 0.143811f, 0.0f,
+ 0.0f, 2.38869f, 0.137421f, 0.0f, 0.0f,
+ 2.38946f, 0.131299f, 0.0f, 0.0f, 2.39016f,
+ 0.125435f, 0.0f, 0.0f, 2.3908f, 0.11982f,
+ 0.0f, 0.0f, 2.39139f, 0.114445f, 0.0f,
+ 0.0f, 2.39192f, 0.1093f, 0.0f, 0.0f,
+ 2.39241f, 0.104376f, 0.0f, 0.0f, 2.39286f,
+ 0.099666f, 0.0f, 0.0f, 2.39327f, 0.0951603f,
+ 0.0f, 0.0f, 2.39364f, 0.0908511f, 0.0f,
+ 0.0f, 2.39398f, 0.0867305f, 0.0f, 0.0f,
+ 2.39429f, 0.082791f, 0.0f, 0.0f, 2.39457f,
+ 0.0790251f, 0.0f, 0.0f, 2.39483f, 0.0754256f,
+ 0.0f, 0.0f, 2.39506f, 0.0719858f, 0.0f,
+ 0.0f, 2.39528f, 0.0686988f, 0.0f, 0.0f,
+ 2.39547f, 0.0655584f, 0.0f, 0.0f, 2.39565f,
+ 0.0625583f, 0.0f, 0.0f, 2.39582f, 0.0596925f,
+ 0.0f, 0.0f, 2.39596f, 0.0569554f, 0.0f,
+ 0.0f, 2.3961f, 0.0543413f, 0.0f, 0.0f,
+ 2.39622f, 0.0518451f, 0.0f, 0.0f, 2.39633f,
+ 0.0494615f, 0.0f, 0.0f, 2.39644f, 0.0471857f,
+ 0.0f, 0.0f, 2.39653f, 0.0450131f, 0.0f,
+ 0.0f, 2.39661f, 0.0429389f, 0.0f, 0.0f,
+ 2.39669f, 0.0409591f, 0.0f, 0.0f, 2.39676f,
+ 0.0390693f, 0.0f, 0.0f, 2.39682f, 0.0372656f,
+ 0.0f, 0.0f, 2.39688f, 0.0355441f, 0.0f,
+ 0.0f, 2.39694f, 0.0339013f, 0.0f, 0.0f,
+ 2.39698f, 0.0323336f, 0.0f, 0.0f, 2.39703f,
+ 0.0308377f, 0.0f, 0.0f, 2.39707f, 0.0294103f,
+ 0.0f, 0.0f, 2.3971f, 0.0280484f, 0.0f,
+ 0.0f, 2.39714f, 0.0267489f, 0.0f, 0.0f,
+ 2.39717f, 0.0255092f, 0.0f, 0.0f, 2.39719f,
+ 0.0243265f, 0.0f, 0.0f, 2.39722f, 0.0231982f,
+ 0.0f, 0.0f, 2.39724f, 0.0221218f, 0.0f,
+ 0.0f, 2.39726f, 0.0210951f, 0.0f, 0.0f,
+ 2.39728f, 0.0201157f, 0.0f, 0.0f, 2.3973f,
+ 0.0191815f, 0.0f, 0.0f, 2.39731f, 0.0182904f,
+ 0.0f, 0.0f, 2.39733f, 0.0174405f, 0.0f,
+ 0.0f, 2.39734f, 0.0166299f, 0.0f, 0.0f,
+ 2.39735f, 0.0158567f, 0.0f, 0.0f, 2.39736f,
+ 0.0151194f, 0.0f, 0.0f, 2.39737f, 0.0144161f,
+ 0.0f, 0.0f, 2.39738f, 0.0137455f, 0.0f,
+ 0.0f, 2.39739f, 0.0131059f, 0.0f, 0.0f,
+ 2.3974f, 0.0124959f, 0.0f, 0.0f, 2.3974f,
+ 0.0119143f, 0.0f, 0.0f, 2.39741f, 0.0113596f,
+ 0.0f, 0.0f, 2.39741f, 0.0108306f, 0.0f,
+ 0.0f, 2.39742f, 0.0103262f, 0.0f, 0.0f,
+ 2.39742f, 0.00984523f, 0.0f, 0.0f, 2.39743f,
+ 0.00938658f, 0.0f, 0.0f, 2.39743f, 0.00894924f,
+ 0.0f, 0.0f, 2.39744f, 0.00853223f, 0.0f,
+ 0.0f, 2.39744f, 0.00813459f, 0.0f, 0.0f,
+ 2.39744f, 0.00775545f, 0.0f, 0.0f, 2.39744f,
+ 0.00739393f, 0.0f, 0.0f, 2.39745f, 0.00704923f,
+ 0.0f, 0.0f, 2.39745f, 0.00672056f, 0.0f,
+ 0.0f, 2.39745f, 0.00640719f, 0.0f, 0.0f,
+ 2.39745f, 0.00610841f, 0.0f, 0.0f, 2.39745f,
+ 0.00582353f, 0.0f, 0.0f, 2.39745f, 0.00555191f,
+ 0.0f, 0.0f, 2.39746f, 0.00529295f, 0.0f,
+ 0.0f, 2.39746f, 0.00504604f, 0.0f, 0.0f,
+ 2.39746f, 0.00481063f, 0.0f, 0.0f, 2.39746f,
+ 0.00458619f, 0.0f, 0.0f, 2.39746f, 0.00437221f,
+ 0.0f, 0.0f, 2.39746f, 0.0041682f, 0.0f,
+ 0.0f, 2.39746f, 0.0039737f, 0.0f, 0.0f,
+ 2.39746f, 0.00378826f, 0.0f, 0.0f, 2.39746f,
+ 0.00361147f, 0.0f, 0.0f, 2.39746f, 0.00344291f,
+ 0.0f, 0.0f, 2.39746f, 0.00328222f, 0.0f,
+ 0.0f, 2.39746f, 0.00312902f, 0.0f, 0.0f,
+ 2.39746f, 0.00298297f, 0.0f, 0.0f, 2.39747f,
+ 0.00284372f, 0.0f, 0.0f, 2.39747f, 0.00271097f,
+ 0.0f, 0.0f, 2.39747f, 0.00258441f, 0.0f,
+ 0.0f, 2.39747f, 0.00246376f, 0.0f, 0.0f,
+ 2.39747f, 0.00234873f, 0.0f, 0.0f, 2.39747f,
+ 0.00223908f, 0.0f, 0.0f, 2.39747f, 0.00213453f,
+ 0.0f, 0.0f, 2.39747f, 0.00203487f, 0.0f,
+ 0.0f, 2.39747f, 0.00193986f, 0.0f, 0.0f,
+ 2.39747f, 0.00184928f, 0.0f, 0.0f, 2.39747f,
+ 0.00176292f, 0.0f, 0.0f, 2.39747f, 0.0016806f,
+ 0.0f, 0.0f, 2.39747f, 0.00160212f, 0.0f,
+ 0.0f, 2.39747f, 0.00152731f, 0.0f, 0.0f,
+ 2.39747f, 0.00145598f, 0.0f, 0.0f, 2.39747f,
+ 0.00138799f, 0.0f, 0.0f, 2.39747f, 0.00132316f,
+ 0.0f, 0.0f, 2.39747f, 0.00126137f, 0.0f,
+ 0.0f, 2.39747f, 0.00120246f, 0.0f, 0.0f,
+ 2.39747f, 0.0011463f, 0.0f, 0.0f, 2.39747f,
+ 0.00109276f, 0.0f, 0.0f, 2.39747f, 0.00104172f,
+ 0.0f, 0.0f, 2.39747f, 0.000993069f, 0.0f,
+ 0.0f, 2.39747f, 0.000946686f, 0.0f, 0.0f,
+ 2.39747f, 0.000902469f, 0.0f, 0.0f, 2.39747f,
+ 0.000860316f, 0.0f, 0.0f, 2.39747f, 0.000820132f,
+ 0.0f, 0.0f, 2.39747f, 0.000781825f, 0.0f,
+ 0.0f, 2.39747f, 0.000745306f, 0.0f, 0.0f,
+ 2.39747f, 0.000710492f, 0.0f, 0.0f, 2.39747f,
+ 0.000677305f, 0.0f, 0.0f, 2.39747f, 0.000645667f,
+ 0.0f, 0.0f, 2.39747f, 0.000615507f, 0.0f,
+ 0.0f, 2.39747f, 0.000586756f, 0.0f, 0.0f,
+ 2.39747f, 0.000559347f, 0.0f, 0.0f, 2.39747f,
+ 0.000533218f, 0.0f, 0.0f, 2.39747f, 0.00050831f,
+ 0.0f, 0.0f, 2.39747f, 0.000484565f, 0.0f,
+ 0.0f, 2.39747f, 0.000461929f, 0.0f, 0.0f,
+ 2.39747f, 0.000440351f, 0.0f, 0.0f, 2.39747f,
+ 0.00041978f, 0.0f, 0.0f, 2.39747f, 0.00040017f,
+ 0.0f, 0.0f, 2.39747f, 0.000381476f, 0.0f,
+ 0.0f, 2.39747f, 0.000363656f, 0.0f, 0.0f,
+ 2.39747f, 0.000346667f, 0.0f, 0.0f, 2.39747f,
+ 0.000330473f, 0.0f, 0.0f, 2.39747f, 0.000315034f,
+ 0.0f, 0.0f, 2.39747f, 0.000300317f, 0.0f,
+ 0.0f, 2.39747f, 0.000286287f, 0.0f, 0.0f,
+ 2.39747f, 0.000272913f, 0.0f, 0.0f, 2.39747f,
+ 0.000260164f, 0.0f, 0.0f, 2.39747f, 0.00024801f,
+ 0.0f, 0.0f, 2.39747f, 0.000236423f, 0.0f,
+ 0.0f, 2.39747f, 0.000225378f, 0.0f, 0.0f,
+ 2.39747f, 0.000214849f, 0.0f, 0.0f, 2.39747f,
+ 0.000204812f, 0.0f, 0.0f, 2.39747f, 0.000195244f,
+ 0.0f, 0.0f, 2.39747f, 0.000186122f, 0.0f,
+ 0.0f, 2.39747f, 0.000177427f, 0.0f, 0.0f,
+ 2.39747f, 0.000169138f, 0.0f, 0.0f, 2.39747f,
+ 0.000161236f, 0.0f, 0.0f, 2.39747f, 0.000153704f,
+ 0.0f, 0.0f, 2.39747f, 0.000146523f, 0.0f,
+ 0.0f, 2.39747f, 0.000139678f, 0.0f, 0.0f,
+ 2.39747f, 0.000133152f, 0.0f, 0.0f, 2.39747f,
+ 0.000126932f, 0.0f, 0.0f, 2.39747f, 0.000121001f,
+ 0.0f, 0.0f, 2.39747f, 0.000115348f, 0.0f,
+ 0.0f, 2.39747f, 0.00010996f, 0.0f, 0.0f,
+ 2.39747f, 0.000104822f, 0.0f, 0.0f, 2.39747f,
+ 9.99252e-05f, 0.0f, 0.0f, 2.39747f, 9.52568e-05f,
+ 0.0f, 0.0f, 2.39747f, 9.08065e-05f, 0.0f,
+ 0.0f, 2.39747f, 8.65641e-05f, 0.0f, 0.0f,
+ 2.39747f, 8.25199e-05f, 0.0f, 0.0f, 2.39747f,
+ 7.86646e-05f, 0.0f, 0.0f, 2.39747f, 7.49895e-05f,
+ 0.0f, 0.0f, 2.39747f, 7.1486e-05f, 0.0f,
+ 0.0f, 2.39747f, 6.81463e-05f, 0.0f, 0.0f,
+ 2.39747f, 6.49625e-05f, 0.0f, 0.0f, 2.39747f,
+ 6.19275e-05f, 0.0f, 0.0f, 2.39747f, 5.90343e-05f,
+ 0.0f, 0.0f, 2.39747f, 5.62762e-05f, 0.0f,
+ 0.0f, 2.39747f, 5.3647e-05f, 0.0f, 0.0f,
+ 2.39747f, 5.11407e-05f, 0.0f, 0.0f, 2.39747f,
+ 4.87514e-05f, 0.0f, 0.0f, 2.39747f, 4.64738e-05f,
+ 0.0f, 0.0f, 2.39747f, 4.43025e-05f, 0.0f,
+ 0.0f, 2.39747f, 4.22327e-05f, 0.0f, 0.0f,
+ 2.39747f, 4.02596e-05f, 0.0f, 0.0f, 2.39747f,
+ 3.83787e-05f, 0.0f, 0.0f, 2.39747f, 3.65857e-05f,
+ 0.0f, 0.0f, 2.39747f, 3.48764e-05f, 0.0f,
+ 0.0f, 2.39747f, 3.3247e-05f, 0.0f, 0.0f,
+ 2.39747f, 3.16937e-05f, 0.0f, 0.0f, 2.39747f,
+ 3.0213e-05f, 0.0f, 0.0f, 2.39747f, 2.88014e-05f,
+ 0.0f, 0.0f, 2.39747f, 2.74558e-05f, 0.0f,
+ 0.0f, 2.39747f, 2.61731e-05f, 0.0f, 0.0f,
+ 2.39747f, 2.49503e-05f, 0.0f, 0.0f, 2.39747f,
+ 2.37846e-05f, 0.0f, 0.0f, 2.39747f, 2.26734e-05f,
+ 0.0f, 0.0f, 2.39747f, 2.16141e-05f, 0.0f,
+ 0.0f, 2.39747f, 2.06043e-05f, 0.0f, 0.0f,
+ 2.39747f, 1.96417e-05f, 0.0f, 0.0f, 2.39747f,
+ 1.8724e-05f, 0.0f, 0.0f, 2.39747f, 1.78492e-05f,
+ 0.0f, 0.0f, 2.39747f, 1.70153e-05f, 0.0f,
+ 0.0f, 2.39747f, 1.62203e-05f, 0.0f, 0.0f,
+ 2.39747f, 1.54625e-05f, 0.0f, 0.0f, 2.39747f,
+ 1.47401e-05f, 0.0f, 0.0f, 2.39747f, 1.40515e-05f,
+ 0.0f, 0.0f, 2.39747f, 1.3395e-05f, 0.0f,
+ 0.0f, 2.39747f, 1.27692e-05f, 0.0f, 0.0f,
+ 2.39747f, 1.21726e-05f, 0.0f, 0.0f, 2.39747f,
+ 1.16039e-05f, 0.0f, 0.0f, 2.39747f, 1.10617e-05f,
+ 0.0f, 0.0f, 2.39747f, 1.05449e-05f, 0.0f,
+ 0.0f, 2.39747f, 1.00523e-05f, 0.0f, 0.0f,
+ 2.39747f, 9.58263e-06f, 0.0f, 0.0f, 2.39747f,
+ 9.13493e-06f, 0.0f, 0.0f, 2.39747f, 8.70814e-06f,
+ 0.0f, 0.0f, 2.39747f, 8.3013e-06f, 0.0f,
+ 0.0f, 2.39747f, 7.91346e-06f, 0.0f, 0.0f,
+ 2.39747f, 7.54374e-06f, 0.0f, 0.0f, 2.39747f,
+ 7.1913e-06f, 0.0f, 0.0f, 2.39747f, 6.85532e-06f,
+ 0.0f, 0.0f, 2.39747f, 6.53504e-06f, 0.0f,
+ 0.0f, 2.39747f, 6.22972e-06f, 0.0f, 0.0f,
+ 2.39747f, 5.93867e-06f, 0.0f, 0.0f, 2.39747f,
+ 5.66121e-06f, 0.0f, 0.0f, 2.39747f, 5.39672e-06f,
+ 0.0f, 0.0f, 2.39747f, 5.14458e-06f, 0.0f,
+ 0.0f, 2.39747f, 4.90423e-06f, 0.0f, 0.0f,
+ 2.39747f, 4.6751e-06f, 0.0f, 0.0f, 2.39747f,
+ 4.45668e-06f, 0.0f, 0.0f, 2.39747f, 4.24846e-06f,
+ 0.0f, 0.0f, 2.39747f, 4.04997e-06f, 0.0f,
+ 0.0f, 2.39747f, 3.86076e-06f, 0.0f, 0.0f,
+ 2.39747f, 3.68038e-06f, 0.0f, 0.0f, 2.39747f,
+ 3.50843e-06f, 0.0f, 0.0f, 2.39747f, 3.34452e-06f,
+ 0.0f, 0.0f, 2.39747f, 3.18826e-06f, 0.0f,
+ 0.0f, 2.39747f, 3.03931e-06f, 0.0f, 0.0f,
+ 2.39747f, 2.89731e-06f, 0.0f, 0.0f, 2.39747f,
+ 2.76195e-06f, 0.0f, 0.0f, 2.39747f, 2.63291e-06f,
+ 0.0f, 0.0f, 2.39747f, 2.5099e-06f, 0.0f,
+ 0.0f, 2.39747f, 2.39263e-06f, 0.0f, 0.0f,
+ 2.39747f, 2.28085e-06f, 0.0f, 0.0f, 2.39747f,
+ 2.17429e-06f, 0.0f, 0.0f, 2.39747f, 2.0727e-06f,
+ 0.0f, 0.0f, 2.39747f, 1.97587e-06f, 0.0f,
+ 0.0f, 2.39747f, 1.88355e-06f, 0.0f, 0.0f,
+ 2.39747f, 1.79555e-06f, 0.0f, 0.0f, 2.39747f,
+ 1.71167e-06f, 0.0f, 0.0f, 2.39747f, 1.6317e-06f,
+ 0.0f, 0.0f, 2.39747f, 1.55546e-06f, 0.0f,
+ 0.0f, 2.39747f, 1.48279e-06f, 0.0f, 0.0f,
+ 2.39747f, 1.41352e-06f, 0.0f, 0.0f, 2.39747f,
+ 1.34748e-06f, 0.0f, 0.0f};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_AMBISONICS_AMBISONIC_SPREAD_COEFFICIENTS_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/associated_legendre_polynomials_generator.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/associated_legendre_polynomials_generator.cc
new file mode 100644
index 000000000..49b7abf89
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/associated_legendre_polynomials_generator.cc
@@ -0,0 +1,147 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/associated_legendre_polynomials_generator.h"
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+AssociatedLegendrePolynomialsGenerator::AssociatedLegendrePolynomialsGenerator(
+ int max_degree, bool condon_shortley_phase, bool compute_negative_order)
+ : max_degree_(max_degree),
+ condon_shortley_phase_(condon_shortley_phase),
+ compute_negative_order_(compute_negative_order) {
+ DCHECK_GE(max_degree_, 0);
+}
+
+std::vector<float> AssociatedLegendrePolynomialsGenerator::Generate(
+ float x) const {
+ std::vector<float> values(GetNumValues());
+
+ // Bases for the recurrence relations.
+ values[GetIndex(0, 0)] = ComputeValue(0, 0, x, values);
+ if (max_degree_ >= 1) values[GetIndex(1, 0)] = ComputeValue(1, 0, x, values);
+
+ // Using recurrence relations, we now compute the rest of the values needed.
+ // (degree, 0), based on (degree - 1, 0) and (degree - 2, 0):
+ for (int degree = 2; degree <= max_degree_; ++degree) {
+ const int order = 0;
+ values[GetIndex(degree, order)] = ComputeValue(degree, order, x, values);
+ }
+ // (degree, degree):
+ for (int degree = 1; degree <= max_degree_; ++degree) {
+ const int order = degree;
+ values[GetIndex(degree, order)] = ComputeValue(degree, order, x, values);
+ }
+ // (degree, degree - 1):
+ for (int degree = 2; degree <= max_degree_; ++degree) {
+ const int order = degree - 1;
+ values[GetIndex(degree, order)] = ComputeValue(degree, order, x, values);
+ }
+ // The remaining positive orders, based on (degree - 1, order) and
+ // (degree - 2, order):
+ for (int degree = 3; degree <= max_degree_; ++degree) {
+ for (int order = 1; order <= degree - 2; ++order) {
+ values[GetIndex(degree, order)] = ComputeValue(degree, order, x, values);
+ }
+ }
+ // (degree, -order):
+ if (compute_negative_order_) {
+ for (int degree = 1; degree <= max_degree_; ++degree) {
+ for (int order = 1; order <= degree; ++order) {
+ values[GetIndex(degree, -order)] =
+ ComputeValue(degree, -order, x, values);
+ }
+ }
+ }
+ if (!condon_shortley_phase_) {
+ for (int degree = 1; degree <= max_degree_; ++degree) {
+ const int start_order = compute_negative_order_ ? -degree : 0;
+ for (int order = start_order; order <= degree; ++order) {
+ // Undo the Condon-Shortley phase.
+ values[GetIndex(degree, order)] *=
+ static_cast<float>(std::pow(-1, order));
+ }
+ }
+ }
+ return values;
+}
+
+size_t AssociatedLegendrePolynomialsGenerator::GetNumValues() const {
+ if (compute_negative_order_)
+ return (max_degree_ + 1) * (max_degree_ + 1);
+ else
+ return ((max_degree_ + 1) * (max_degree_ + 2)) / 2;
+}
+
+size_t AssociatedLegendrePolynomialsGenerator::GetIndex(int degree,
+ int order) const {
+ CheckIndexValidity(degree, order);
+ size_t result;
+ if (compute_negative_order_) {
+ result = static_cast<size_t>(degree * (degree + 1) + order);
+ } else {
+ result = static_cast<size_t>((degree * (degree + 1)) / 2 + order);
+ }
+ DCHECK_GE(result, 0U);
+ DCHECK_LT(result, GetNumValues());
+ return result;
+}
+
+float AssociatedLegendrePolynomialsGenerator::ComputeValue(
+ int degree, int order, float x, const std::vector<float>& values) const {
+ CheckIndexValidity(degree, order);
+ if (degree == 0 && order == 0) {
+ return 1;
+ } else if (degree == 1 && order == 0) {
+ return x;
+ } else if (degree == order) {
+ return std::pow(-1.0f, static_cast<float>(degree)) *
+ DoubleFactorial(2 * degree - 1) *
+ std::pow((1.0f - x * x), 0.5f * static_cast<float>(degree));
+ } else if (order == degree - 1) {
+ return x * static_cast<float>(2 * degree - 1) *
+ values[GetIndex(degree - 1, degree - 1)];
+ } else if (order < 0) {
+ return std::pow(-1.0f, static_cast<float>(order)) *
+ Factorial(degree + order) / Factorial(degree - order) *
+ values[GetIndex(degree, -order)];
+ } else {
+ return (static_cast<float>(2 * degree - 1) * x *
+ values[GetIndex(degree - 1, order)] -
+ static_cast<float>(degree - 1 + order) *
+ values[GetIndex(degree - 2, order)]) /
+ static_cast<float>(degree - order);
+ }
+}
+
+void AssociatedLegendrePolynomialsGenerator::CheckIndexValidity(
+ int degree, int order) const {
+ DCHECK_GE(degree, 0);
+ DCHECK_LE(degree, max_degree_);
+ if (compute_negative_order_) {
+ DCHECK_LE(-degree, order);
+ } else {
+ DCHECK_GE(order, 0);
+ }
+ DCHECK_LE(order, degree);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/associated_legendre_polynomials_generator.h b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/associated_legendre_polynomials_generator.h
new file mode 100644
index 000000000..84eb9d2f6
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/associated_legendre_polynomials_generator.h
@@ -0,0 +1,89 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_AMBISONICS_ASSOCIATED_LEGENDRE_POLYNOMIALS_GENERATOR_H_
+#define RESONANCE_AUDIO_AMBISONICS_ASSOCIATED_LEGENDRE_POLYNOMIALS_GENERATOR_H_
+
+#include <stddef.h>
+#include <vector>
+
+namespace vraudio {
+
+// Generates associated Legendre polynomials.
+class AssociatedLegendrePolynomialsGenerator {
+ public:
+ // Constructs a generator for associated Legendre polynomials (ALP).
+ //
+ // @param max_degree The maximum ALP degree supported by this generator.
+ // @param condon_shortley_phase Whether the Condon-Shortley phase, (-1)^order,
+ // should be included in the polynomials generated.
+ // @param compute_negative_order Whether this generator should compute
+ // negative-ordered polynomials.
+ AssociatedLegendrePolynomialsGenerator(int max_degree,
+ bool condon_shortley_phase,
+ bool compute_negative_order);
+
+ // Generates the associated Legendre polynomials for the given |x|, returning
+ // the computed sequence.
+ //
+ // @param x The abscissa (the polynomials' variable).
+ // @return Output vector of computed values.
+ std::vector<float> Generate(float x) const;
+
+ // Gets the number of associated Legendre polynomials this generator produces.
+ //
+ // @return The number of associated Legendre polynomials this generator
+ // produces.
+ size_t GetNumValues() const;
+
+ // Gets the index into the output vector for the given |degree| and |order|.
+ //
+ // @param degree The polynomial's degree.
+ // @param order The polynomial's order.
+ // @return The index into the vector of computed values corresponding to the
+ // specified ALP.
+ size_t GetIndex(int degree, int order) const;
+
+ private:
+ // Computes the ALP for (degree, order) the given |x| using recurrence
+ // relations. It is assumed that the ALPs necessary for each computation are
+ // already computed and stored in |values|.
+ //
+ // @param degree The degree of the polynomial being computed.
+ // @param degree The order of the polynomial being computed.
+ // @param values The previously computed values.
+ // @return The computed polynomial.
+ inline float ComputeValue(int degree, int order, float x,
+ const std::vector<float>& values) const;
+
+ // Checks the validity of the given index.
+ //
+ // @param degree The polynomial's degree.
+ // @param order The polynomial's order.
+ inline void CheckIndexValidity(int degree, int order) const;
+
+ // The maximum polynomial degree that can be computed; must be >= 0.
+ int max_degree_ = 0;
+ // Whether the Condon-Shortley phase, (-1)^order, should be included in the
+ // polynomials generated.
+ bool condon_shortley_phase_ = false;
+ // Whether this generator should compute negative-ordered polynomials.
+ bool compute_negative_order_ = false;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_AMBISONICS_ASSOCIATED_LEGENDRE_POLYNOMIALS_GENERATOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/associated_legendre_polynomials_generator_test.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/associated_legendre_polynomials_generator_test.cc
new file mode 100644
index 000000000..b9b6cdec4
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/associated_legendre_polynomials_generator_test.cc
@@ -0,0 +1,167 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/associated_legendre_polynomials_generator.h"
+
+#include <cmath>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+namespace {
+
+// Tolerated test error margin for single-precision floating points.
+const float kEpsilon = 1e-5f;
+
+// Generates associated Legendre polynomials up to and including the 4th degree,
+// with the Condon-Shortley phase and negative orders included.
+std::vector<float> GenerateExpectedValuesFourthDegree(float x) {
+ // From http://en.wikipedia.org/wiki/Associated_Legendre_polynomials
+ // Comments are (degree, order).
+ const std::vector<float> expected_values = {
+ 1.0f, // (0, 0)
+ 0.5f * std::sqrt(1.0f - x * x), // (1, -1)
+ x, // (1, 0)
+ -std::sqrt(1.0f - x * x), // (1, 1)
+ 1.0f / 8.0f * (1.0f - x * x), // (2, -2)
+ 0.5f * x * std::sqrt(1.0f - x * x), // (2, -1)
+ 0.5f * (3.0f * x * x - 1.0f), // (2, 0)
+ -3.0f * x * std::sqrt(1.0f - x * x), // (2, 1)
+ 3.0f * (1.0f - x * x), // (2, 2)
+ 15.0f / 720.0f * std::pow(1.0f - x * x, 3.0f / 2.0f), // (3, -3)
+ 15.0f / 120.0f * x * (1.0f - x * x), // (3, -2)
+ 3.0f / 24.0f * (5.0f * x * x - 1.0f) *
+ std::sqrt(1.0f - x * x), // (3, -1)
+ 0.5f * (5.0f * IntegerPow(x, 3) - 3.0f * x), // (3, 0)
+ -3.0f / 2.0f * (5.0f * x * x - 1.0f) * std::sqrt(1.0f - x * x), // (3, 1)
+ 15.0f * x * (1.0f - x * x), // (3, 2)
+ -15.0f * std::pow(1.0f - x * x, 3.0f / 2.0f), // (3, 3)
+ 105.0f / 40320.0f * IntegerPow(1.0f - x * x, 2), // (4, -4)
+ 105.0f / 5040.0f * x * std::pow(1.0f - x * x, 3.0f / 2.0f), // (4, -3)
+ 15.0f / 720.0f * (7.0f * x * x - 1.0f) * (1.0f - x * x), // (4, -2)
+ 5.0f / 40.0f * (7.0f * IntegerPow(x, 3) - 3.0f * x) *
+ std::sqrt(1.0f - x * x), // (4, -1)
+ 1.0f / 8.0f *
+ (35.0f * IntegerPow(x, 4) - 30.0f * x * x + 3.0f), // (4, 0)
+ -5.0f / 2.0f * (7.0f * IntegerPow(x, 3) - 3.0f * x) *
+ std::sqrt(1.0f - x * x), // (4, 1)
+ 15.0f / 2.0f * (7.0f * x * x - 1.0f) * (1.0f - x * x), // (4, 2)
+ -105.0f * x * std::pow(1.0f - x * x, 3.0f / 2.0f), // (4, 3)
+ 105.0f * IntegerPow(1.0f - x * x, 2) // (4, 4)
+ };
+
+ return expected_values;
+}
+
+// Tests that the values given by GetIndex are successive indices (n, n+1, n+2,
+// and so on).
+TEST(AssociatedLegendrePolynomialsGeneratorTest, GetIndex_SuccessiveIndices) {
+ const int kMaxDegree = 5;
+ const AssociatedLegendrePolynomialsGenerator alp_generator(kMaxDegree, false,
+ true);
+ int last_index = -1;
+ for (int degree = 0; degree <= kMaxDegree; ++degree) {
+ for (int order = -degree; order <= degree; ++order) {
+ int index = static_cast<int>(alp_generator.GetIndex(degree, order));
+ EXPECT_EQ(last_index + 1, index);
+ last_index = index;
+ }
+ }
+}
+
+// Tests that the zeroth-degree, zeroth-order ALP is always 1.
+TEST(AssociatedLegendrePolynomialsGeneratorTest, Generate_ZerothElementIsOne) {
+ const int kMaxDegree = 10;
+ for (int max_degree = 0; max_degree <= kMaxDegree; ++max_degree) {
+ for (int condon_shortley_phase = 0; condon_shortley_phase <= 1;
+ ++condon_shortley_phase) {
+ for (int compute_negative_order = 0; compute_negative_order <= 1;
+ ++compute_negative_order) {
+ AssociatedLegendrePolynomialsGenerator alp_generator(
+ max_degree, condon_shortley_phase != 0,
+ compute_negative_order != 0);
+
+ const float kVariableStep = 0.2f;
+ for (float x = -1.0f; x <= 1.0f; x += kVariableStep) {
+ const std::vector<float> values = alp_generator.Generate(x);
+
+ EXPECT_NEAR(values[0], 1.0f, kEpsilon);
+ }
+ }
+ }
+ }
+}
+
+// Tests that the polynomials generated are correct until the 4th degree.
+TEST(AssociatedLegendrePolynomialsGeneratorTest, Generate_CorrectFourthDegree) {
+ const int kMaxDegree = 4;
+ const bool kCondonShortleyPhase = true;
+ const bool kComputeNegativeOrder = true;
+ const AssociatedLegendrePolynomialsGenerator alp_generator(
+ kMaxDegree, kCondonShortleyPhase, kComputeNegativeOrder);
+
+ const float kVariableStep = 0.05f;
+ for (float x = -1.0f; x <= 1.0f; x += kVariableStep) {
+ const std::vector<float> generated_values = alp_generator.Generate(x);
+ const std::vector<float> expected_values =
+ GenerateExpectedValuesFourthDegree(x);
+ ASSERT_EQ(expected_values.size(), generated_values.size());
+ for (size_t i = 0; i < expected_values.size(); ++i) {
+ EXPECT_NEAR(generated_values[i], expected_values[i], kEpsilon)
+ << " at index " << i;
+ }
+ }
+}
+
+// Tests that the Condon-Shortley phase is correctly applied.
+TEST(AssociatedLegendrePolynomialsGeneratorTest, Generate_CondonShortleyPhase) {
+ const int kMaxDegree = 10;
+ const float kValue = 0.12345f;
+ for (int max_degree = 0; max_degree <= kMaxDegree; ++max_degree) {
+ for (int compute_negative_order = 0; compute_negative_order <= 1;
+ ++compute_negative_order) {
+ const AssociatedLegendrePolynomialsGenerator alp_generator_without_phase(
+ max_degree, false, compute_negative_order != 0);
+ const std::vector<float> values_without_phase =
+ alp_generator_without_phase.Generate(kValue);
+
+ const AssociatedLegendrePolynomialsGenerator alp_generator_with_phase(
+ max_degree, true, compute_negative_order != 0);
+ const std::vector<float> values_with_phase =
+ alp_generator_with_phase.Generate(kValue);
+
+ ASSERT_EQ(values_with_phase.size(), values_without_phase.size());
+ for (int degree = 0; degree <= max_degree; ++degree) {
+ const int start_order = compute_negative_order ? -degree : 0;
+ for (int order = start_order; order <= degree; ++order) {
+ const size_t index =
+ alp_generator_without_phase.GetIndex(degree, order);
+ const float expected = values_without_phase[index] *
+ std::pow(-1.0f, static_cast<float>(order));
+ EXPECT_NEAR(values_with_phase[index], expected, kEpsilon)
+ << " at degree " << degree << " and order " << order;
+ }
+ }
+ }
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/foa_rotator.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/foa_rotator.cc
new file mode 100644
index 000000000..f1ac68118
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/foa_rotator.cc
@@ -0,0 +1,117 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "ambisonics/foa_rotator.h"
+
+#include <algorithm>
+
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+bool FoaRotator::Process(const WorldRotation& target_rotation,
+ const AudioBuffer& input, AudioBuffer* output) {
+
+ DCHECK(output);
+ DCHECK_EQ(input.num_channels(), kNumFirstOrderAmbisonicChannels);
+ DCHECK_EQ(input.num_channels(), output->num_channels());
+ DCHECK_EQ(input.num_frames(), output->num_frames());
+
+ static const WorldRotation kIdentityRotation;
+
+ if (current_rotation_.AngularDifferenceRad(kIdentityRotation) <
+ kRotationQuantizationRad &&
+ target_rotation.AngularDifferenceRad(kIdentityRotation) <
+ kRotationQuantizationRad) {
+ return false;
+ }
+
+ if (current_rotation_.AngularDifferenceRad(target_rotation) <
+ kRotationQuantizationRad) {
+ // Rotate the whole input buffer frame by frame.
+ Rotate(current_rotation_, 0, input.num_frames(), input, output);
+ return true;
+ }
+
+ // In order to perform a smooth rotation, we divide the buffer into
+ // chunks of size |kSlerpFrameInterval|.
+ //
+
+ const size_t kSlerpFrameInterval = 32;
+
+ WorldRotation slerped_rotation;
+ // Rotate the input buffer at every slerp update interval. Truncate the
+ // final chunk if the input buffer is not an integer multiple of the
+ // chunk size.
+ for (size_t i = 0; i < input.num_frames(); i += kSlerpFrameInterval) {
+ const size_t duration =
+ std::min(input.num_frames() - i, kSlerpFrameInterval);
+ const float interpolation_factor = static_cast<float>(i + duration) /
+ static_cast<float>(input.num_frames());
+ slerped_rotation =
+ current_rotation_.slerp(interpolation_factor, target_rotation);
+ // Rotate the input buffer frame by frame within the current chunk.
+ Rotate(slerped_rotation, i, duration, input, output);
+ }
+
+ current_rotation_ = target_rotation;
+
+ return true;
+}
+
+void FoaRotator::Rotate(const WorldRotation& target_rotation,
+ size_t start_location, size_t duration,
+ const AudioBuffer& input, AudioBuffer* output) {
+
+ const AudioBuffer::Channel& input_channel_audio_space_w = input[0];
+ const AudioBuffer::Channel& input_channel_audio_space_y = input[1];
+ const AudioBuffer::Channel& input_channel_audio_space_z = input[2];
+ const AudioBuffer::Channel& input_channel_audio_space_x = input[3];
+ AudioBuffer::Channel* output_channel_audio_space_w = &(*output)[0];
+ AudioBuffer::Channel* output_channel_audio_space_y = &(*output)[1];
+ AudioBuffer::Channel* output_channel_audio_space_z = &(*output)[2];
+ AudioBuffer::Channel* output_channel_audio_space_x = &(*output)[3];
+
+ for (size_t frame = start_location; frame < start_location + duration;
+ ++frame) {
+ // Convert the current audio frame into world space position.
+ temp_audio_position_(0) = input_channel_audio_space_x[frame];
+ temp_audio_position_(1) = input_channel_audio_space_y[frame];
+ temp_audio_position_(2) = input_channel_audio_space_z[frame];
+ ConvertWorldFromAudioPosition(temp_audio_position_, &temp_world_position_);
+ // Apply rotation to |world_position| and return to audio space.
+ temp_rotated_world_position_ = target_rotation * temp_world_position_;
+
+ ConvertAudioFromWorldPosition(temp_rotated_world_position_,
+ &temp_rotated_audio_position_);
+ (*output_channel_audio_space_x)[frame] =
+ temp_rotated_audio_position_(0); // X
+ (*output_channel_audio_space_y)[frame] =
+ temp_rotated_audio_position_(1); // Y
+ (*output_channel_audio_space_z)[frame] =
+ temp_rotated_audio_position_(2); // Z
+ }
+ // Copy W channel.
+ std::copy_n(&input_channel_audio_space_w[start_location], duration,
+ &(*output_channel_audio_space_w)[start_location]);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/foa_rotator.h b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/foa_rotator.h
new file mode 100644
index 000000000..a91b975d4
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/foa_rotator.h
@@ -0,0 +1,61 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_AMBISONICS_FOA_ROTATOR_H_
+#define RESONANCE_AUDIO_AMBISONICS_FOA_ROTATOR_H_
+
+#include "base/audio_buffer.h"
+#include "base/spherical_angle.h"
+
+namespace vraudio {
+
+// Rotator for first order ambisonic soundfields. It supports ACN channel
+// ordering and SN3D normalization (AmbiX).
+class FoaRotator {
+ public:
+ // @param target_rotation Target rotation to be applied to the input buffer.
+ // @param input First order soundfield input buffer to be rotated.
+ // @param output Pointer to output buffer.
+ // @return True if rotation has been applied.
+ bool Process(const WorldRotation& target_rotation, const AudioBuffer& input,
+ AudioBuffer* output);
+
+ private:
+ // Method which rotates a specified chunk of data in the AudioBuffer.
+ //
+ // @param target_rotation Target rotation to be applied to the soundfield.
+ // @param start_location Sample index in the soundfield where the rotation
+ // should begin.
+ // @param duration Number of samples in soundfield to be rotated.
+ // @param input First order soundfield input buffer to be rotated.
+ // @param output Pointer to output buffer.
+ void Rotate(const WorldRotation& target_rotation, size_t start_location,
+ size_t duration, const AudioBuffer& input, AudioBuffer* output);
+
+ // Current rotation which is used in the interpolation process in order to
+ // perform a smooth rotation. Initialized with an identity matrix.
+ WorldRotation current_rotation_;
+
+ // Preallocation of temporary variables used during rotation.
+ AudioPosition temp_audio_position_;
+ WorldPosition temp_world_position_;
+ AudioPosition temp_rotated_audio_position_;
+ WorldPosition temp_rotated_world_position_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_AMBISONICS_FOA_ROTATOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/foa_rotator_test.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/foa_rotator_test.cc
new file mode 100644
index 000000000..e2f957fa2
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/foa_rotator_test.cc
@@ -0,0 +1,201 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/foa_rotator.h"
+
+#include <algorithm>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "ambisonics/ambisonic_codec_impl.h"
+#include "base/constants_and_types.h"
+#include "utils/planar_interleaved_conversion.h"
+
+namespace vraudio {
+
+namespace {
+
+using testing::tuple;
+using testing::Values;
+
+const int kAmbisonicOrder = 1;
+
+// Rotation angle used in the test.
+const float kAngleDegrees = 90.0f;
+
+// Initial, arbitrary direction of the encoded soundfield source.
+const SphericalAngle kInitialSourceAngle =
+ SphericalAngle::FromDegrees(22.0f, 33.0f);
+
+// Expected direction of the sound source rotated by |kAngleDegrees| against the
+// right facing axis (x in the world coordinate system).
+const SphericalAngle kXrotatedSourceAngle =
+ SphericalAngle::FromDegrees(150.021778639249f, 51.0415207997462f);
+
+// Expected direction of the sound source rotated by |kAngleDegrees| against the
+// upward facing axis (y in the world coordinate system).
+const SphericalAngle kYrotatedSourceAngle =
+ SphericalAngle::FromDegrees(112.0f, 33.0f);
+
+// Expected direction of the sound source rotated by |kAngleDegrees| against the
+// back facing axis (z in the world coordinate system).
+const SphericalAngle kZrotatedSourceAngle =
+ SphericalAngle::FromDegrees(35.0077312770459f, -18.3108067341351f);
+
+// Rotation interpolation interval in terms of frames (used by the FoaRotator).
+const size_t kSlerpFrameInterval = 32;
+
+class FoaRotatorTest : public ::testing::Test {
+ protected:
+ FoaRotatorTest() {}
+
+ // Virtual methods from ::testing::Test
+ ~FoaRotatorTest() override {}
+
+ void SetUp() override {
+ // Initialize the first order soundfield rotator.
+ foa_rotator_ = std::unique_ptr<FoaRotator>(new FoaRotator);
+ }
+
+ void TearDown() override {}
+
+ // Test method which creates a soundfield buffer, rotates it by
+ // |rotation_angle| against |rotation_axis|, and compares the output to the
+ // reference soundfield buffer (where the source is spatialized at the
+ // |expected_source_angle|).
+ void CompareRotatedAndReferenceSoundfields(
+ const std::vector<float>& input_data, float rotation_angle,
+ const WorldPosition& rotation_axis,
+ const SphericalAngle& expected_source_angle) {
+ AudioBuffer input_buffer(1, input_data.size());
+ FillAudioBuffer(input_data, 1, &input_buffer);
+ // Initialize a first order mono codec with the |kInitialSourceAngle|. This
+ // will be used to obtain the rotated soundfield.
+ std::unique_ptr<MonoAmbisonicCodec<>> rotated_source_mono_codec(
+ new MonoAmbisonicCodec<>(kAmbisonicOrder, {kInitialSourceAngle}));
+ // Initialize a first order mono codec with the expected source angle. This
+ // will be used as a reference for the rotated soundfield.
+ std::unique_ptr<MonoAmbisonicCodec<>> reference_source_mono_codec(
+ new MonoAmbisonicCodec<>(kAmbisonicOrder, {expected_source_angle}));
+ // Generate soundfield buffers representing sound sources at required
+ // angles.
+ AudioBuffer encoded_rotated_buffer(kNumFirstOrderAmbisonicChannels,
+ input_data.size());
+ AudioBuffer encoded_reference_buffer(kNumFirstOrderAmbisonicChannels,
+ input_data.size());
+ rotated_source_mono_codec->EncodeBuffer(input_buffer,
+ &encoded_rotated_buffer);
+ reference_source_mono_codec->EncodeBuffer(input_buffer,
+ &encoded_reference_buffer);
+ // Rotate the test soundfield by |rotation_angle| degrees wrt the given
+ // |rotation_axis|.
+ const WorldRotation rotation = WorldRotation(
+ AngleAxisf(rotation_angle * kRadiansFromDegrees, rotation_axis));
+ const bool result = foa_rotator_->Process(rotation, encoded_rotated_buffer,
+ &encoded_rotated_buffer);
+ EXPECT_TRUE(result);
+ // Check if the sound source in the reference and rotated buffers are in the
+ // same direction.
+ // If the buffer size is more than |kSlerpFrameInterval|, due to
+ // interpolation, we expect that the last |kSlerpFrameInterval| frames have
+ // reached the target rotation.
+ // If the buffer size is less than |kSlerpFrameInterval|, because no
+ // interpolation is applied, the rotated soundfield should result from
+ // frame 0.
+ for (size_t channel = 0; channel < encoded_rotated_buffer.num_channels();
+ ++channel) {
+ const int num_frames =
+ static_cast<int>(encoded_rotated_buffer[channel].size());
+ const int interval = static_cast<int>(kSlerpFrameInterval);
+ const int frames_to_compare =
+ (num_frames % interval) ? (num_frames % interval) : interval;
+ const int start_frame = std::max(0, num_frames - frames_to_compare);
+ ASSERT_LT(start_frame, num_frames);
+ for (int frame = start_frame; frame < num_frames; ++frame) {
+ EXPECT_NEAR(encoded_rotated_buffer[channel][frame],
+ encoded_reference_buffer[channel][frame], kEpsilonFloat);
+ }
+ }
+ }
+
+ // First Order Ambisonic rotator instance.
+ std::unique_ptr<FoaRotator> foa_rotator_;
+};
+
+// Tests that no rotation is aplied if |kRotationQuantizationRad| is not
+// exceeded.
+TEST_F(FoaRotatorTest, RotationThresholdTest) {
+ const size_t kFramesPerBuffer = 16;
+ const std::vector<float> kInputVector(
+ kFramesPerBuffer * kNumFirstOrderAmbisonicChannels, 1.0f);
+ AudioBuffer input_buffer(kNumFirstOrderAmbisonicChannels, kFramesPerBuffer);
+ FillAudioBuffer(kInputVector, kNumFirstOrderAmbisonicChannels, &input_buffer);
+ AudioBuffer output_buffer(kNumFirstOrderAmbisonicChannels, kFramesPerBuffer);
+ const WorldRotation kSmallRotation =
+ WorldRotation(1.0f, 0.001f, 0.001f, 0.001f);
+ const WorldRotation kLargeRotation = WorldRotation(1.0f, 0.1f, 0.1f, 0.1f);
+ EXPECT_FALSE(
+ foa_rotator_->Process(kSmallRotation, input_buffer, &output_buffer));
+ EXPECT_TRUE(
+ foa_rotator_->Process(kLargeRotation, input_buffer, &output_buffer));
+}
+
+typedef tuple<WorldPosition, SphericalAngle> TestParams;
+class FoaAxesRotationTest : public FoaRotatorTest,
+ public ::testing::WithParamInterface<TestParams> {};
+
+// Tests first order soundfield rotation against the x, y and z axis using long
+// input buffers (>> slerp interval).
+TEST_P(FoaAxesRotationTest, CompareWithExpectedAngleLongBuffer) {
+ const WorldPosition& rotation_axis = ::testing::get<0>(GetParam());
+ const SphericalAngle& expected_angle = ::testing::get<1>(GetParam());
+ const size_t kLongFramesPerBuffer = 512;
+ const std::vector<float> kLongInputData(kLongFramesPerBuffer, 1.0f);
+ CompareRotatedAndReferenceSoundfields(kLongInputData, kAngleDegrees,
+ rotation_axis, expected_angle);
+}
+
+// Tests first order soundfield rotation against the x, y and z axes using short
+// input buffers (< slerp interval).
+TEST_P(FoaAxesRotationTest, CompareWithExpectedAngleShortBuffer) {
+ const WorldPosition& rotation_axis = ::testing::get<0>(GetParam());
+ const SphericalAngle& expected_angle = ::testing::get<1>(GetParam());
+ const size_t kShortFramesPerBuffer = kSlerpFrameInterval / 2;
+ const std::vector<float> kShortInputData(kShortFramesPerBuffer, 1.0f);
+ CompareRotatedAndReferenceSoundfields(kShortInputData, kAngleDegrees,
+ rotation_axis, expected_angle);
+}
+
+// Tests first order soundfield rotation against the x, y and z axes using
+// buffer sizes that are (> slerp interval) and not integer multiples of
+// the slerp interval.
+TEST_P(FoaAxesRotationTest, CompareWithExpectedAngleOddBufferSizes) {
+ const WorldPosition& rotation_axis = ::testing::get<0>(GetParam());
+ const SphericalAngle& expected_angle = ::testing::get<1>(GetParam());
+ const size_t frames_per_buffer = kSlerpFrameInterval + 3U;
+ const std::vector<float> kShortInputData(frames_per_buffer, 1.0f);
+ CompareRotatedAndReferenceSoundfields(kShortInputData, kAngleDegrees,
+ rotation_axis, expected_angle);
+}
+
+INSTANTIATE_TEST_CASE_P(
+ TestParameters, FoaAxesRotationTest,
+ Values(TestParams({1.0f, 0.0f, 0.0f}, kXrotatedSourceAngle),
+ TestParams({0.0f, 1.0f, 0.0f}, kYrotatedSourceAngle),
+ TestParams({0.0f, 0.0f, 1.0f}, kZrotatedSourceAngle)));
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/hoa_rotator.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/hoa_rotator.cc
new file mode 100644
index 000000000..a76575e22
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/hoa_rotator.cc
@@ -0,0 +1,308 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/hoa_rotator.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "ambisonics/utils.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+namespace {
+
+// Below are the helper methods to compute SH rotation using recursion. The code
+// is branched / modified from:
+
+// maths described in the following papers:
+//
+// [1] R. Green, "Spherical Harmonic Lighting: The Gritty Details", GDC 2003,
+// http://www.research.scea.com/gdc2003/spherical-harmonic-lighting.pdf
+// [2] J. Ivanic and K. Ruedenberg, "Rotation Matrices for Real Spherical
+// Harmonics. Direct Determination by Recursion", J. Phys. Chem., vol. 100,
+// no. 15, pp. 6342-6347, 1996.
+// http://pubs.acs.org/doi/pdf/10.1021/jp953350u
+// [2b] Corrections to initial publication:
+// http://pubs.acs.org/doi/pdf/10.1021/jp9833350
+
+// Kronecker Delta function.
+inline float KroneckerDelta(int i, int j) { return (i == j) ? 1.0f : 0.0f; }
+
+// [2] uses an odd convention of referring to the rows and columns using
+// centered indices, so the middle row and column are (0, 0) and the upper
+// left would have negative coordinates.
+//
+// This is a convenience function to allow us to access an Eigen::MatrixXf
+// in the same manner, assuming r is a (2l+1)x(2l+1) matrix.
+float GetCenteredElement(const Eigen::MatrixXf& r, int i, int j) {
+ // The shift to go from [-l, l] to [0, 2l] is (rows - 1) / 2 = l,
+ // (since the matrix is assumed to be square, rows == cols).
+ const int offset = (static_cast<int>(r.rows()) - 1) / 2;
+ return r(i + offset, j + offset);
+}
+
+// Helper function defined in [2] that is used by the functions U, V, W.
+// This should not be called on its own, as U, V, and W (and their coefficients)
+// select the appropriate matrix elements to access arguments |a| and |b|.
+float P(int i, int a, int b, int l, const std::vector<Eigen::MatrixXf>& r) {
+ if (b == l) {
+ return GetCenteredElement(r[1], i, 1) *
+ GetCenteredElement(r[l - 1], a, l - 1) -
+ GetCenteredElement(r[1], i, -1) *
+ GetCenteredElement(r[l - 1], a, -l + 1);
+ } else if (b == -l) {
+ return GetCenteredElement(r[1], i, 1) *
+ GetCenteredElement(r[l - 1], a, -l + 1) +
+ GetCenteredElement(r[1], i, -1) *
+ GetCenteredElement(r[l - 1], a, l - 1);
+ } else {
+ return GetCenteredElement(r[1], i, 0) * GetCenteredElement(r[l - 1], a, b);
+ }
+}
+
+// The functions U, V, and W should only be called if the correspondingly
+// named coefficient u, v, w from the function ComputeUVWCoeff() is non-zero.
+// When the coefficient is 0, these would attempt to access matrix elements that
+// are out of bounds. The vector of rotations, |r|, must have the |l - 1|
+// previously completed band rotations. These functions are valid for |l >= 2|.
+
+float U(int m, int n, int l, const std::vector<Eigen::MatrixXf>& r) {
+ // Although [1, 2] split U into three cases for m == 0, m < 0, m > 0
+ // the actual values are the same for all three cases.
+ return P(0, m, n, l, r);
+}
+
+float V(int m, int n, int l, const std::vector<Eigen::MatrixXf>& r) {
+ if (m == 0) {
+ return P(1, 1, n, l, r) + P(-1, -1, n, l, r);
+ } else if (m > 0) {
+ const float d = KroneckerDelta(m, 1);
+ return P(1, m - 1, n, l, r) * std::sqrt(1 + d) -
+ P(-1, -m + 1, n, l, r) * (1 - d);
+ } else {
+ // Note there is apparent errata in [1,2,2b] dealing with this particular
+ // case. [2b] writes it should be P*(1-d)+P*(1-d)^0.5
+ // [1] writes it as P*(1+d)+P*(1-d)^0.5, but going through the math by hand,
+ // you must have it as P*(1-d)+P*(1+d)^0.5 to form a 2^.5 term, which
+ // parallels the case where m > 0.
+ const float d = KroneckerDelta(m, -1);
+ return P(1, m + 1, n, l, r) * (1 - d) +
+ P(-1, -m - 1, n, l, r) * std::sqrt(1 + d);
+ }
+}
+
+float W(int m, int n, int l, const std::vector<Eigen::MatrixXf>& r) {
+ if (m == 0) {
+ // Whenever this happens, w is also 0 so W can be anything.
+ return 0.0f;
+ } else if (m > 0) {
+ return P(1, m + 1, n, l, r) + P(-1, -m - 1, n, l, r);
+ } else {
+ return P(1, m - 1, n, l, r) - P(-1, -m + 1, n, l, r);
+ }
+}
+
+// Calculates the coefficients applied to the U, V, and W functions. Because
+// their equations share many common terms they are computed simultaneously.
+void ComputeUVWCoeff(int m, int n, int l, float* u, float* v, float* w) {
+ const float d = KroneckerDelta(m, 0);
+ const float denom = (abs(n) == l ? static_cast<float>(2 * l * (2 * l - 1))
+ : static_cast<float>((l + n) * (l - n)));
+ const float one_over_denom = 1.0f / denom;
+
+ *u = std::sqrt(static_cast<float>((l + m) * (l - m)) * one_over_denom);
+ *v = 0.5f *
+ std::sqrt((1.0f + d) * static_cast<float>(l + abs(m) - 1) *
+ (static_cast<float>(l + abs(m))) * one_over_denom) *
+ (1.0f - 2.0f * d);
+ *w = -0.5f *
+ std::sqrt(static_cast<float>(l - abs(m) - 1) *
+ (static_cast<float>(l - abs(m))) * one_over_denom) *
+ (1.0f - d);
+}
+
+// Calculates the (2l+1)x(2l+1) rotation matrix for the band l.
+// This uses the matrices computed for band 1 and band l-1 to compute the
+// matrix for band l. |rotations| must contain the previously computed l-1
+// rotation matrices.
+//
+// This implementation comes from p. 5 (6346), Table 1 and 2 in [2] taking
+// into account the corrections from [2b].
+void ComputeBandRotation(int l, std::vector<Eigen::MatrixXf>* rotations) {
+ // The lth band rotation matrix has rows and columns equal to the number of
+ // coefficients within that band (-l <= m <= l implies 2l + 1 coefficients).
+ Eigen::MatrixXf rotation(2 * l + 1, 2 * l + 1);
+ for (int m = -l; m <= l; ++m) {
+ for (int n = -l; n <= l; ++n) {
+ float u, v, w;
+ ComputeUVWCoeff(m, n, l, &u, &v, &w);
+
+ // The functions U, V, W are only safe to call if the coefficients
+ // u, v, w are not zero.
+ if (std::abs(u) > 0.0f) u *= U(m, n, l, *rotations);
+ if (std::abs(v) > 0.0f) v *= V(m, n, l, *rotations);
+ if (std::abs(w) > 0.0f) w *= W(m, n, l, *rotations);
+
+ rotation(m + l, n + l) = (u + v + w);
+ }
+ }
+ (*rotations)[l] = rotation;
+}
+
+} // namespace
+
+HoaRotator::HoaRotator(int ambisonic_order)
+ : ambisonic_order_(ambisonic_order),
+ rotation_matrices_(ambisonic_order_ + 1),
+ rotation_matrix_(
+ static_cast<int>(GetNumPeriphonicComponents(ambisonic_order)),
+ static_cast<int>(GetNumPeriphonicComponents(ambisonic_order))) {
+ DCHECK_GE(ambisonic_order_, 2);
+
+ // Initialize rotation sub-matrices.
+ // Order 0 matrix (first band) is simply the 1x1 identity.
+ Eigen::MatrixXf r(1, 1);
+ r(0, 0) = 1.0f;
+ rotation_matrices_[0] = r;
+ // All the other ambisonic orders (bands) are set to identity matrices of
+ // corresponding sizes.
+ for (int l = 1; l <= ambisonic_order_; ++l) {
+ const size_t submatrix_size = GetNumNthOrderPeriphonicComponents(l);
+ r.resize(submatrix_size, submatrix_size);
+ rotation_matrices_[l] = r.setIdentity();
+ }
+ // Initialize the final rotation matrix to an identity matrix.
+ rotation_matrix_.setIdentity();
+}
+
+bool HoaRotator::Process(const WorldRotation& target_rotation,
+ const AudioBuffer& input, AudioBuffer* output) {
+
+ DCHECK(output);
+ DCHECK_EQ(input.num_channels(), GetNumPeriphonicComponents(ambisonic_order_));
+ DCHECK_EQ(input.num_channels(), output->num_channels());
+ DCHECK_EQ(input.num_frames(), output->num_frames());
+
+ static const WorldRotation kIdentityRotation;
+
+ if (current_rotation_.AngularDifferenceRad(kIdentityRotation) <
+ kRotationQuantizationRad &&
+ target_rotation.AngularDifferenceRad(kIdentityRotation) <
+ kRotationQuantizationRad) {
+ return false;
+ }
+
+ const size_t channel_stride = input.GetChannelStride();
+
+ typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
+ RowMajorMatrixf;
+
+ const Eigen::Map<const RowMajorMatrixf, Eigen::Aligned, Eigen::OuterStride<>>
+ input_matrix(input[0].begin(), static_cast<int>(input.num_channels()),
+ static_cast<int>(input.num_frames()),
+ Eigen::OuterStride<>(static_cast<int>(channel_stride)));
+
+ Eigen::Map<RowMajorMatrixf, Eigen::Aligned, Eigen::OuterStride<>>
+ output_matrix((*output)[0].begin(),
+ static_cast<int>(input.num_channels()),
+ static_cast<int>(input.num_frames()),
+ Eigen::OuterStride<>(static_cast<int>(channel_stride)));
+
+ if (current_rotation_.AngularDifferenceRad(target_rotation) <
+ kRotationQuantizationRad) {
+ output_matrix = rotation_matrix_ * input_matrix;
+ return true;
+ }
+
+ // In order to perform a smooth rotation, we divide the buffer into
+ // chunks of size |kSlerpFrameInterval|.
+ //
+
+ const size_t kSlerpFrameInterval = 32;
+
+ WorldRotation slerped_rotation;
+ // Rotate the input buffer at every slerp update interval. Truncate the
+ // final chunk if the input buffer is not an integer multiple of the
+ // chunk size.
+ for (size_t i = 0; i < input.num_frames(); i += kSlerpFrameInterval) {
+ const size_t duration =
+ std::min(input.num_frames() - i, kSlerpFrameInterval);
+ const float interpolation_factor = static_cast<float>(i + duration) /
+ static_cast<float>(input.num_frames());
+ UpdateRotationMatrix(
+ current_rotation_.slerp(interpolation_factor, target_rotation));
+ output_matrix.block(0 /* first channel */, i, output->num_channels(),
+ duration) =
+ rotation_matrix_ * input_matrix.block(0 /* first channel */, i,
+ input.num_channels(), duration);
+ }
+ current_rotation_ = target_rotation;
+
+ return true;
+}
+
+void HoaRotator::UpdateRotationMatrix(const WorldRotation& rotation) {
+
+
+ // There is no need to update 0th order 1-element sub-matrix.
+ // First order sub-matrix can be updated directly from the WorldRotation
+ // quaternion. However, we must account for the flipped left-right and
+ // front-back axis in the World coordinates.
+ AudioRotation rotation_audio_space;
+ ConvertAudioFromWorldRotation(rotation, &rotation_audio_space);
+ rotation_matrices_[1] = rotation_audio_space.toRotationMatrix();
+ rotation_matrix_.block(1, 1, 3, 3) = rotation_matrices_[1];
+
+ // Sub-matrices for the remaining orders are updated recursively using the
+ // equations provided in [2, 2b]. An example final rotation matrix composed of
+ // sub-matrices of orders 0 to 3 has the following structure:
+ //
+ // X | 0 0 0 | 0 0 0 0 0 | 0 0 0 0 0 0 0
+ // -------------------------------------
+ // 0 | X X X | 0 0 0 0 0 | 0 0 0 0 0 0 0
+ // 0 | X X X | 0 0 0 0 0 | 0 0 0 0 0 0 0
+ // 0 | X X X | 0 0 0 0 0 | 0 0 0 0 0 0 0
+ // -------------------------------------
+ // 0 | 0 0 0 | X X X X X | 0 0 0 0 0 0 0
+ // 0 | 0 0 0 | X X X X X | 0 0 0 0 0 0 0
+ // 0 | 0 0 0 | X X X X X | 0 0 0 0 0 0 0
+ // 0 | 0 0 0 | X X X X X | 0 0 0 0 0 0 0
+ // 0 | 0 0 0 | X X X X X | 0 0 0 0 0 0 0
+ // -------------------------------------
+ // 0 | 0 0 0 | 0 0 0 0 0 | X X X X X X X
+ // 0 | 0 0 0 | 0 0 0 0 0 | X X X X X X X
+ // 0 | 0 0 0 | 0 0 0 0 0 | X X X X X X X
+ // 0 | 0 0 0 | 0 0 0 0 0 | X X X X X X X
+ // 0 | 0 0 0 | 0 0 0 0 0 | X X X X X X X
+ // 0 | 0 0 0 | 0 0 0 0 0 | X X X X X X X
+ // 0 | 0 0 0 | 0 0 0 0 0 | X X X X X X X
+ //
+ for (int current_order = 2; current_order <= ambisonic_order_;
+ ++current_order) {
+ ComputeBandRotation(current_order, &rotation_matrices_);
+ const int index = current_order * current_order;
+ const int size =
+ static_cast<int>(GetNumNthOrderPeriphonicComponents(current_order));
+ rotation_matrix_.block(index, index, size, size) =
+ rotation_matrices_[current_order];
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/hoa_rotator.h b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/hoa_rotator.h
new file mode 100644
index 000000000..956811df4
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/hoa_rotator.h
@@ -0,0 +1,69 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_AMBISONICS_HOA_ROTATOR_H_
+#define RESONANCE_AUDIO_AMBISONICS_HOA_ROTATOR_H_
+
+#include <vector>
+
+#include "Eigen/Dense"
+#include "base/audio_buffer.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+// Rotator for higher order ambisonic sound fields. It supports ACN channel
+// ordering and SN3D normalization (AmbiX).
+class HoaRotator {
+ public:
+ // Constructs a sound field rotator of an arbitrary ambisonic order.
+ //
+ // @param ambisonic_order Order of ambisonic sound field.
+ explicit HoaRotator(int ambisonic_order);
+
+ // Performs a smooth inplace rotation of a sound field buffer from
+ // |current_rotation_| to |target_rotation|.
+ //
+ // @param target_rotation Target rotation to be applied to the input buffer.
+ // @param input Higher order sound field input buffer to be rotated.
+ // @param output Pointer to output buffer.
+ // @return True if rotation has been applied.
+ bool Process(const WorldRotation& target_rotation, const AudioBuffer& input,
+ AudioBuffer* output);
+
+ private:
+ // Updates the rotation matrix with using supplied WorldRotation.
+ //
+ // @param rotation World rotation.
+ void UpdateRotationMatrix(const WorldRotation& rotation);
+
+ // Order of the ambisonic sound field handled by the rotator.
+ const int ambisonic_order_;
+
+ // Current rotation which is used in the interpolation process in order to
+ // compute new rotation matrix. Initialized with an identity rotation.
+ WorldRotation current_rotation_;
+
+ // Spherical harmonics rotation sub-matrices for each order.
+ std::vector<Eigen::MatrixXf> rotation_matrices_;
+
+ // Final spherical harmonics rotation matrix.
+ Eigen::MatrixXf rotation_matrix_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_AMBISONICS_HOA_ROTATOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/hoa_rotator_test.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/hoa_rotator_test.cc
new file mode 100644
index 000000000..6472412c9
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/hoa_rotator_test.cc
@@ -0,0 +1,201 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/hoa_rotator.h"
+
+#include <algorithm>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "ambisonics/ambisonic_codec_impl.h"
+#include "base/constants_and_types.h"
+#include "utils/planar_interleaved_conversion.h"
+
+namespace vraudio {
+
+namespace {
+
+using testing::tuple;
+using testing::Values;
+
+const int kAmbisonicOrder = 3;
+
+// Rotation angle used in the test.
+const float kAngleDegrees = 90.0f;
+
+// Initial, arbitrary direction of the encoded soundfield source.
+const SphericalAngle kInitialSourceAngle =
+ SphericalAngle::FromDegrees(22.0f, 33.0f);
+
+// Expected direction of the sound source rotated by |kAngleDegrees| against the
+// right facing axis (x in the world coordinate system).
+const SphericalAngle kXrotatedSourceAngle =
+ SphericalAngle::FromDegrees(150.021778639249f, 51.0415207997462f);
+
+// Expected direction of the sound source rotated by |kAngleDegrees| against the
+// upward facing axis (y in the world coordinate system).
+const SphericalAngle kYrotatedSourceAngle =
+ SphericalAngle::FromDegrees(112.0f, 33.0f);
+
+// Expected direction of the sound source rotated by |kAngleDegrees| against the
+// back facing axis (z in the world coordinate system).
+const SphericalAngle kZrotatedSourceAngle =
+ SphericalAngle::FromDegrees(35.0077312770459f, -18.3108067341351f);
+
+// Rotation interpolation interval in terms of frames (used by the HoaRotator).
+const size_t kSlerpFrameInterval = 32;
+
+class HoaRotatorTest : public ::testing::Test {
+ protected:
+ HoaRotatorTest() {}
+
+ // Virtual methods from ::testing::Test
+ ~HoaRotatorTest() override {}
+
+ void SetUp() override {
+ // Initialize the third order soundfield rotator.
+ hoa_rotator_ = std::unique_ptr<HoaRotator>(new HoaRotator(kAmbisonicOrder));
+ }
+
+ void TearDown() override {}
+
+ // Test method which creates a HOA soundfield buffer, rotates it by
+ // |rotation_angle| against |rotation_axis|, and compares the output to the
+ // reference soundfield buffer (where the source is already spatialized at the
+ // |expected_source_angle|).
+ void CompareRotatedAndReferenceSoundfields(
+ const std::vector<float>& input_data, float rotation_angle,
+ const WorldPosition& rotation_axis,
+ const SphericalAngle& expected_source_angle) {
+ AudioBuffer input_buffer(1, input_data.size());
+ FillAudioBuffer(input_data, 1, &input_buffer);
+ // Initialize a third order mono codec with the |kInitialSourceAngle|. This
+ // will be used to obtain the rotated soundfield.
+ std::unique_ptr<MonoAmbisonicCodec<>> rotated_source_mono_codec(
+ new MonoAmbisonicCodec<>(kAmbisonicOrder, {kInitialSourceAngle}));
+ // Initialize a third order mono codec with the expected source angle. This
+ // will be used as a reference for the rotated soundfield.
+ std::unique_ptr<MonoAmbisonicCodec<>> reference_source_mono_codec(
+ new MonoAmbisonicCodec<>(kAmbisonicOrder, {expected_source_angle}));
+ // Generate soundfield buffers representing sound sources at required
+ // angles.
+ AudioBuffer encoded_rotated_buffer(kNumThirdOrderAmbisonicChannels,
+ input_data.size());
+ AudioBuffer encoded_reference_buffer(kNumThirdOrderAmbisonicChannels,
+ input_data.size());
+ rotated_source_mono_codec->EncodeBuffer(input_buffer,
+ &encoded_rotated_buffer);
+ reference_source_mono_codec->EncodeBuffer(input_buffer,
+ &encoded_reference_buffer);
+ // Rotate the test soundfield by |rotation_angle| degrees wrt the given
+ // |rotation_axis|.
+ const WorldRotation rotation = WorldRotation(
+ AngleAxisf(rotation_angle * kRadiansFromDegrees, rotation_axis));
+ const bool result = hoa_rotator_->Process(rotation, encoded_rotated_buffer,
+ &encoded_rotated_buffer);
+ EXPECT_TRUE(result);
+ // Check if the sound source in the reference and rotated buffers are in the
+ // same direction.
+ // If the buffer size is more than |kSlerpFrameInterval|, due to
+ // interpolation, we expect that the last |kSlerpFrameInterval| frames have
+ // undergone the full rotation.
+ // If the buffer size is less than |kSlerpFrameInterval|, because no
+ // interpolation is applied, the rotated soundfield should result from
+ // frame 0.
+ for (size_t channel = 0; channel < encoded_rotated_buffer.num_channels();
+ ++channel) {
+ const int num_frames =
+ static_cast<int>(encoded_rotated_buffer[channel].size());
+ const int interval = static_cast<int>(kSlerpFrameInterval);
+ const int frames_to_compare =
+ (num_frames % interval) ? (num_frames % interval) : interval;
+ const int start_frame = std::max(0, num_frames - frames_to_compare);
+ ASSERT_LT(start_frame, num_frames);
+ for (int frame = start_frame; frame < num_frames; ++frame) {
+ EXPECT_NEAR(encoded_rotated_buffer[channel][frame],
+ encoded_reference_buffer[channel][frame], kEpsilonFloat);
+ }
+ }
+ }
+
+ // Higher Order Ambisonic rotator instance.
+ std::unique_ptr<HoaRotator> hoa_rotator_;
+};
+
+// Tests that no rotation is aplied if |kRotationQuantizationRad| is not
+// exceeded.
+TEST_F(HoaRotatorTest, RotationThresholdTest) {
+ const size_t kFramesPerBuffer = 16;
+ const std::vector<float> kInputVector(
+ kFramesPerBuffer * kNumThirdOrderAmbisonicChannels, 1.0f);
+ AudioBuffer input_buffer(kNumThirdOrderAmbisonicChannels, kFramesPerBuffer);
+ FillAudioBuffer(kInputVector, kNumThirdOrderAmbisonicChannels, &input_buffer);
+ AudioBuffer output_buffer(kNumThirdOrderAmbisonicChannels, kFramesPerBuffer);
+ const WorldRotation kSmallRotation =
+ WorldRotation(1.0f, 0.001f, 0.001f, 0.001f);
+ const WorldRotation kLargeRotation = WorldRotation(1.0f, 0.1f, 0.1f, 0.1f);
+ EXPECT_FALSE(
+ hoa_rotator_->Process(kSmallRotation, input_buffer, &output_buffer));
+ EXPECT_TRUE(
+ hoa_rotator_->Process(kLargeRotation, input_buffer, &output_buffer));
+}
+
+typedef tuple<WorldPosition, SphericalAngle> TestParams;
+class HoaAxesRotationTest : public HoaRotatorTest,
+ public ::testing::WithParamInterface<TestParams> {};
+
+// Tests third order soundfield rotation against the x, y and z axis using long
+// input buffers (>> slerp interval).
+TEST_P(HoaAxesRotationTest, CompareWithExpectedAngleLongBuffer) {
+ const WorldPosition& rotation_axis = ::testing::get<0>(GetParam());
+ const SphericalAngle& expected_angle = ::testing::get<1>(GetParam());
+ const size_t kLongFramesPerBuffer = 512;
+ const std::vector<float> kLongInputData(kLongFramesPerBuffer, 1.0f);
+ CompareRotatedAndReferenceSoundfields(kLongInputData, kAngleDegrees,
+ rotation_axis, expected_angle);
+}
+
+// Tests third order soundfield rotation against the x, y and z axes using short
+// input buffers (< slerp interval).
+TEST_P(HoaAxesRotationTest, CompareWithExpectedAngleShortBuffer) {
+ const WorldPosition& rotation_axis = ::testing::get<0>(GetParam());
+ const SphericalAngle& expected_angle = ::testing::get<1>(GetParam());
+ const size_t kShortFramesPerBuffer = kSlerpFrameInterval / 2;
+ const std::vector<float> kShortInputData(kShortFramesPerBuffer, 1.0f);
+ CompareRotatedAndReferenceSoundfields(kShortInputData, kAngleDegrees,
+ rotation_axis, expected_angle);
+}
+
+// Tests third order soundfield rotation against the x, y and z axes using
+// buffer sizes that are (> slerp interval) and not integer multiples of
+// the slerp interval.
+TEST_P(HoaAxesRotationTest, CompareWithExpectedAngleOddBufferSizes) {
+ const WorldPosition& rotation_axis = ::testing::get<0>(GetParam());
+ const SphericalAngle& expected_angle = ::testing::get<1>(GetParam());
+ const size_t frames_per_buffer = kSlerpFrameInterval + 3U;
+ const std::vector<float> kShortInputData(frames_per_buffer, 1.0f);
+ CompareRotatedAndReferenceSoundfields(kShortInputData, kAngleDegrees,
+ rotation_axis, expected_angle);
+}
+
+INSTANTIATE_TEST_CASE_P(
+ TestParameters, HoaAxesRotationTest,
+ Values(TestParams({1.0f, 0.0f, 0.0f}, kXrotatedSourceAngle),
+ TestParams({0.0f, 1.0f, 0.0f}, kYrotatedSourceAngle),
+ TestParams({0.0f, 0.0f, 1.0f}, kZrotatedSourceAngle)));
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/stereo_from_soundfield_converter.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/stereo_from_soundfield_converter.cc
new file mode 100644
index 000000000..94462a445
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/stereo_from_soundfield_converter.cc
@@ -0,0 +1,52 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/stereo_from_soundfield_converter.h"
+
+#include "base/constants_and_types.h"
+#include "dsp/gain.h"
+
+namespace vraudio {
+
+namespace {
+
+const float kMidSideChannelGain = 0.5f;
+
+} // namespace
+
+void StereoFromSoundfield(const AudioBuffer& soundfield_input,
+ AudioBuffer* stereo_output) {
+ DCHECK(stereo_output);
+ DCHECK_EQ(kNumStereoChannels, stereo_output->num_channels());
+ DCHECK_EQ(soundfield_input.num_frames(), stereo_output->num_frames());
+ DCHECK_GE(soundfield_input.num_channels(), kNumFirstOrderAmbisonicChannels);
+ const AudioBuffer::Channel& channel_audio_space_w = soundfield_input[0];
+ const AudioBuffer::Channel& channel_audio_space_y = soundfield_input[1];
+ AudioBuffer::Channel* left_channel_output = &(*stereo_output)[0];
+ AudioBuffer::Channel* right_channel_output = &(*stereo_output)[1];
+ // Left = 0.5 * (Mid + Side).
+ *left_channel_output = channel_audio_space_w;
+ *left_channel_output += channel_audio_space_y;
+ ConstantGain(0 /* no offset */, kMidSideChannelGain, *left_channel_output,
+ left_channel_output, false /* accumulate_output */);
+ // Right = 0.5 * (Mid - Side).
+ *right_channel_output = channel_audio_space_w;
+ *right_channel_output -= channel_audio_space_y;
+ ConstantGain(0 /* no offset */, kMidSideChannelGain, *right_channel_output,
+ right_channel_output, false /* accumulate_output */);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/stereo_from_soundfield_converter.h b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/stereo_from_soundfield_converter.h
new file mode 100644
index 000000000..44d2144e7
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/stereo_from_soundfield_converter.h
@@ -0,0 +1,35 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_AMBISONICS_STEREO_FROM_SOUNDFIELD_CONVERTER_H_
+#define RESONANCE_AUDIO_AMBISONICS_STEREO_FROM_SOUNDFIELD_CONVERTER_H_
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Performs Ambisonic to stereo decode by performing Mid-Side (M-S) matrixing.
+// Soundfield format assumed is Ambix (ACN/SN3D). Only first order channels
+// are used for the stereo decode.
+//
+// @param soundfield_input Soundfield of arbitrary order.
+// @param stereo_output Pointer to stereo output buffer.
+void StereoFromSoundfield(const AudioBuffer& soundfield_input,
+ AudioBuffer* stereo_output);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_AMBISONICS_STEREO_FROM_SOUNDFIELD_CONVERTER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/stereo_from_soundfield_converter_test.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/stereo_from_soundfield_converter_test.cc
new file mode 100644
index 000000000..aa1f5c70a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/stereo_from_soundfield_converter_test.cc
@@ -0,0 +1,103 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/stereo_from_soundfield_converter.h"
+
+#include <iterator>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "utils/planar_interleaved_conversion.h"
+
+namespace vraudio {
+
+namespace {
+
+// Number of frames per buffer.
+const size_t kFramesPerBuffer = 2;
+
+// First order ambisonic signal in the AmbiX format (W, Y, Z, X), sound source
+// in the front.
+const float kFirstOrderSourceFront[] = {1.0f, 0.0f, 0.0f, 1.0f,
+ 1.0f, 0.0f, 0.0f, 1.0f};
+
+// First order ambisonic signal in the AmbiX format (W, Y, Z, X), sound source
+// to the left.
+const float kFirstOrderSourceLeft[] = {1.0f, 1.0f, 0.0f, 0.0f,
+ 1.0f, 1.0f, 0.0f, 0.0f};
+
+// Tests whether the conversion to stereo from soundfield results in equal
+// signal in both L/R output channels when there is a single source in front of
+// the soundfield.
+TEST(StereoFromSoundfieldConverterTest, StereoFromSoundfieldFrontTest) {
+ // Initialize the soundfield input buffer.
+ const std::vector<float> soundfield_data(std::begin(kFirstOrderSourceFront),
+ std::end(kFirstOrderSourceFront));
+ AudioBuffer soundfield_buffer(
+ kNumFirstOrderAmbisonicChannels,
+ soundfield_data.size() / kNumFirstOrderAmbisonicChannels);
+ FillAudioBuffer(soundfield_data, kNumFirstOrderAmbisonicChannels,
+ &soundfield_buffer);
+
+ // Output buffer is stereo.
+ AudioBuffer output(kNumStereoChannels,
+ soundfield_data.size() / kNumFirstOrderAmbisonicChannels);
+
+ StereoFromSoundfield(soundfield_buffer, &output);
+
+ // Test for near equality.
+ ASSERT_EQ(kNumStereoChannels, output.num_channels());
+ const AudioBuffer::Channel& output_channel_left = output[0];
+ const AudioBuffer::Channel& output_channel_right = output[1];
+ for (size_t frame = 0; frame < kFramesPerBuffer; ++frame) {
+ EXPECT_NEAR(output_channel_left[frame], output_channel_right[frame],
+ kEpsilonFloat);
+ }
+}
+
+// Tests whether the conversion to stereo from soundfield, when the sound source
+// in the soundfield is to the left, results in a signal only in the L output
+// channel.
+TEST(StereoFromSoundfieldConverterTest, StereoFromSoundfieldLeftTest) {
+ // Initialize the soundfield input buffer.
+ const std::vector<float> soundfield_data(std::begin(kFirstOrderSourceLeft),
+ std::end(kFirstOrderSourceLeft));
+ AudioBuffer soundfield_buffer(
+ kNumFirstOrderAmbisonicChannels,
+ soundfield_data.size() / kNumFirstOrderAmbisonicChannels);
+ FillAudioBuffer(soundfield_data, kNumFirstOrderAmbisonicChannels,
+ &soundfield_buffer);
+
+ // Output buffer is stereo.
+ AudioBuffer output(kNumStereoChannels,
+ soundfield_data.size() / kNumFirstOrderAmbisonicChannels);
+
+ StereoFromSoundfield(soundfield_buffer, &output);
+
+ // Test for near equality.
+ ASSERT_EQ(kNumStereoChannels, output.num_channels());
+ const AudioBuffer::Channel& output_channel_left = output[0];
+ const AudioBuffer::Channel& output_channel_right = output[1];
+ for (size_t frame = 0; frame < kFramesPerBuffer; ++frame) {
+ EXPECT_NEAR(output_channel_left[frame], 1.0f, kEpsilonFloat);
+ EXPECT_NEAR(output_channel_right[frame], 0.0f, kEpsilonFloat);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/utils.h b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/utils.h
new file mode 100644
index 000000000..8c71944d6
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/utils.h
@@ -0,0 +1,120 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_AMBISONICS_UTILS_H_
+#define RESONANCE_AUDIO_AMBISONICS_UTILS_H_
+
+#include <cmath>
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+// Returns ACN channel sequence from a degree and order of a spherical harmonic.
+inline int AcnSequence(int degree, int order) {
+ DCHECK_GE(degree, 0);
+ DCHECK_LE(-degree, order);
+ DCHECK_LE(order, degree);
+
+ return degree * degree + degree + order;
+}
+
+// Returns normalization factor for Schmidt semi-normalized spherical harmonics
+// used in AmbiX.
+inline float Sn3dNormalization(int degree, int order) {
+ DCHECK_GE(degree, 0);
+ DCHECK_LE(-degree, order);
+ DCHECK_LE(order, degree);
+ return std::sqrt((2.0f - ((order == 0) ? 1.0f : 0.0f)) *
+ Factorial(degree - std::abs(order)) /
+ Factorial(degree + std::abs(order)));
+}
+
+// Returns the number of spherical harmonics for a periphonic ambisonic sound
+// field of |ambisonic_order| at compile-time.
+// We have to use template metaprogramming because MSVC12 doesn't support
+// constexpr.
+template <size_t AmbisonicOrder>
+struct GetNumPeriphonicComponentsStatic {
+ enum { value = (AmbisonicOrder + 1) * (AmbisonicOrder + 1) };
+};
+
+// Returns the number of spherical harmonics for a periphonic ambisonic sound
+// field of |ambisonic_order|.
+inline size_t GetNumPeriphonicComponents(int ambisonic_order) {
+ return static_cast<size_t>((ambisonic_order + 1) * (ambisonic_order + 1));
+}
+
+// Returns the number of periphonic spherical harmonics (SHs) for a particular
+// Ambisonic order. E.g. number of 1st, 2nd or 3rd degree SHs in a 3rd order
+// sound field.
+inline size_t GetNumNthOrderPeriphonicComponents(int ambisonic_order) {
+ if (ambisonic_order == 0) return 1;
+ return static_cast<size_t>(GetNumPeriphonicComponents(ambisonic_order) -
+ GetNumPeriphonicComponents(ambisonic_order - 1));
+}
+
+// Calculates the ambisonic order of a periphonic sound field with the given
+// number of spherical harmonics.
+inline int GetPeriphonicAmbisonicOrder(size_t num_components) {
+ DCHECK_GT(num_components, 0);
+ const int ambisonic_order = static_cast<int>(std::sqrt(num_components)) - 1;
+ // Detect when num_components is not square.
+ DCHECK_EQ((ambisonic_order + 1) * (ambisonic_order + 1),
+ static_cast<int>(num_components));
+ return ambisonic_order;
+}
+
+// Calculates the order of the current spherical harmonic channel as the integer
+// part of a square root of the channel number. Please note, that in Ambisonics
+// the terms 'order' (usually denoted as 'n') and 'degree' (usually denoted as
+// 'm') are used in the opposite meaning as in more traditional maths or physics
+// conventions:
+// [1] C. Nachbar, F. Zotter, E. Deleflie, A. Sontacchi, "AMBIX - A SUGGESTED
+// AMBISONICS FORMAT", Proc. of the 2nd Ambisonics Symposium, June 2-3 2011,
+// Lexington, KY, https://goo.gl/jzt4Yy.
+inline int GetPeriphonicAmbisonicOrderForChannel(size_t channel) {
+ return static_cast<int>(sqrtf(static_cast<float>(channel)));
+}
+
+// Calculates the degree of the current spherical harmonic channel. Please note,
+// that in Ambisonics the terms 'order' (usually denoted as 'n') and 'degree'
+// (usually denoted as 'm') are used in the opposite meaning as in more
+// traditional maths or physics conventions:
+// [1] C. Nachbar, F. Zotter, E. Deleflie, A. Sontacchi, "AMBIX - A SUGGESTED
+// AMBISONICS FORMAT", Proc. of the 2nd Ambisonics Symposium, June 2-3 2011,
+// Lexington, KY, https://goo.gl/jzt4Yy.
+inline int GetPeriphonicAmbisonicDegreeForChannel(size_t channel) {
+ const int order = GetPeriphonicAmbisonicOrderForChannel(channel);
+ return static_cast<int>(channel) - order * (order + 1);
+}
+
+// Returns whether the given |num_channels| corresponds to a valid ambisonic
+// order configuration.
+inline bool IsValidAmbisonicOrder(size_t num_channels) {
+ if (num_channels == 0) {
+ return false;
+ }
+ // Number of channels must be a square number for valid ambisonic orders.
+ const size_t sqrt_num_channels = static_cast<size_t>(std::sqrt(num_channels));
+ return num_channels == sqrt_num_channels * sqrt_num_channels;
+}
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_AMBISONICS_UTILS_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/ambisonics/utils_test.cc b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/utils_test.cc
new file mode 100644
index 000000000..51dfa13d5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/ambisonics/utils_test.cc
@@ -0,0 +1,65 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "ambisonics/utils.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+// Ambisonic Channel Numbers (ACNs) used in the tests.
+const size_t kAcnChannels[] = {0, 1, 4, 8, 16, 32};
+
+// Number of different ACNs to be tested.
+const size_t kNumAcnChannels = sizeof(kAcnChannels) / sizeof(kAcnChannels[0]);
+
+// Tests that the Ambisonic order for a given channel returned by the
+// GetPeriphonicAmbisonicOrderForChannel() method is correct.
+TEST(UtilsTest, GetPeriphonicAmbisonicOrderForChannelTest) {
+ const int kExpectedOrders[] = {0, 1, 2, 2, 4, 5};
+ for (size_t channel = 0; channel < kNumAcnChannels; ++channel) {
+ EXPECT_EQ(kExpectedOrders[channel],
+ GetPeriphonicAmbisonicOrderForChannel(kAcnChannels[channel]));
+ }
+}
+
+// Tests that the Ambisonic degree for a given channel returned by the
+// GetPeriphonicAmbisonicDegreeForChannel() method is correct.
+TEST(UtilsTest, GetPeriphonicAmbisonicDegreeForChannelTest) {
+ const int kExpectedDegrees[] = {0, -1, -2, 2, -4, 2};
+ for (size_t channel = 0; channel < kNumAcnChannels; ++channel) {
+ EXPECT_EQ(kExpectedDegrees[channel],
+ GetPeriphonicAmbisonicDegreeForChannel(kAcnChannels[channel]));
+ }
+}
+
+// Tests that the ambisonic order validation returns the expected results for
+// arbitrary number of channels.
+TEST(UtilsTest, IsValidAmbisonicOrderTest) {
+ for (size_t valid_channel : {1, 4, 9, 16, 25, 36}) {
+ EXPECT_TRUE(IsValidAmbisonicOrder(valid_channel));
+ }
+ for (size_t invalid_channel : {2, 3, 5, 8, 50, 99}) {
+ EXPECT_FALSE(IsValidAmbisonicOrder(invalid_channel));
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/api/binaural_surround_renderer.cc b/src/3rdparty/resonance-audio/resonance_audio/api/binaural_surround_renderer.cc
new file mode 100644
index 000000000..96ed399ae
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/api/binaural_surround_renderer.cc
@@ -0,0 +1,35 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "api/binaural_surround_renderer.h"
+
+#include "base/logging.h"
+#include "graph/binaural_surround_renderer_impl.h"
+
+namespace vraudio {
+
+BinauralSurroundRenderer* BinauralSurroundRenderer::Create(
+ size_t frames_per_buffer, int sample_rate_hz,
+ SurroundFormat surround_format) {
+ std::unique_ptr<BinauralSurroundRendererImpl> renderer(
+ new BinauralSurroundRendererImpl(frames_per_buffer, sample_rate_hz));
+ if (!renderer->Init(surround_format)) {
+ return nullptr;
+ }
+ return renderer.release();
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/api/binaural_surround_renderer.h b/src/3rdparty/resonance-audio/resonance_audio/api/binaural_surround_renderer.h
new file mode 100644
index 000000000..31e8e3b6e
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/api/binaural_surround_renderer.h
@@ -0,0 +1,255 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_API_BINAURAL_SURROUND_RENDERER_H_
+#define RESONANCE_AUDIO_API_BINAURAL_SURROUND_RENDERER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+// Avoid dependency to base/integral_types.h
+typedef int16_t int16;
+
+namespace vraudio {
+
+// Renders virtual surround sound as well as ambisonic soundfields to binaural
+// stereo.
+class BinauralSurroundRenderer {
+ public:
+
+ enum SurroundFormat {
+ // Enables to initialize a yet undefined rendering mode.
+ kInvalid = 0,
+
+ // Binaurally renders a single virtual speaker at 0 degrees in front.
+ kSurroundMono = 1,
+
+ // Binaurally renders virtual stereo speakers at -30 degrees and +30
+ // degrees.
+ kSurroundStereo = 2,
+
+ // Binaurally renders 5.1 surround sound according to the ITU-R BS.775-3
+ // speaker configuration recommendation:
+ // - Left (L) at 30 degrees.
+ // - Right (R) at -30 degrees.
+ // - Center (C) at 0 degrees.
+ // - Low frequency effects (LFE) at front center at 0 degrees.
+ // - Left surround (LS) at 110 degrees.
+ // - Right surround (RS) at -110 degrees.
+ //
+ // The 5.1 channel input layout must matches AAC: L, R, C, LFE, LS, RS.
+ // Note that this differs from the Vorbis/Opus 5.1 channel layout, which
+ // is: L, C, R, LS, RS, LFE.
+ kSurroundFiveDotOne = 3,
+
+ // Binaurally renders 7.1 surround sound according to the ITU-R BS.775-3
+ // speaker configuration recommendation:
+ // - Left (FL) at 30 degrees.
+ // - Right (FR) at -30 degrees.
+ // - Center (C) at 0 degrees.
+ // - Low frequency effects (LFE) at front center at 0 degrees.
+ // - Left surround 1 (LS1) at 90 degrees.
+ // - Right surround 1 (RS1) at -90 degrees.
+ // - Left surround 2 (LS2) at 150 degrees.
+ // - Right surround 2 (LS2) at -150 degrees.
+ //
+ // The 7.1 channel input layout must matches AAC: L, R, C, LFE, LS1, RS1,
+ // LS2, RS2.
+ // Note that this differs from the Vorbis/Opus 7.1 channel layout, which
+ // is: L, C, R, LS1, RS1, LS2, RS2, LFE.
+ kSurroundSevenDotOne = 10,
+
+ // Binaurally renders first-order ambisonics
+ // (AmbiX format: 4 channels, ACN channel ordering, SN3D normalization).
+ kFirstOrderAmbisonics = 4,
+
+ // Binaurally renders second-order ambisonics.
+ // (AmbiX format: 9 channels, ACN channel ordering, SN3D normalization).
+ kSecondOrderAmbisonics = 5,
+
+ // Binaurally renders third-order ambisonics.
+ // (AmbiX format: 16 channels, ACN channel ordering, SN3D normalization).
+ kThirdOrderAmbisonics = 6,
+
+ // Binaurally renders first-order ambisonics with a non-diegetic-stereo
+ // track. The first 4 channels contain ambisonic AmbiX format.
+ // (AmbiX format: 4 channels, ACN channel ordering, SN3D normalization).
+ // Channel 5 to 6 contain non-diegetic-stereo.
+ kFirstOrderAmbisonicsWithNonDiegeticStereo = 7,
+
+ // Binaurally renders second-order ambisonics with a non-diegetic-stereo
+ // track. The first 9 channels contain ambisonic AmbiX format.
+ // (AmbiX format: 9 channels, ACN channel ordering, SN3D normalization).
+ // Channel 10 to 11 contain non-diegetic-stereo.
+ kSecondOrderAmbisonicsWithNonDiegeticStereo = 8,
+
+ // Binaurally renders third-order ambisonics with a non-diegetic-stereo
+ // track. The first 16 channels contain ambisonic AmbiX format.
+ // (AmbiX format: 16 channels, ACN channel ordering, SN3D normalization).
+ // Channel 17 to 18 contain non-diegetic-stereo.
+ kThirdOrderAmbisonicsWithNonDiegeticStereo = 9,
+
+ // Note: Next available value is: 11
+ };
+
+
+ virtual ~BinauralSurroundRenderer() {}
+
+ // Factory method to create a |BinauralSurroundRenderer| instance. Caller must
+ // take ownership of returned instance and destroy it via operator delete.
+ //
+ // @param frames_per_buffer Number of frames in output buffer.
+ // @param sample_rate_hz Sample rate of audio buffers.
+ // @param surround_format Input surround sound format.
+ // @param return |BinauralSurroundRenderer| instance, nullptr if creation
+ // fails.
+ static BinauralSurroundRenderer* Create(size_t frames_per_buffer,
+ int sample_rate_hz,
+ SurroundFormat surround_format);
+
+ // Enables the stereo speaker mode. When activated, it disables HRTF-based
+ // filtering and switches to computationally cheaper stereo-panning. This
+ // helps to avoid HRTF-based coloring effects when stereo speakers are used
+ // and reduces computational complexity when headphone-based HRTF filtering is
+ // not needed. By default the stereo speaker mode is disabled.
+ //
+ // @param enabled Flag to enable stereo speaker mode.
+ virtual void SetStereoSpeakerMode(bool enabled) = 0;
+
+ // Returns the number of frames the input buffer is currently able to consume.
+ //
+ // @return Number of available frames in input buffer.
+ virtual size_t GetNumAvailableFramesInInputBuffer() const = 0;
+
+ // Adds interleaved int16 audio data to the renderer. If enough data has been
+ // provided for an output buffer to be generated then it will be immediately
+ // available via |Get[Interleaved|Planar]StereoOutputBuffer|. The input data
+ // is copied into an internal buffer which allows the caller to re-use the
+ // input buffer immediately. The available space in the internal buffer can be
+ // obtained via |GetAvailableInputSizeSamples|.
+ //
+ // @param input_buffer_ptr Pointer to interleaved input data.
+ // @param num_channels Number of channels in input buffer.
+ // @param num_frames Number of frames in input buffer.
+ // @return The number of consumed frames.
+ virtual size_t AddInterleavedInput(const int16* input_buffer_ptr,
+ size_t num_channels,
+ size_t num_frames) = 0;
+
+ // Adds interleaved floating point audio data to the renderer. If enough data
+ // has been provided for an output buffer to be generated then it will be
+ // immediately available via |Get[Interleaved|Planar]StereoOutputBuffer|. The
+ // input data is copied into an internal buffer which allows the caller to
+ // re-use the input buffer immediately. The available space in the internal
+ // buffer can be obtained via |GetAvailableInputSizeSamples|.
+ //
+ // @param input_buffer_ptr Pointer to interleaved input data.
+ // @param num_channels Number of channels in input buffer.
+ // @param num_frames Number of frames in input buffer.
+ // @return The number of consumed frames.
+ virtual size_t AddInterleavedInput(const float* input_buffer_ptr,
+ size_t num_channels,
+ size_t num_frames) = 0;
+
+ // Adds planar int16 audio data to the renderer. If enough data has
+ // been provided for an output buffer to be generated then it will be
+ // immediately available via |Get[Interleaved|Planar]StereoOutputBuffer|. The
+ // input data is copied into an internal buffer which allows the caller to
+ // re-use the input buffer immediately. The available space in the internal
+ // buffer can be obtained via |GetAvailableInputSizeSamples|.
+ //
+ // @param input_buffer_ptrs Array of pointers to planar channel data.
+ // @param num_channels Number of channels in input buffer.
+ // @param num_frames Number of frames in input buffer.
+ // @return The number of consumed frames.
+ virtual size_t AddPlanarInput(const int16* const* input_buffer_ptrs,
+ size_t num_channels, size_t num_frames) = 0;
+
+ // Adds planar floating point audio data to the renderer. If enough data has
+ // been provided for an output buffer to be generated then it will be
+ // immediately available via |Get[Interleaved|Planar]StereoOutputBuffer|. The
+ // input data is copied into an internal buffer which allows the caller to
+ // re-use the input buffer immediately. The available space in the internal
+ // buffer can be obtained via |GetAvailableInputSizeSamples|.
+ //
+ // @param input_buffer_ptrs Array of pointers to planar channel data.
+ // @param num_channels Number of channels in input buffer.
+ // @param num_frames Number of frames in input buffer.
+ // @return The number of consumed frames.
+ virtual size_t AddPlanarInput(const float* const* input_buffer_ptrs,
+ size_t num_channels, size_t num_frames) = 0;
+
+ // Returns the number of samples available in the output buffer.
+ //
+ // @return Number of available samples in output buffer.
+ virtual size_t GetAvailableFramesInStereoOutputBuffer() const = 0;
+
+ // Gets a processed output buffer in interleaved int16 format.
+ //
+ // @param output_buffer_ptr Pointer to allocated interleaved output buffer.
+ // @param num_frames Size of output buffer in frames.
+ // @return The number of consumed frames.
+ virtual size_t GetInterleavedStereoOutput(int16* output_buffer_ptr,
+ size_t num_frames) = 0;
+
+ // Gets a processed output buffer in interleaved float format.
+ //
+ // @param output_buffer_ptr Pointer to allocated interleaved output buffer.
+ // @param num_frames Size of output buffer in frames.
+ // @return The number of consumed frames.
+ virtual size_t GetInterleavedStereoOutput(float* output_buffer_ptr,
+ size_t num_frames) = 0;
+
+ // Gets a processed output buffer in planar int16 point format.
+ //
+ // @param output_buffer_ptrs Array of pointers to planar channel data.
+ // @param num_frames Number of frames in output buffer.
+ // @return The number of consumed frames.
+ virtual size_t GetPlanarStereoOutput(int16** output_buffer_ptrs,
+ size_t num_frames) = 0;
+
+ // Gets a processed output buffer in planar floating point format.
+ //
+ // @param output_buffer_ptrs Array of pointers to planar channel data.
+ // @param num_frames Number of frames in output buffer.
+ // @return The number of consumed frames.
+ virtual size_t GetPlanarStereoOutput(float** output_buffer_ptrs,
+ size_t num_frames) = 0;
+
+ // Removes all buffered input and processed output buffers from the buffer
+ // queues.
+ virtual void Clear() = 0;
+
+ // Triggers the processing of data that has been input but not yet processed.
+ // Note after calling this method, all processed output must be consumed via
+ // |Get[Interleaved|Planar]StereoOutputBuffer| before adding new input
+ // buffers.
+ //
+ // @return Whether any data was processed.
+ virtual bool TriggerProcessing() = 0;
+
+ // Updates the head rotation.
+ //
+ // @param w W component of quaternion.
+ // @param x X component of quaternion.
+ // @param y Y component of quaternion.
+ // @param z Z component of quaternion.
+ virtual void SetHeadRotation(float w, float x, float y, float z) = 0;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_API_BINAURAL_SURROUND_RENDERER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/api/resonance_audio_api.cc b/src/3rdparty/resonance-audio/resonance_audio/api/resonance_audio_api.cc
new file mode 100644
index 000000000..d7b9ccfdd
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/api/resonance_audio_api.cc
@@ -0,0 +1,30 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "api/resonance_audio_api.h"
+
+#include "base/logging.h"
+#include "graph/resonance_audio_api_impl.h"
+
+namespace vraudio {
+
+extern "C" EXPORT_API ResonanceAudioApi* CreateResonanceAudioApi(
+ size_t num_channels, size_t frames_per_buffer, int sample_rate_hz) {
+ return new ResonanceAudioApiImpl(num_channels, frames_per_buffer,
+ sample_rate_hz);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/api/resonance_audio_api.h b/src/3rdparty/resonance-audio/resonance_audio/api/resonance_audio_api.h
new file mode 100644
index 000000000..f734dbee9
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/api/resonance_audio_api.h
@@ -0,0 +1,416 @@
+
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_API_RESONANCE_AUDIO_API_H_
+#define RESONANCE_AUDIO_API_RESONANCE_AUDIO_API_H_
+
+// EXPORT_API can be used to define the dllimport storage-class attribute.
+#if !defined(EXPORT_API)
+#define EXPORT_API
+#endif
+
+#include <cstddef> // size_t declaration.
+#include <cstdint> // int16_t declaration.
+
+
+typedef int16_t int16;
+
+namespace vraudio {
+
+// Rendering modes define CPU load / rendering quality balances.
+// Note that this struct is C-compatible by design to be used across external
+// C/C++ and C# implementations.
+enum RenderingMode {
+ // Stereo panning, i.e., this disables HRTF-based rendering.
+ kStereoPanning = 0,
+ // HRTF-based rendering using First Order Ambisonics, over a virtual array of
+ // 8 loudspeakers arranged in a cube configuration around the listener's head.
+ kBinauralLowQuality,
+ // HRTF-based rendering using Second Order Ambisonics, over a virtual array of
+ // 12 loudspeakers arranged in a dodecahedral configuration (using faces of
+ // the dodecahedron).
+ kBinauralMediumQuality,
+ // HRTF-based rendering using Third Order Ambisonics, over a virtual array of
+ // 26 loudspeakers arranged in a Lebedev grid: https://goo.gl/DX1wh3.
+ kBinauralHighQuality,
+ // Room effects only rendering. This disables HRTF-based rendering and direct
+ // (dry) output of a sound object. Note that this rendering mode should *not*
+ // be used for general-purpose sound object spatialization, as it will only
+ // render the corresponding room effects of given sound objects without the
+ // direct spatialization.
+ kRoomEffectsOnly,
+};
+
+// Distance rolloff models used for distance attenuation.
+// Note that this enum is C-compatible by design to be used across external
+// C/C++ and C# implementations.
+enum DistanceRolloffModel {
+ // Logarithmic distance rolloff model.
+ kLogarithmic = 0,
+ // Linear distance rolloff model.
+ kLinear,
+ // Distance attenuation value will be explicitly set by the user.
+ kNone,
+};
+
+// Early reflection properties of an acoustic environment.
+// Note that this struct is C-compatible by design to be used across external
+// C/C++ and C# implementations.
+struct ReflectionProperties {
+ // Default constructor initializing all data members to 0.
+ ReflectionProperties()
+ : room_position{0.0f, 0.0f, 0.0f},
+ room_rotation{0.0f, 0.0f, 0.0f, 1.0f},
+ room_dimensions{0.0f, 0.0f, 0.0f},
+ cutoff_frequency(0.0f),
+ coefficients{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
+ gain(0.0f) {}
+
+ // Center position of the shoebox room in world space.
+ float room_position[3];
+
+ // Rotation (quaternion) of the shoebox room in world space.
+ float room_rotation[4];
+
+ // Size of the shoebox shoebox room in world space.
+ float room_dimensions[3];
+
+ // Frequency threshold for low pass filtering (-3dB cuttoff).
+ float cutoff_frequency;
+
+ // Reflection coefficients that are stored in world space as follows:
+ // [0] (-)ive x-axis wall (left)
+ // [1] (+)ive x-axis wall (right)
+ // [2] (-)ive y-axis wall (bottom)
+ // [3] (+)ive y-axis wall (top)
+ // [4] (-)ive z-axis wall (front)
+ // [5] (+)ive z-axis wall (back)
+ float coefficients[6];
+
+ // Uniform reflections gain which is applied to all reflections.
+ float gain;
+};
+
+// Late reverberation properties of an acoustic environment.
+// Note that this struct is C-compatible by design to be used across external
+// C/C++ and C# implementations.
+struct ReverbProperties {
+ // Default constructor initializing all data members to 0.
+ ReverbProperties()
+ : rt60_values{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
+ gain(0.0f) {}
+
+ // RT60's of the reverberation tail at different octave band centre
+ // frequencies in seconds.
+ float rt60_values[9];
+
+ // Reverb gain.
+ float gain;
+};
+
+class ResonanceAudioApi;
+
+// Factory method to create a |ResonanceAudioApi| instance. Caller must
+// take ownership of returned instance and destroy it via operator delete.
+//
+// @param num_channels Number of channels of audio output.
+// @param frames_per_buffer Number of frames per buffer.
+// @param sample_rate_hz System sample rate.
+extern "C" EXPORT_API ResonanceAudioApi* CreateResonanceAudioApi(
+ size_t num_channels, size_t frames_per_buffer, int sample_rate_hz);
+
+// The ResonanceAudioApi library renders high-quality spatial audio. It provides
+// methods to binaurally render virtual sound sources with simulated room
+// acoustics. In addition, it supports decoding and binaural rendering of
+// ambisonic soundfields. Its implementation is single-threaded, thread-safe
+// and non-blocking to be able to process raw PCM audio buffers directly on the
+// audio thread while receiving parameter updates from the main/render thread.
+class ResonanceAudioApi {
+ public:
+ // Sound object / ambisonic source identifier.
+ typedef int SourceId;
+
+ // Invalid source id that can be used to initialize handler variables during
+ // class construction.
+ static const SourceId kInvalidSourceId = -1;
+
+ virtual ~ResonanceAudioApi() {}
+
+ // Renders and outputs an interleaved output buffer in float format.
+ //
+ // @param num_frames Size of output buffer in frames.
+ // @param num_channels Number of channels in output buffer.
+ // @param buffer_ptr Raw float pointer to audio buffer.
+ // @return True if a valid output was successfully rendered, false otherwise.
+ virtual bool FillInterleavedOutputBuffer(size_t num_channels,
+ size_t num_frames,
+ float* buffer_ptr) = 0;
+
+ // Renders and outputs an interleaved output buffer in int16 format.
+ //
+ // @param num_channels Number of channels in output buffer.
+ // @param num_frames Size of output buffer in frames.
+ // @param buffer_ptr Raw int16 pointer to audio buffer.
+ // @return True if a valid output was successfully rendered, false otherwise.
+ virtual bool FillInterleavedOutputBuffer(size_t num_channels,
+ size_t num_frames,
+ int16* buffer_ptr) = 0;
+
+ // Renders and outputs a planar output buffer in float format.
+ //
+ // @param num_frames Size of output buffer in frames.
+ // @param num_channels Number of channels in output buffer.
+ // @param buffer_ptr Pointer to array of raw float pointers to each channel of
+ // the audio buffer.
+ // @return True if a valid output was successfully rendered, false otherwise.
+ virtual bool FillPlanarOutputBuffer(size_t num_channels, size_t num_frames,
+ float* const* buffer_ptr) = 0;
+
+ // Renders and outputs a planar output buffer in int16 format.
+ //
+ // @param num_channels Number of channels in output buffer.
+ // @param num_frames Size of output buffer in frames.
+ // @param buffer_ptr Pointer to array of raw int16 pointers to each channel of
+ // the audio buffer.
+ // @return True if a valid output was successfully rendered, false otherwise.
+ virtual bool FillPlanarOutputBuffer(size_t num_channels, size_t num_frames,
+ int16* const* buffer_ptr) = 0;
+
+ // Sets listener's head position.
+ //
+ // @param x X coordinate of head position in world space.
+ // @param y Y coordinate of head position in world space.
+ // @param z Z coordinate of head position in world space.
+ virtual void SetHeadPosition(float x, float y, float z) = 0;
+
+ // Sets listener's head rotation.
+ //
+ // @param x X component of quaternion.
+ // @param y Y component of quaternion.
+ // @param z Z component of quaternion.
+ // @param w W component of quaternion.
+ virtual void SetHeadRotation(float x, float y, float z, float w) = 0;
+
+ // Sets the master volume of the main audio output.
+ //
+ // @param volume Master volume (linear) in amplitude in range [0, 1] for
+ // attenuation, range [1, inf) for gain boost.
+ virtual void SetMasterVolume(float volume) = 0;
+
+ // Enables the stereo speaker mode. When activated, it disables HRTF-based
+ // filtering and switches to computationally cheaper stereo-panning. This
+ // helps to avoid HRTF-based coloring effects when stereo speakers are used
+ // and reduces computational complexity when headphone-based HRTF filtering is
+ // not needed. By default the stereo speaker mode is disabled. Note that
+ // stereo speaker mode overrides the |enable_hrtf| flag in
+ // |CreateSoundObjectSource|.
+ //
+ // @param enabled Flag to enable stereo speaker mode.
+ virtual void SetStereoSpeakerMode(bool enabled) = 0;
+
+ // Creates an ambisonic source instance.
+ //
+ // @param num_channels Number of input channels.
+ // @return Id of new ambisonic source.
+ virtual SourceId CreateAmbisonicSource(size_t num_channels) = 0;
+
+ // Creates a stereo non-spatialized source instance, which directly plays back
+ // mono or stereo audio.
+ //
+ // @param num_channels Number of input channels.
+ // @return Id of new non-spatialized source.
+ virtual SourceId CreateStereoSource(size_t num_channels) = 0;
+
+ // Creates a sound object source instance.
+ //
+ // @param rendering_mode Rendering mode which governs quality and performance.
+ // @return Id of new sound object source.
+ virtual SourceId CreateSoundObjectSource(RenderingMode rendering_mode) = 0;
+
+ // Destroys source instance.
+ //
+ // @param source_id Id of source to be destroyed.
+ virtual void DestroySource(SourceId id) = 0;
+
+ // Sets the next audio buffer in interleaved float format to a sound source.
+ //
+ // @param source_id Id of sound source.
+ // @param audio_buffer_ptr Pointer to interleaved float audio buffer.
+ // @param num_channels Number of channels in interleaved audio buffer.
+ // @param num_frames Number of frames per channel in interleaved audio buffer.
+ virtual void SetInterleavedBuffer(SourceId source_id,
+ const float* audio_buffer_ptr,
+ size_t num_channels, size_t num_frames) = 0;
+
+ // Sets the next audio buffer in interleaved int16 format to a sound source.
+ //
+ // @param source_id Id of sound source.
+ // @param audio_buffer_ptr Pointer to interleaved int16 audio buffer.
+ // @param num_channels Number of channels in interleaved audio buffer.
+ // @param num_frames Number of frames per channel in interleaved audio buffer.
+ virtual void SetInterleavedBuffer(SourceId source_id,
+ const int16* audio_buffer_ptr,
+ size_t num_channels, size_t num_frames) = 0;
+
+ // Sets the next audio buffer in planar float format to a sound source.
+ //
+ // @param source_id Id of sound source.
+ // @param audio_buffer_ptr Pointer to array of pointers referring to planar
+ // audio buffers for each channel.
+ // @param num_channels Number of planar input audio buffers.
+ // @param num_frames Number of frames per channel.
+ virtual void SetPlanarBuffer(SourceId source_id,
+ const float* const* audio_buffer_ptr,
+ size_t num_channels, size_t num_frames) = 0;
+
+ // Sets the next audio buffer in planar int16 format to a sound source.
+ //
+ // @param source_id Id of sound source.
+ // @param audio_buffer_ptr Pointer to array of pointers referring to planar
+ // audio buffers for each channel.
+ // @param num_channels Number of planar input audio buffers.
+ // @param num_frames Number of frames per channel.
+ virtual void SetPlanarBuffer(SourceId source_id,
+ const int16* const* audio_buffer_ptr,
+ size_t num_channels, size_t num_frames) = 0;
+
+ // Sets the given source's distance attenuation value explicitly. The distance
+ // rolloff model of the source must be set to |DistanceRolloffModel::kNone|
+ // for the set value to take effect.
+ //
+ // @param source_id Id of source.
+ // @param distance_attenuation Distance attenuation value.
+ virtual void SetSourceDistanceAttenuation(SourceId source_id,
+ float distance_attenuation) = 0;
+
+ // Sets the given source's distance attenuation method with minimum and
+ // maximum distances. Maximum distance must be greater than the minimum
+ // distance for the method to be set.
+ //
+ // @param source_id Id of source.
+ // @param rolloff Linear or logarithmic distance rolloff models.
+ // @param min_distance Minimum distance to apply distance attenuation method.
+ // @param max_distance Maximum distance to apply distance attenuation method.
+ virtual void SetSourceDistanceModel(SourceId source_id,
+ DistanceRolloffModel rolloff,
+ float min_distance,
+ float max_distance) = 0;
+
+ // Sets the given source's position. Note that, the given position for an
+ // ambisonic source is only used to determine the corresponding room effects
+ // to be applied.
+ //
+ // @param source_id Id of source.
+ // @param x X coordinate of source position in world space.
+ // @param y Y coordinate of source position in world space.
+ // @param z Z coordinate of source position in world space.
+ virtual void SetSourcePosition(SourceId source_id, float x, float y,
+ float z) = 0;
+
+ // Sets the room effects contribution for the given source.
+ //
+ // @param source_id Id of source.
+ // @param room_effects_gain Linear room effects volume in amplitude in range
+ // [0, 1] for attenuation, range [1, inf) for gain boost.
+ virtual void SetSourceRoomEffectsGain(SourceId source_id,
+ float room_effects_gain) = 0;
+
+ // Sets the given source's rotation.
+ //
+ // @param source_id Id of source.
+ // @param x X component of quaternion.
+ // @param y Y component of quaternion.
+ // @param z Z component of quaternion.
+ // @param w W component of quaternion.
+ virtual void SetSourceRotation(SourceId source_id, float x, float y, float z,
+ float w) = 0;
+
+ // Sets the given source's volume.
+ //
+ // @param source_id Id of source.
+ // @param volume Linear source volume in amplitude in range [0, 1] for
+ // attenuation, range [1, inf) for gain boost.
+ virtual void SetSourceVolume(SourceId source_id, float volume) = 0;
+
+ // Sets the given sound object source's directivity.
+ //
+ // @param sound_object_source_id Id of sound object source.
+ // @param alpha Weighting balance between figure of eight pattern and circular
+ // pattern for source emission in range [0, 1]. A value of 0.5 results in
+ // a cardioid pattern.
+ // @param order Order applied to computed directivity. Higher values will
+ // result in narrower and sharper directivity patterns. Range [1, inf).
+ virtual void SetSoundObjectDirectivity(SourceId sound_object_source_id,
+ float alpha, float order) = 0;
+
+ // Sets the listener's directivity with respect to the given sound object.
+ // This method could be used to simulate an angular rolloff in terms of the
+ // listener's orientation, given the polar pickup pattern with |alpha| and
+ // |order|.
+ //
+ // @param sound_object_source_id Id of sound object source.
+ // @param alpha Weighting balance between figure of eight pattern and circular
+ // pattern for listener's pickup in range [0, 1]. A value of 0.5 results
+ // in a cardioid pattern.
+ // @param order Order applied to computed pickup pattern. Higher values will
+ // result in narrower and sharper pickup patterns. Range [1, inf).
+ virtual void SetSoundObjectListenerDirectivity(
+ SourceId sound_object_source_id, float alpha, float order) = 0;
+
+ // Sets the gain (linear) of the near field effect.
+ //
+ // @param sound_object_source_id Id of sound object source.
+ // @param gain Gain of the near field effect. Range [0, 9] (corresponding to
+ // approx. (-Inf, +20dB]).
+ virtual void SetSoundObjectNearFieldEffectGain(
+ SourceId sound_object_source_id, float gain) = 0;
+
+ // Sets the given sound object source's occlusion intensity.
+ //
+ // @param sound_object_source_id Id of sound object source.
+ // @param intensity Number of occlusions occurred for the object. The value
+ // can be set to fractional for partial occlusions. Range [0, inf).
+ virtual void SetSoundObjectOcclusionIntensity(SourceId sound_object_source_id,
+ float intensity) = 0;
+
+ // Sets the given sound object source's spread.
+ //
+ // @param sound_object_source_id Id of sound object source.
+ // @param spread_deg Spread in degrees.
+ virtual void SetSoundObjectSpread(SourceId sound_object_source_id,
+ float spread_deg) = 0;
+
+ // Turns on/off the reflections and reverberation.
+ virtual void EnableRoomEffects(bool enable) = 0;
+
+ // Sets the early reflection properties of the environment.
+ //
+ // @param reflection_properties Reflection properties.
+ virtual void SetReflectionProperties(
+ const ReflectionProperties& reflection_properties) = 0;
+
+ // Sets the late reverberation properties of the environment.
+ //
+ // @param reverb_properties Reverb properties.
+ virtual void SetReverbProperties(
+ const ReverbProperties& reverb_properties) = 0;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_API_RESONANCE_AUDIO_API_H_
+
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator.h b/src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator.h
new file mode 100644
index 000000000..628ccaa02
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator.h
@@ -0,0 +1,117 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_BASE_ALIGNED_ALLOCATOR_H_
+#define RESONANCE_AUDIO_BASE_ALIGNED_ALLOCATOR_H_
+
+#include <stdlib.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+
+#include "base/simd_utils.h"
+
+
+namespace vraudio {
+
+// Performs static assert checks on the types size and alignment parameters.
+template <size_t TypeSize, size_t Alignment>
+void StaticAlignmentCheck() {
+ const bool alignment_is_power_of_two =
+ !(Alignment == 0) && !(Alignment & (Alignment - 1));
+ static_assert(alignment_is_power_of_two, "Alignment must be power of two");
+
+ const bool type_size_is_power_of_two = !(TypeSize & (TypeSize - 1));
+ static_assert(type_size_is_power_of_two, "Type size must be power of two");
+}
+
+// Returns a pointer to aligned memory.
+template <typename Type, typename SizeType, typename PointerType>
+PointerType AllignedMalloc(SizeType size, SizeType alignment) {
+ const SizeType data_size = size * sizeof(Type);
+ const SizeType offset = alignment - 1 + sizeof(PointerType);
+ void* mem_block_begin = malloc(data_size + offset);
+ if (mem_block_begin == nullptr) {
+ return nullptr;
+ }
+ // Find memory aligned address.
+ void** mem_block_aligned = reinterpret_cast<void**>(
+ ((reinterpret_cast<SizeType>(mem_block_begin) + offset) &
+ (~(alignment - 1))));
+ // Save pointer to original block right before the aligned block.
+ mem_block_aligned[-1] = mem_block_begin;
+ return reinterpret_cast<PointerType>(mem_block_aligned);
+}
+
+// Frees memory that has been aligned with |AllignedMalloc|.
+template <typename PointerType>
+void AllignedFree(PointerType mem_block_aligned) {
+ free(*(reinterpret_cast<void**>(mem_block_aligned) - 1));
+}
+
+// Class that allocates aligned memory. It is derived from std::allocator class
+// to be used with STL containers.
+//
+// @tparam Type Datatype of container to allocate.
+// @tparam Alignment Size of memory alignment.
+template <typename Type, size_t Alignment>
+class AlignedAllocator : public std::allocator<Type> {
+ public:
+ using Pointer = typename std::allocator_traits<std::allocator<Type>>::pointer;
+ using ConstPointer = typename std::allocator_traits<std::allocator<Type>>::const_pointer;
+ using SizeType = typename std::allocator_traits<std::allocator<Type>>::size_type;
+
+ AlignedAllocator() { StaticAlignmentCheck<sizeof(Type), Alignment>(); }
+
+ // Allocates memory for |size| elements and returns a pointer that is aligned
+ // to a multiple to |Alignment|.
+ //
+ // @param size Number of elements to allocate.
+ // @return Returns memory aligned pointer.
+ Pointer allocate(SizeType size) { return allocate(size, nullptr); }
+
+ // Allocates memory for |size| elements and returns a pointer that is aligned
+ // to a multiple to |Alignment|.
+ //
+ // @param size Number of elements to allocate.
+ // @return Returns memory aligned pointer.
+ Pointer allocate(SizeType size, ConstPointer /* hint */) {
+
+ return AllignedMalloc<Type, SizeType, Pointer>(size, Alignment);
+ }
+
+ void deallocate(Pointer mem_block_aligned, size_t size) {
+ AllignedFree<Pointer>(mem_block_aligned);
+ }
+
+ // Copy constructor to support rebind operation (to make MSVC happy).
+ template <typename U>
+ explicit AlignedAllocator<Type, Alignment>(
+ const AlignedAllocator<U, Alignment>& other) {}
+
+ // Rebind is used to allocate container internal variables of type |U|
+ // (which don't need to be aligned).
+ template <typename U>
+ struct rebind {
+ typedef AlignedAllocator<U, Alignment> other;
+ };
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_BASE_ALIGNED_ALLOCATOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator_test.cc b/src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator_test.cc
new file mode 100644
index 000000000..c38b8a2f5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/aligned_allocator_test.cc
@@ -0,0 +1,53 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "base/aligned_allocator.h"
+
+#include <cstddef>
+#include <vector>
+
+#include "base/integral_types.h"
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/simd_utils.h"
+
+using vraudio::AlignedAllocator;
+
+namespace {
+
+// Helper method to test memory alignment.
+template <size_t Alignment>
+void TestAlignedAllocator() {
+ static const size_t kRuns = 1000;
+ for (size_t run = 0; run < kRuns; ++run) {
+ std::vector<float, AlignedAllocator<float, Alignment> > aligned_vector(1);
+ const bool is_aligned =
+ ((reinterpret_cast<size_t>(&aligned_vector[0]) & (Alignment - 1)) == 0);
+ EXPECT_TRUE(is_aligned);
+ }
+}
+
+} // namespace
+
+// Allocates multiple std::vectors using the AlignedAllocator and tests if the
+// allocated memory is aligned.
+TEST(AlignedAlocatorTest, TestAlignment) {
+ TestAlignedAllocator<2>();
+ TestAlignedAllocator<4>();
+ TestAlignedAllocator<16>();
+ TestAlignedAllocator<32>();
+ TestAlignedAllocator<64>();
+}
+
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/audio_buffer.cc b/src/3rdparty/resonance-audio/resonance_audio/base/audio_buffer.cc
new file mode 100644
index 000000000..c2ad61510
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/audio_buffer.cc
@@ -0,0 +1,74 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+AudioBuffer::AudioBuffer() : num_frames_(0), source_id_(kInvalidSourceId) {}
+
+AudioBuffer::AudioBuffer(size_t num_channels, size_t num_frames)
+ : num_frames_(num_frames), source_id_(kInvalidSourceId) {
+
+ InitChannelViews(num_channels);
+}
+
+// Copy assignment from AudioBuffer.
+AudioBuffer& AudioBuffer::operator=(const AudioBuffer& other) {
+ if (this != &other) {
+ num_frames_ = other.num_frames_;
+ source_id_ = other.source_id_;
+ InitChannelViews(other.num_channels());
+ for (size_t i = 0; i < num_channels(); ++i) {
+ channel_views_[i] = other.channel_views_[i];
+ }
+ }
+ return *this;
+}
+
+AudioBuffer::AudioBuffer(AudioBuffer&& other) {
+ num_frames_ = other.num_frames_;
+ other.num_frames_ = 0;
+ data_ = std::move(other.data_);
+ data_size_ = other.data_size_;
+ other.data_size_ = 0;
+ channel_views_ = std::move(other.channel_views_);
+ source_id_ = other.source_id_;
+ other.source_id_ = kInvalidSourceId;
+}
+
+void AudioBuffer::InitChannelViews(size_t num_channels) {
+
+
+ const size_t num_frames_to_next_channel = FindNextAlignedArrayIndex(
+ num_frames_, sizeof(float), kMemoryAlignmentBytes);
+
+ data_size_ = num_channels * num_frames_to_next_channel;
+ data_.resize(data_size_);
+
+ channel_views_.clear();
+ channel_views_.reserve(num_channels);
+
+ float* itr = data_.data();
+
+ for (size_t i = 0; i < num_channels; ++i) {
+ ChannelView new_channel_view(itr, num_frames_);
+ channel_views_.push_back(new_channel_view);
+ itr += num_frames_to_next_channel;
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/audio_buffer.h b/src/3rdparty/resonance-audio/resonance_audio/base/audio_buffer.h
new file mode 100644
index 000000000..c63fd905c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/audio_buffer.h
@@ -0,0 +1,222 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_BASE_AUDIO_BUFFER_H_
+#define RESONANCE_AUDIO_BASE_AUDIO_BUFFER_H_
+
+#include <algorithm>
+#include <memory>
+
+#include "base/integral_types.h"
+#include "base/aligned_allocator.h"
+#include "base/channel_view.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/simd_utils.h"
+
+
+namespace vraudio {
+
+// Audio buffer that manages multi-channel audio data in a planar data format.
+// All channels are sequentially stored within a single consecutive chunk of
+// memory. To access individual channel data, the array subscript operator can
+// be used to obtain a |AudioBuffer::Channel|. Note that the user must guarantee
+// that the AudioBuffer instance lives as long as its channel data is accessed
+// via |AudioBuffer::Channel|s. Note that allocated buffers may *not* be
+// initialized to zero.
+//
+// Examples:
+//
+// // Range-based for-loop over all channels and all samples.
+// AudioBuffer audio_buffer(...)
+// for (AudioBuffer::Channel& channel : audio_buffer) {
+// for (float& sample : channel) {
+// sample *= gain;
+// }
+// }
+//
+// // Range-based for-loop over all channels and array subscripts-based for-loop
+// // to access samples.
+// AudioBuffer audio_buffer(...)
+// for (AudioBuffer::Channel& channel : audio_buffer) {
+// for (size_t i = 0; i < channel.num_frames(); ++i) {
+// channel[i] *= gain;
+// }
+// }
+//
+// // Array subscript-based for-loops over all channels samples.
+// // AudioBuffer audio_buffer(...)
+// for (size_t c=0; c < audio_buffer.num_channels(); ++c) {
+// // First obtain a reference to AudioBuffer::Channel.
+// AudioBuffer::Channel& channel = audio_buffer[c];
+// for (size_t i = 0; i < channel.num_frames(); ++i) {
+// channel[i] *= gain;
+// }
+// }
+//
+// Note do *NOT* use double array subscripts to iterate over multiple samples
+// since it performs a channel iterator lookup for every sample:
+// for (size_t c=0; c < audio_buffer.num_channels(); ++c) {
+// for (size_t i = 0; i < channel.size(); ++i) {
+// audio_buffer[c][i] *= gain; // *BAD*
+// }
+// }
+//
+class AudioBuffer {
+ public:
+ // View on separate audio channel.
+ typedef ChannelView Channel;
+
+ // Allocator class to allocate aligned floats.
+ typedef AlignedAllocator<float, kMemoryAlignmentBytes> FloatAllocator;
+
+ // Allocator class to allocate aligned int16s.
+ typedef AlignedAllocator<int16, kMemoryAlignmentBytes> Int16Allocator;
+
+ // AlignedFloatBuffer for storing audio data.
+ typedef std::vector<float, FloatAllocator> AlignedFloatVector;
+
+ // AlignedInt16Buffer for storing audio data.
+ typedef std::vector<int16, Int16Allocator> AlignedInt16Vector;
+
+ // Default constructor initializes an empty |AudioBuffer|.
+ AudioBuffer();
+
+ // Constructor.
+ //
+ // @param num_channels Number of channels.
+ // @param num_frames Number of frames.
+ AudioBuffer(size_t num_channels, size_t num_frames);
+
+ // Move constructor.
+ AudioBuffer(AudioBuffer&& other);
+
+ // Copy constructor is explicitly deleted to prevent accidental copies.
+ // Use copy assignment operator instead.
+ AudioBuffer(const AudioBuffer& other) = delete;
+
+ // Copy assignment from AudioBuffer.
+ AudioBuffer& operator=(const AudioBuffer& other);
+
+ // Returns the number of audio channels.
+ size_t num_channels() const { return channel_views_.size(); }
+
+ // Returns the number of frames per buffer.
+ size_t num_frames() const { return num_frames_; }
+
+ // Returns this buffer's source id.
+ SourceId source_id() const { return source_id_; }
+
+ // Returns a reference to the selected ChannelView.
+ Channel& operator[](size_t channel) {
+ DCHECK_LT(channel, channel_views_.size());
+ return channel_views_[channel];
+ }
+
+ // Returns a const reference to the selected ChannelView.
+ const Channel& operator[](size_t channel) const {
+ DCHECK_LT(channel, channel_views_.size());
+ return channel_views_[channel];
+ }
+
+ // Copy assignment from std::vector<std::vector<float>>.
+ AudioBuffer& operator=(const std::vector<std::vector<float>>& other) {
+ DCHECK_EQ(other.size(), channel_views_.size());
+ for (size_t channel = 0; channel < channel_views_.size(); ++channel) {
+ channel_views_[channel] = other[channel];
+ }
+ return *this;
+ }
+
+ // += operator
+ AudioBuffer& operator+=(const AudioBuffer& other) {
+ DCHECK_EQ(other.num_channels(), num_channels());
+ DCHECK_EQ(other.num_frames(), num_frames());
+ for (size_t i = 0; i < channel_views_.size(); ++i)
+ channel_views_[i] += other[i];
+
+ return *this;
+ }
+
+ // -= operator
+ AudioBuffer& operator-=(const AudioBuffer& other) {
+ DCHECK_EQ(other.num_channels(), num_channels());
+ DCHECK_EQ(other.num_frames(), num_frames());
+ for (size_t i = 0; i < channel_views_.size(); ++i)
+ channel_views_[i] -= other[i];
+
+ return *this;
+ }
+
+ // Returns an iterator to the ChannelView of the first channel.
+ std::vector<Channel>::iterator begin() { return channel_views_.begin(); }
+
+ // Returns an iterator to the end of the ChannelView vector.
+ std::vector<Channel>::iterator end() { return channel_views_.end(); }
+
+ // Returns a const_iterator to the ChannelView of the first channel.
+ std::vector<Channel>::const_iterator begin() const {
+ return channel_views_.begin();
+ }
+
+ // Returns an const_iterator to the end of the ChannelView vector.
+ std::vector<Channel>::const_iterator end() const {
+ return channel_views_.end();
+ }
+
+ // Fills all channels with zeros and reenables |Channel|s.
+ void Clear() {
+ for (Channel& channel : channel_views_) {
+ channel.SetEnabled(true);
+ channel.Clear();
+ }
+ }
+
+ // Returns the number of allocated frames per |Channel|. Note this may
+ // differ from the actual size of the |Channel| to ensure alignment of all
+ // |Channel|s.
+ size_t GetChannelStride() const {
+ return FindNextAlignedArrayIndex(num_frames_, sizeof(float),
+ kMemoryAlignmentBytes);
+ }
+
+ // Sets the source id of which the buffer belongs to.
+ void set_source_id(SourceId source_id) { source_id_ = source_id; }
+
+ private:
+ // Allocates memory and initializes vector of |ChannelView|s.
+ void InitChannelViews(size_t num_channels);
+
+ // Number of frames per buffer.
+ size_t num_frames_;
+
+ // Audio buffer that sequentially stores multiple audio channels in a planar
+ // format.
+ AlignedFloatVector data_;
+
+ // Size of audio buffer.
+ size_t data_size_;
+
+ // Vector of |AudioBuffer::Channel|s.
+ std::vector<Channel> channel_views_;
+
+ // Id of a source that this buffer belongs to.
+ SourceId source_id_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_BASE_AUDIO_BUFFER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/audio_buffer_test.cc b/src/3rdparty/resonance-audio/resonance_audio/base/audio_buffer_test.cc
new file mode 100644
index 000000000..2c8f3e20b
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/audio_buffer_test.cc
@@ -0,0 +1,144 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "base/audio_buffer.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+// Tests default constructor.
+TEST(AudioBuffer, AudioBufferDefaultConstructor) {
+ AudioBuffer audio_buffer;
+ EXPECT_EQ(audio_buffer.num_channels(), 0U);
+ EXPECT_EQ(audio_buffer.num_frames(), 0U);
+}
+
+// Tests initialization of |AudioBuffer|.
+TEST(AudioBuffer, AudioBufferInitializationTest) {
+ static const size_t kNumChannels = 2;
+ static const size_t kFramesPerBuffer = 16;
+ AudioBuffer audio_buffer(kNumChannels, kFramesPerBuffer);
+
+ EXPECT_EQ(audio_buffer.num_channels(), kNumChannels);
+ EXPECT_EQ(audio_buffer.num_frames(), kFramesPerBuffer);
+ EXPECT_EQ(static_cast<size_t>(audio_buffer.end() - audio_buffer.begin()),
+ kNumChannels);
+
+ // Test range-based for-loop.
+ size_t channel_idx = 0;
+ for (const AudioBuffer::Channel& channel : audio_buffer) {
+ EXPECT_EQ(channel.begin(), audio_buffer[channel_idx].begin());
+ EXPECT_EQ(channel.end(), audio_buffer[channel_idx].end());
+ ++channel_idx;
+ }
+}
+
+// Tests assignment operator from std::vector<std::vector<float>>.
+TEST(AudioBuffer, AudioBufferAssignmentOperator) {
+ const std::vector<std::vector<float>> kTestVector = {
+ {0.0f, 1.0f, 2.0f}, {3.0f, 4.0f, 5.0f}, {6.0f, 7.0f, 8.0f}};
+
+ AudioBuffer audio_buffer(kTestVector.size(), kTestVector[0].size());
+ audio_buffer = kTestVector;
+
+ for (size_t channel = 0; channel < kTestVector.size(); ++channel) {
+ for (size_t frame = 0; frame < kTestVector[0].size(); ++frame) {
+ EXPECT_EQ(audio_buffer[channel][frame], kTestVector[channel][frame]);
+ }
+ }
+}
+
+// Tests move constructor.
+TEST(AudioBuffer, AudioBufferMoveConstructor) {
+ const std::vector<std::vector<float>> kTestVector = {
+ {0.0f, 1.0f, 2.0f}, {3.0f, 4.0f, 5.0f}, {6.0f, 7.0f, 8.0f}};
+
+ AudioBuffer audio_buffer(kTestVector.size(), kTestVector[0].size());
+ audio_buffer = kTestVector;
+ const size_t num_channels = audio_buffer.num_channels();
+ const size_t num_frames = audio_buffer.num_frames();
+
+ AudioBuffer moved_audio_buffer(std::move(audio_buffer));
+ EXPECT_EQ(audio_buffer.num_channels(), 0U);
+ EXPECT_EQ(audio_buffer.num_frames(), 0U);
+ EXPECT_EQ(moved_audio_buffer.num_channels(), num_channels);
+ EXPECT_EQ(moved_audio_buffer.num_frames(), num_frames);
+
+ for (size_t channel = 0; channel < kTestVector.size(); ++channel) {
+ for (size_t frame = 0; frame < kTestVector[0].size(); ++frame) {
+ EXPECT_EQ(moved_audio_buffer[channel][frame],
+ kTestVector[channel][frame]);
+ }
+ }
+}
+
+// Tests memory alignment of each channel buffer. The address if the first
+// element of each channel should be memory aligned.
+TEST(AudioBuffer, TestBufferAlignment) {
+ static const size_t kNumRuns = 100;
+ static const size_t kNumChannels = 16;
+
+ for (size_t run = 0; run < kNumRuns; ++run) {
+ const size_t frames_per_buffer = run + 1;
+ AudioBuffer audio_buffer(kNumChannels, frames_per_buffer);
+ for (size_t channel = 0; channel < kNumChannels; ++channel) {
+ const AudioBuffer::Channel& channel_view = audio_buffer[channel];
+ const bool is_aligned =
+ ((reinterpret_cast<size_t>(&(*channel_view.begin())) &
+ (kMemoryAlignmentBytes - 1)) == 0);
+ EXPECT_TRUE(is_aligned);
+ }
+ }
+}
+
+// Tests Clear method.
+TEST(AudioBuffer, AudioBufferClear) {
+ const std::vector<std::vector<float>> kTestVector = {
+ {0.0f, 1.0f, 2.0f}, {3.0f, 4.0f, 5.0f}, {6.0f, 7.0f, 8.0f}};
+
+ AudioBuffer audio_buffer(kTestVector.size(), kTestVector[0].size());
+ audio_buffer = kTestVector;
+
+ audio_buffer.Clear();
+
+ for (size_t channel = 0; channel < kTestVector.size(); ++channel) {
+ for (size_t frame = 0; frame < kTestVector[0].size(); ++frame) {
+ EXPECT_EQ(0.0f, audio_buffer[channel][frame]);
+ }
+ }
+}
+
+// Tests GetChannelStride method.
+TEST(AudioBuffer, GetChannelStride) {
+ const size_t num_frames_per_alignment = kMemoryAlignmentBytes / sizeof(float);
+ for (size_t num_frames = 1; num_frames < num_frames_per_alignment * 5;
+ ++num_frames) {
+ AudioBuffer buffer(1, num_frames);
+ // Fast way to ceil(frame/num_frames_per_alignment).
+ const size_t expected_num_alignment_blocks =
+ (num_frames + num_frames_per_alignment - 1) / num_frames_per_alignment;
+ EXPECT_EQ(expected_num_alignment_blocks * num_frames_per_alignment,
+ buffer.GetChannelStride());
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/channel_view.cc b/src/3rdparty/resonance-audio/resonance_audio/base/channel_view.cc
new file mode 100644
index 000000000..e3d6fe14a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/channel_view.cc
@@ -0,0 +1,50 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "base/channel_view.h"
+
+#include "base/simd_utils.h"
+
+namespace vraudio {
+
+ChannelView& ChannelView::operator+=(const ChannelView& other) {
+ DCHECK_EQ(other.size(), size_);
+ DCHECK(enabled_);
+ float* this_sample = begin();
+ const float* other_sample = other.begin();
+ AddPointwise(size_, other_sample, this_sample, this_sample);
+ return *this;
+}
+
+ChannelView& ChannelView::operator-=(const ChannelView& other) {
+ DCHECK_EQ(other.size(), size_);
+ DCHECK(enabled_);
+ float* this_sample = begin();
+ const float* other_sample = other.begin();
+ SubtractPointwise(size_, other_sample, this_sample, this_sample);
+ return *this;
+}
+
+ChannelView& ChannelView::operator*=(const ChannelView& other) {
+ DCHECK_EQ(other.size(), size_);
+ DCHECK(enabled_);
+ float* this_sample = begin();
+ const float* other_sample = other.begin();
+ MultiplyPointwise(size_, other_sample, this_sample, this_sample);
+ return *this;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/channel_view.h b/src/3rdparty/resonance-audio/resonance_audio/base/channel_view.h
new file mode 100644
index 000000000..557773a99
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/channel_view.h
@@ -0,0 +1,138 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_BASE_CHANNEL_VIEW_H_
+#define RESONANCE_AUDIO_BASE_CHANNEL_VIEW_H_
+
+#include <algorithm>
+#include <cstring>
+#include <vector>
+
+#include "base/logging.h"
+
+namespace vraudio {
+
+// Provides an interface to a single audio channel in |AudioBuffer|. Note that a
+// |ChannelView| instance does not own the data it is initialized with.
+class ChannelView {
+ public:
+ // Array subscript operator returning a reference.
+ float& operator[](size_t index) {
+ DCHECK(enabled_);
+ DCHECK_LT(index, size_);
+ return *(begin() + index);
+ }
+
+ // Const array subscript operator returning a const reference.
+ const float& operator[](size_t index) const {
+ DCHECK(enabled_);
+ DCHECK_LT(index, size_);
+ return *(begin() + index);
+ }
+
+ // Returns the size of the channel in samples.
+ size_t size() const { return size_; }
+
+ // Returns a float pointer to the begin of the channel data.
+ float* begin() {
+ DCHECK(enabled_);
+ return begin_itr_;
+ }
+
+ // Returns a float pointer to the end of the channel data.
+ float* end() {
+ DCHECK(enabled_);
+ return begin_itr_ + size_;
+ }
+
+ // Returns a const float pointer to the begin of the channel data.
+ const float* begin() const {
+ DCHECK(enabled_);
+ return begin_itr_;
+ }
+
+ // Returns a const float pointer to the end of the channel data.
+ const float* end() const {
+ DCHECK(enabled_);
+ return begin_itr_ + size_;
+ }
+
+ // Copy assignment from float vector.
+ ChannelView& operator=(const std::vector<float>& other) {
+ DCHECK(enabled_);
+ DCHECK_EQ(other.size(), size_);
+ memcpy(begin(), other.data(), sizeof(float) * size_);
+ return *this;
+ }
+
+ // Copy assignment from ChannelView.
+ ChannelView& operator=(const ChannelView& other) {
+ if (this != &other) {
+ DCHECK(enabled_);
+ DCHECK_EQ(other.size(), size_);
+ memcpy(begin(), other.begin(), sizeof(float) * size_);
+ }
+ return *this;
+ }
+
+ // Adds a |ChannelView| to this |ChannelView|.
+ ChannelView& operator+=(const ChannelView& other);
+
+ // Subtracts a |ChannelView| from this |ChannelView|.
+ ChannelView& operator-=(const ChannelView& other);
+
+ // Pointwise multiplies a |ChannelView| with this |Channelview|.
+ ChannelView& operator*=(const ChannelView& other);
+
+ // Fills channel buffer with zeros.
+ void Clear() {
+ DCHECK(enabled_);
+ memset(begin(), 0, sizeof(float) * size_);
+ }
+
+ // Allows for disabling the channel to prevent access to the channel data and
+ // channel iterators. It is used in the |Mixer| class to prevent the copies of
+ // silence |ChannelView|s. Note that |ChannelView| are enabled by default.
+ //
+ // @param enabled True to enable the channel.
+ void SetEnabled(bool enabled) { enabled_ = enabled; }
+
+ // Returns true if |ChannelView| is enabled.
+ //
+ // @return State of |enabled_| flag.
+ bool IsEnabled() const { return enabled_; }
+
+ private:
+ friend class AudioBuffer;
+
+ // Constructor is initialized with a float pointer to the first sample and the
+ // size of chunk of planar channel data.
+ ChannelView(float* begin_itr, size_t size)
+ : begin_itr_(begin_itr), size_(size), enabled_(true) {}
+
+ // Iterator of first and last element in channel.
+ float* const begin_itr_;
+
+ // Channel size.
+ const size_t size_;
+
+ // Flag indicating if the channel is enabled.
+ bool enabled_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_BASE_CHANNEL_VIEW_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/channel_view_test.cc b/src/3rdparty/resonance-audio/resonance_audio/base/channel_view_test.cc
new file mode 100644
index 000000000..24b04dfd9
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/channel_view_test.cc
@@ -0,0 +1,157 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "base/channel_view.h"
+
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+namespace {
+
+const float kTestData[] = {0.0f, 1.0f, 2.0f};
+const size_t kTestDataSize = sizeof(kTestData) / sizeof(float);
+
+typedef std::vector<float> Buffer;
+
+// Tests initialization of |ChannelView| class.
+TEST(ChannelView, InitializationTest) {
+ AudioBuffer test_buffer(1, kTestDataSize);
+ ChannelView& test_buffer_view = test_buffer[0];
+ for (size_t i = 0; i < kTestDataSize; ++i) {
+ test_buffer_view[i] = kTestData[i];
+ }
+
+ EXPECT_EQ(test_buffer.num_frames(), test_buffer_view.size());
+ EXPECT_EQ(&test_buffer[0][0], test_buffer_view.begin());
+ EXPECT_EQ(&test_buffer[0][0] + test_buffer.num_frames(),
+ test_buffer_view.end());
+}
+
+// Tests iterators and array subscript of |ChannelView|.
+TEST(ChannelView, IteratorAndArraySubscriptTest) {
+ AudioBuffer test_buffer(1, kTestDataSize);
+ ChannelView& test_buffer_view = test_buffer[0];
+ for (size_t i = 0; i < kTestDataSize; ++i) {
+ test_buffer_view[i] = kTestData[i];
+ }
+
+ for (size_t i = 0; i < test_buffer_view.size(); i++) {
+ EXPECT_EQ(test_buffer[0][i], test_buffer_view[i]);
+ EXPECT_EQ(kTestData[i], test_buffer_view[i]);
+ }
+
+ // Test range-based for-loops.
+ for (float& sample : test_buffer_view) {
+ sample *= 2.0f;
+ }
+ size_t idx = 0;
+ for (const float& sample : test_buffer_view) {
+ EXPECT_EQ(kTestData[idx] * 2.0f, sample);
+ ++idx;
+ }
+}
+
+// Tests copy-assignment operators.
+TEST(ChannelView, CopyAssignmentTest) {
+ AudioBuffer test_buffer(1, kTestDataSize);
+ ChannelView& test_buffer_view = test_buffer[0];
+ for (size_t i = 0; i < kTestDataSize; ++i) {
+ test_buffer_view[i] = kTestData[i];
+ }
+
+ AudioBuffer target_buffer(1, kTestDataSize);
+ ChannelView& target_vector_view = target_buffer[0];
+ for (size_t i = 0; i < kTestDataSize; ++i) {
+ target_vector_view[i] = -1.0f;
+ }
+
+ // Copy assignment from ChannelView.
+ target_vector_view = test_buffer_view;
+
+ for (size_t i = 0; i < test_buffer_view.size(); i++) {
+ EXPECT_EQ(test_buffer_view[i], target_vector_view[i]);
+ }
+
+ AudioBuffer target2_buffer(1, kTestDataSize);
+ ChannelView& target2_vector_view = target2_buffer[0];
+ for (size_t i = 0; i < kTestDataSize; ++i) {
+ target2_vector_view[i] = -1.0f;
+ }
+
+ // Copy assignment from AudioBuffer channel.
+ target2_vector_view = test_buffer[0];
+
+ for (size_t i = 0; i < test_buffer_view.size(); i++) {
+ EXPECT_EQ(test_buffer[0][i], target2_vector_view[i]);
+ }
+}
+
+// Tests addition-assignment operator.
+TEST(ChannelView, AdditionOperatorTest) {
+ // Here an AudioBuffer is used to ensure that the data is aligned.
+ AudioBuffer test_buffer(1, kTestDataSize);
+ ChannelView& test_buffer_view = test_buffer[0];
+ for (size_t i = 0; i < kTestDataSize; ++i) {
+ test_buffer_view[i] = kTestData[i];
+ }
+
+ // Execute |ChannelView|s addition operator.
+ test_buffer_view += test_buffer_view;
+
+ for (size_t i = 0; i < test_buffer_view.size(); i++) {
+ EXPECT_EQ(kTestData[i] * 2.0f, test_buffer_view[i]);
+ }
+}
+
+// Tests subtraction-assignment operator.
+TEST(ChannelView, SubtractionOperatorTest) {
+ // Here an AudioBuffer is used to ensure that the data is aligned.
+ AudioBuffer test_buffer(1, kTestDataSize);
+ ChannelView& test_buffer_view = test_buffer[0];
+ for (size_t i = 0; i < kTestDataSize; ++i) {
+ test_buffer_view[i] = kTestData[i];
+ }
+
+ // Execute |ChannelView|s subtraction operator.
+ test_buffer_view -= test_buffer_view;
+
+ for (size_t i = 0; i < test_buffer_view.size(); i++) {
+ EXPECT_EQ(0.0f, test_buffer_view[i]);
+ }
+}
+
+// Tests Clear method.
+TEST(ChannelView, ClearTest) {
+ AudioBuffer test_buffer(1, kTestDataSize);
+ ChannelView& test_buffer_view = test_buffer[0];
+ for (size_t i = 0; i < kTestDataSize; ++i) {
+ test_buffer_view[i] = kTestData[i];
+ }
+
+ test_buffer_view.Clear();
+
+ for (const float& sample : test_buffer_view) {
+ EXPECT_EQ(0.0f, sample);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/constants_and_types.h b/src/3rdparty/resonance-audio/resonance_audio/base/constants_and_types.h
new file mode 100644
index 000000000..591c958dd
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/constants_and_types.h
@@ -0,0 +1,176 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_BASE_CONSTANTS_AND_TYPES_H_
+#define RESONANCE_AUDIO_BASE_CONSTANTS_AND_TYPES_H_
+
+#include <cmath>
+#include <string> // for size_t
+
+namespace vraudio {
+
+// Sound object / ambisonic source identifier.
+
+typedef int SourceId;
+
+// Invalid source id that can be used to initialize handler variables during
+// class construction.
+static const SourceId kInvalidSourceId = -1;
+
+
+// Defines memory alignment of audio buffers. Note that not only the first
+// element of the |data_| buffer is memory aligned but also the address of the
+// first elements of the |ChannelView|s.
+const size_t kMemoryAlignmentBytes = 64;
+
+// Maximum Ambisonic order currently supported in vr audio, equivalent to High
+// Quality sound object rendering mode. This number is limited by a) number of
+// HRIR data points used in the binaural renderer; b) size of the lookup table
+// controlling the angular spread of a sound source in the Ambisonic Lookup
+// Table class.
+static const int kMaxSupportedAmbisonicOrder = 3;
+
+// Maximum allowed size of internal buffers.
+const size_t kMaxSupportedNumFrames = 16384;
+
+// Number of mono channels.
+static const size_t kNumMonoChannels = 1;
+
+// Number of stereo channels.
+static const size_t kNumStereoChannels = 2;
+
+// Number of surround 5.1 channels.
+static const size_t kNumSurroundFiveDotOneChannels = 6;
+
+// Number of surround 7.1 channels.
+static const size_t kNumSurroundSevenDotOneChannels = 8;
+
+// Number of first-order ambisonic channels.
+static const size_t kNumFirstOrderAmbisonicChannels = 4;
+
+// Number of second-order ambisonic channels.
+static const size_t kNumSecondOrderAmbisonicChannels = 9;
+
+// Number of third-order ambisonic channels.
+static const size_t kNumThirdOrderAmbisonicChannels = 16;
+
+// Number of first-order ambisonic with non-diegetic stereo channels.
+static const size_t kNumFirstOrderAmbisonicWithNonDiegeticStereoChannels = 6;
+
+// Number of second-order ambisonic with non-diegetic stereo channels.
+static const size_t kNumSecondOrderAmbisonicWithNonDiegeticStereoChannels = 11;
+
+// Number of third-order ambisonic with non-diegetic stereo channels.
+static const size_t kNumThirdOrderAmbisonicWithNonDiegeticStereoChannels = 18;
+
+// Negative 60dB in amplitude.
+static const float kNegative60dbInAmplitude = 0.001f;
+
+// Tolerated error margins for floating points.
+static const double kEpsilonDouble = 1e-6;
+static const float kEpsilonFloat = 1e-6f;
+
+// Inverse square root of two (equivalent to -3dB audio signal attenuation).
+static const float kInverseSqrtTwo = 1.0f / std::sqrt(2.0f);
+
+// Square roots.
+static const float kSqrtTwo = std::sqrt(2.0f);
+static const float kSqrtThree = std::sqrt(3.0f);
+
+// Pi in radians.
+static const float kPi = static_cast<float>(M_PI);
+// Half pi in radians.
+static const float kHalfPi = static_cast<float>(M_PI / 2.0);
+// Two pi in radians.
+static const float kTwoPi = static_cast<float>(2.0 * M_PI);
+
+// Defines conversion factor from degrees to radians.
+static const float kRadiansFromDegrees = static_cast<float>(M_PI / 180.0);
+
+// Defines conversion factor from radians to degrees.
+static const float kDegreesFromRadians = static_cast<float>(180.0 / M_PI);
+
+// The negated natural logarithm of 1000.
+static const float kNegativeLog1000 = -std::log(1000.0f);
+
+// The lowest octave band for computing room effects.
+static const float kLowestOctaveBandHz = 31.25f;
+
+// Number of octave bands in which room effects are computed.
+static const size_t kNumReverbOctaveBands = 9;
+
+// Centers of possible frequency bands up 8 kHz.
+// ------------------------------------
+// Band no. Low Center High [Frequencies in Hz]
+// ------------------------------------
+// 0 22 31.25 44.2
+// 1 44.2 62.5 88.4
+// 2 88.4 125 176.8
+// 3 176.8 250 353.6
+// 4 353.6 500 707.1
+// 5 707.1 1000 1414.2
+// 6 1414.2 2000 2828.4
+// 7 2828.4 4000 5656.9
+// 8 5656.9 8000 11313.7
+//--------------------------------------
+const float kOctaveBandCentres[kNumReverbOctaveBands] = {
+ 31.25f, 62.5f, 125.0f, 250.0f, 500.0f, 1000.0f, 2000.0f, 4000.0f, 8000.0f};
+
+// Number of surfaces in a shoe-box room.
+static const size_t kNumRoomSurfaces = 6;
+
+// Speed of sound in air at 20 degrees Celsius in meters per second.
+// http://www.sengpielaudio.com/calculator-speedsound.htm
+static const float kSpeedOfSound = 343.0f;
+
+// Locations of the stereo virtual loudspeakers in degrees.
+static const float kStereoLeftDegrees = 90.0f;
+static const float kStereoRightDegrees = -90.0f;
+
+// Conversion factor from seconds to milliseconds.
+static const float kMillisecondsFromSeconds = 1000.0f;
+
+// Conversion factor from milliseconds to seconds.
+static const float kSecondsFromMilliseconds = 0.001f;
+
+// Conversion factor from seconds to milliseconds.
+static const double kMicrosecondsFromSeconds = 1e6;
+
+// Conversion factor from milliseconds to seconds.
+static const double kSecondsFromMicroseconds = 1e-6;
+
+// The distance threshold where the near field effect should fade in.
+static const float kNearFieldThreshold = 1.0f;
+
+// Minimum allowed distance of a near field sound source used to cap the allowed
+// energy boost.
+static const float kMinNearFieldDistance = 0.1f;
+
+// Maximum gain applied by Near Field Effect to the mono source signal.
+static const float kMaxNearFieldEffectGain = 9.0f;
+
+// Number of samples across which the gain value should be interpolated for
+// a unit gain change of 1.0f.
+
+static const size_t kUnitRampLength = 2048;
+
+// Rotation quantization which applies in ambisonic soundfield rotators.
+
+static const float kRotationQuantizationRad = 1.0f * kRadiansFromDegrees;
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_BASE_CONSTANTS_AND_TYPES_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/integral_types.h b/src/3rdparty/resonance-audio/resonance_audio/base/integral_types.h
new file mode 100644
index 000000000..7a4d9216e
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/integral_types.h
@@ -0,0 +1,121 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Basic integer type definitions for various platforms
+
+#ifndef BASE_INTEGRAL_TYPES_H_
+#define BASE_INTEGRAL_TYPES_H_
+
+// Standard typedefs
+typedef signed char schar;
+typedef signed char int8;
+typedef short int16;
+typedef int int32;
+#ifdef _MSC_VER
+typedef __int64 int64;
+#else
+typedef long long int64;
+#endif /* _MSC_VER */
+
+// NOTE: unsigned types are DANGEROUS in loops and other arithmetical
+// places. Use the signed types unless your variable represents a bit
+// pattern (eg a hash value) or you really need the extra bit. Do NOT
+// use 'unsigned' to express "this value should always be positive";
+// use assertions for this.
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint32;
+#ifdef _MSC_VER
+typedef unsigned __int64 uint64;
+#else
+typedef unsigned long long uint64;
+#endif /* _MSC_VER */
+
+// A type to represent a Unicode code-point value. As of Unicode 4.0,
+// such values require up to 21 bits.
+// (For type-checking on pointers, make this explicitly signed,
+// and it should always be the signed version of whatever int32 is.)
+typedef signed int char32;
+
+// A type to represent a natural machine word (for e.g. efficiently
+// scanning through memory for checksums or index searching). Don't use
+// this for storing normal integers. Ideally this would be just
+// unsigned int, but our 64-bit architectures use the LP64 model
+// (http://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models), hence
+// their ints are only 32 bits. We want to use the same fundamental
+// type on all archs if possible to preserve *printf() compatability.
+typedef unsigned long uword_t;
+
+// long long macros to be used because gcc and vc++ use different suffixes,
+// and different size specifiers in format strings
+#undef GG_LONGLONG
+#undef GG_ULONGLONG
+#undef GG_LL_FORMAT
+
+#ifdef _MSC_VER /* if Visual C++ */
+
+// VC++ long long suffixes
+#define GG_LONGLONG(x) x##I64
+#define GG_ULONGLONG(x) x##UI64
+
+// Length modifier in printf format string for int64's (e.g. within %d)
+#define GG_LL_FORMAT "I64" // As in printf("%I64d", ...)
+#define GG_LL_FORMAT_W L"I64"
+
+#else /* not Visual C++ */
+
+#define GG_LONGLONG(x) x##LL
+#define GG_ULONGLONG(x) x##ULL
+#define GG_LL_FORMAT "ll" // As in "%lld". Note that "q" is poor form also.
+#define GG_LL_FORMAT_W L"ll"
+
+#endif // _MSC_VER
+
+
+static const uint8 kuint8max = (( uint8) 0xFF);
+static const uint16 kuint16max = ((uint16) 0xFFFF);
+static const uint32 kuint32max = ((uint32) 0xFFFFFFFF);
+static const uint64 kuint64max = ((uint64) GG_LONGLONG(0xFFFFFFFFFFFFFFFF));
+static const int8 kint8min = (( int8) ~0x7F);
+static const int8 kint8max = (( int8) 0x7F);
+static const int16 kint16min = (( int16) ~0x7FFF);
+static const int16 kint16max = (( int16) 0x7FFF);
+static const int32 kint32min = (( int32) ~0x7FFFFFFF);
+static const int32 kint32max = (( int32) 0x7FFFFFFF);
+static const int64 kint64min = (( int64) GG_LONGLONG(~0x7FFFFFFFFFFFFFFF));
+static const int64 kint64max = (( int64) GG_LONGLONG(0x7FFFFFFFFFFFFFFF));
+
+
+typedef uint64 Fprint;
+static const Fprint kIllegalFprint = 0;
+static const Fprint kMaxFprint = GG_ULONGLONG(0xFFFFFFFFFFFFFFFF);
+
+#endif // BASE_INTEGRAL_TYPES_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/logging.h b/src/3rdparty/resonance-audio/resonance_audio/base/logging.h
new file mode 100644
index 000000000..63dd00431
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/logging.h
@@ -0,0 +1,108 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_PLATFORM_LOGGING_H_
+#define RESONANCE_AUDIO_PLATFORM_LOGGING_H_
+
+#include <cstdlib>
+
+#include <cassert>
+#include <iostream>
+#include <sstream>
+
+#undef DCHECK
+#undef DCHECK_EQ
+#undef DCHECK_NE
+#undef DCHECK_LE
+#undef DCHECK_LT
+#undef DCHECK_GE
+#undef DCHECK_GT
+#undef CHECK
+#undef CHECK_EQ
+#undef CHECK_NE
+#undef CHECK_LE
+#undef CHECK_LT
+#undef CHECK_GE
+#undef CHECK_GT
+#undef CHECK_NOTNULL
+#undef LOG
+
+// This class is used to disable logging, while still allowing for log messages
+// to contain '<<' expressions.
+class NullLogger {
+ public:
+ std::ostream& GetStream() {
+ static std::ostream kNullStream(nullptr);
+ return kNullStream;
+ }
+};
+
+// If statement prevents unused variable warnings.
+#define DCHECK(expr) \
+ if (false && (expr)) \
+ ; \
+ else \
+ NullLogger().GetStream()
+#define DCHECK_OP(val1, val2, op) DCHECK((val1)op(val2))
+
+#define DCHECK_EQ(val1, val2) DCHECK_OP((val1), (val2), ==)
+#define DCHECK_NE(val1, val2) DCHECK_OP((val1), (val2), !=)
+#define DCHECK_LE(val1, val2) DCHECK_OP((val1), (val2), <=)
+#define DCHECK_LT(val1, val2) DCHECK_OP((val1), (val2), <)
+#define DCHECK_GE(val1, val2) DCHECK_OP((val1), (val2), >=)
+#define DCHECK_GT(val1, val2) DCHECK_OP((val1), (val2), >)
+
+// This class is used to log to std::cerr.
+class FatalLogger {
+ public:
+ FatalLogger(const char* file, int line) {
+ error_string_ << file << ":" << line << ": ";
+ }
+ ~FatalLogger() {
+ const std::string error_string = error_string_.str();
+ std::cerr << error_string << std::endl;
+ abort();
+ }
+ std::ostream& GetStream() { return error_string_; }
+
+ private:
+ std::ostringstream error_string_;
+};
+
+#define CHECK(condition) \
+ !(condition) ? FatalLogger(__FILE__, __LINE__).GetStream() \
+ : NullLogger().GetStream()
+
+#define CHECK_OP(val1, val2, op) CHECK((val1)op(val2))
+
+#define CHECK_EQ(val1, val2) CHECK_OP((val1), (val2), ==)
+#define CHECK_NE(val1, val2) CHECK_OP((val1), (val2), !=)
+#define CHECK_LE(val1, val2) CHECK_OP((val1), (val2), <=)
+#define CHECK_LT(val1, val2) CHECK_OP((val1), (val2), <)
+#define CHECK_GE(val1, val2) CHECK_OP((val1), (val2), >=)
+#define CHECK_GT(val1, val2) CHECK_OP((val1), (val2), >)
+
+// Helper for CHECK_NOTNULL(), using C++11 perfect forwarding.
+template <typename T>
+T CheckNotNull(T&& t) {
+ assert(t != nullptr);
+ return std::forward<T>(t);
+}
+#define CHECK_NOTNULL(val) CheckNotNull(val)
+
+#define LOG(severity) NullLogger().GetStream()
+
+#endif // RESONANCE_AUDIO_PLATFORM_LOGGING_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/misc_math.cc b/src/3rdparty/resonance-audio/resonance_audio/base/misc_math.cc
new file mode 100644
index 000000000..4de96ba41
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/misc_math.cc
@@ -0,0 +1,94 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+WorldPosition::WorldPosition() { setZero(); }
+
+WorldRotation::WorldRotation() { setIdentity(); }
+
+bool LinearLeastSquareFitting(const std::vector<float>& x_array,
+ const std::vector<float>& y_array, float* slope,
+ float* intercept, float* r_squared) {
+ // The array sizes must agree.
+ if (x_array.size() != y_array.size()) {
+ return false;
+ }
+
+ // At least two points are needed to fit a line.
+ if (x_array.size() < 2) {
+ return false;
+ }
+
+ float x_sum = 0.0f;
+ float y_sum = 0.0f;
+ float x_square_sum = 0.0f;
+ float xy_sum = 0.0f;
+
+ for (size_t i = 0; i < x_array.size(); ++i) {
+ const float x = x_array[i];
+ const float y = y_array[i];
+ x_sum += x;
+ y_sum += y;
+ x_square_sum += x * x;
+ xy_sum += x * y;
+ }
+
+ const float n_inverse = 1.0f / static_cast<float>(x_array.size());
+ const float x_mean = x_sum * n_inverse;
+ const float y_mean = y_sum * n_inverse;
+ const float x_square_mean = x_square_sum * n_inverse;
+ const float xy_mean = xy_sum * n_inverse;
+ const float x_mean_square = x_mean * x_mean;
+
+ // Prevent division by zero, which means a vertical line and the slope is
+ // infinite.
+ if (x_square_mean == x_mean_square) {
+ return false;
+ }
+
+ *slope = (xy_mean - x_mean * y_mean) / (x_square_mean - x_mean_square);
+ *intercept = y_mean - *slope * x_mean;
+
+ // Compute the coefficient of determination.
+ float total_sum_of_squares = 0.0f;
+ float residual_sum_of_squares = 0.0f;
+ for (size_t i = 0; i < x_array.size(); ++i) {
+ const float y_i = y_array[i];
+ total_sum_of_squares += (y_i - y_mean) * (y_i - y_mean);
+ const float y_fit = *slope * x_array[i] + *intercept;
+ residual_sum_of_squares += (y_fit - y_i) * (y_fit - y_i);
+ }
+
+ if (total_sum_of_squares == 0.0f) {
+ if (residual_sum_of_squares == 0.0f) {
+ // A special case where all y's are equal, where the |r_squared| should
+ // be 1.0, and the line is a perfectly horizontal line.
+ *r_squared = 1.0f;
+ return true;
+ } else {
+ // Division by zero.
+ return false;
+ }
+ }
+
+ *r_squared = 1.0f - residual_sum_of_squares / total_sum_of_squares;
+ return true;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/misc_math.h b/src/3rdparty/resonance-audio/resonance_audio/base/misc_math.h
new file mode 100644
index 000000000..5993eb55d
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/misc_math.h
@@ -0,0 +1,385 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_BASE_MISC_MATH_H_
+#define RESONANCE_AUDIO_BASE_MISC_MATH_H_
+
+#ifndef _USE_MATH_DEFINES
+#define _USE_MATH_DEFINES // Enable MSVC math constants (e.g., M_PI).
+#endif // _USE_MATH_DEFINES
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+#include <limits>
+#include <utility>
+#include <vector>
+
+#include "base/integral_types.h"
+#include "Eigen/Dense"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+class WorldPosition : public Eigen::Matrix<float, 3, 1, Eigen::DontAlign> {
+ public:
+ // Inherits all constructors with 1-or-more arguments. Necessary because
+ // MSVC12 doesn't support inheriting constructors.
+ template <typename Arg1, typename... Args>
+ WorldPosition(const Arg1& arg1, Args&&... args)
+ : Matrix(arg1, std::forward<Args>(args)...) {}
+
+ // Constructs a zero vector.
+ WorldPosition();
+
+ // Returns True if other |WorldPosition| differs by at least |kEpsilonFloat|.
+ bool operator!=(const WorldPosition& other) const {
+ return std::abs(this->x() - other.x()) > kEpsilonFloat ||
+ std::abs(this->y() - other.y()) > kEpsilonFloat ||
+ std::abs(this->z() - other.z()) > kEpsilonFloat;
+ }
+};
+
+class WorldRotation : public Eigen::Quaternion<float, Eigen::DontAlign> {
+ public:
+ // Inherits all constructors with 1-or-more arguments. Necessary because
+ // MSVC12 doesn't support inheriting constructors.
+ template <typename Arg1, typename... Args>
+ WorldRotation(const Arg1& arg1, Args&&... args)
+ : Quaternion(arg1, std::forward<Args>(args)...) {}
+
+ // Constructs an identity rotation.
+ WorldRotation();
+
+ // Returns the shortest arc between two |WorldRotation|s in radians.
+ float AngularDifferenceRad(const WorldRotation& other) const {
+ const Quaternion difference = this->inverse() * other;
+ return static_cast<float>(Eigen::AngleAxisf(difference).angle());
+ }
+};
+
+typedef Eigen::AngleAxis<float> AngleAxisf;
+
+typedef WorldPosition AudioPosition;
+
+typedef WorldRotation AudioRotation;
+
+// Converts |world_position| into an equivalent audio space position.
+// The world space follows the typical CG coordinate system convention:
+// Positive x points right, positive y points up, negative z points forward.
+// The audio space follows the ambiX coordinate system convention that is
+// commonly accepted in literature [http://goo.gl/XdYNm9]:
+// Positive x points forward, negative y points right, positive z points up.
+// Positions in both world space and audio space are in meters.
+//
+// @param world_position 3D position in world space.
+// @param audio_position Output 3D position in audio space.
+inline void ConvertAudioFromWorldPosition(const WorldPosition& world_position,
+ AudioPosition* audio_position) {
+ DCHECK(audio_position);
+ (*audio_position)(0) = -world_position[2];
+ (*audio_position)(1) = -world_position[0];
+ (*audio_position)(2) = world_position[1];
+}
+
+// Converts |audio_position| into an equivalent world space position.
+// The world space follows the typical CG coordinate system convention:
+// Positive x points right, positive y points up, negative z points forward.
+// The audio space follows the ambiX coordinate system convention that is
+// commonly accepted in literature [http://goo.gl/XdYNm9]:
+// Positive x points forward, negative y points right, positive z points up.
+// Positions in both world space and audio space are in meters.
+//
+// @param audio_position 3D position in audio space.
+// @param world_position Output 3D position in world space.
+inline void ConvertWorldFromAudioPosition(const AudioPosition& audio_position,
+ AudioPosition* world_position) {
+ DCHECK(world_position);
+ (*world_position)(0) = -audio_position[1];
+ (*world_position)(1) = audio_position[2];
+ (*world_position)(2) = -audio_position[0];
+}
+
+// Converts |world_rotation| into an equivalent audio space rotation.
+// The world space follows the typical CG coordinate system convention:
+// Positive x points right, positive y points up, negative z points forward.
+// The audio space follows the ambiX coordinate system convention that is
+// commonly accepted in literature [http://goo.gl/XdYNm9]:
+// Positive x points forward, negative y points right, positive z points up.
+// Positions in both world space and audio space are in meters.
+//
+// @param world_rotation 3D rotation in world space.
+// @param audio_rotation Output 3D rotation in audio space.
+inline void ConvertAudioFromWorldRotation(const WorldRotation& world_rotation,
+ AudioRotation* audio_rotation) {
+ DCHECK(audio_rotation);
+ audio_rotation->w() = world_rotation.w();
+ audio_rotation->x() = -world_rotation.x();
+ audio_rotation->y() = world_rotation.y();
+ audio_rotation->z() = -world_rotation.z();
+}
+
+// Returns the relative direction vector |from_position| and |to_position| by
+// rotating the relative position vector with respect to |from_rotation|.
+//
+// @param from_position Origin position of the direction.
+// @param from_rotation Origin orientation of the direction.
+// @param to_position Target position of the direction.
+// @param relative_direction Relative direction vector (not normalized).
+inline void GetRelativeDirection(const WorldPosition& from_position,
+ const WorldRotation& from_rotation,
+ const WorldPosition& to_position,
+ WorldPosition* relative_direction) {
+ DCHECK(relative_direction);
+ *relative_direction =
+ from_rotation.conjugate() * (to_position - from_position);
+}
+
+// Returns the closest relative position in an axis-aligned bounding box to the
+// given |relative_position|.
+//
+// @param position Input position relative to the center of the bounding box.
+// @param aabb_dimensions Bounding box dimensions.
+// @return aabb bounded position.
+inline void GetClosestPositionInAabb(const WorldPosition& relative_position,
+ const WorldPosition& aabb_dimensions,
+ WorldPosition* closest_position) {
+ DCHECK(closest_position);
+ const WorldPosition aabb_offset = 0.5f * aabb_dimensions;
+ (*closest_position)[0] =
+ std::min(std::max(relative_position[0], -aabb_offset[0]), aabb_offset[0]);
+ (*closest_position)[1] =
+ std::min(std::max(relative_position[1], -aabb_offset[1]), aabb_offset[1]);
+ (*closest_position)[2] =
+ std::min(std::max(relative_position[2], -aabb_offset[2]), aabb_offset[2]);
+}
+
+// Returns true if given world |position| is in given axis-aligned bounding box.
+//
+// @param position Position to be tested.
+// @param aabb_center Bounding box center.
+// @param aabb_dimensions Bounding box dimensions.
+// @return True if |position| is within bounding box, false otherwise.
+inline bool IsPositionInAabb(const WorldPosition& position,
+ const WorldPosition& aabb_center,
+ const WorldPosition& aabb_dimensions) {
+ return std::abs(position[0] - aabb_center[0]) <= 0.5f * aabb_dimensions[0] &&
+ std::abs(position[1] - aabb_center[1]) <= 0.5f * aabb_dimensions[1] &&
+ std::abs(position[2] - aabb_center[2]) <= 0.5f * aabb_dimensions[2];
+}
+
+// Returns true if an integer overflow occurred during the calculation of
+// x = a * b.
+//
+// @param a First multiplicand.
+// @param b Second multiplicand.
+// @param x Product.
+// @return True if integer overflow occurred, false otherwise.
+template <typename T>
+inline bool DoesIntegerMultiplicationOverflow(T a, T b, T x) {
+ // Detects an integer overflow occurs by inverting the multiplication and
+ // testing for x / a != b.
+ return a == 0 ? false : (x / a != b);
+}
+
+// Returns true if an integer overflow occurred during the calculation of
+// a + b.
+//
+// @param a First summand.
+// @param b Second summand.
+// @return True if integer overflow occurred, false otherwise.
+template <typename T>
+inline bool DoesIntegerAdditionOverflow(T a, T b) {
+ T x = a + b;
+ return x < b;
+}
+
+// Safely converts an int to a size_t.
+//
+// @param i Integer input.
+// @param x Size_t output.
+// @return True if integer overflow occurred, false otherwise.
+inline bool DoesIntSafelyConvertToSizeT(int i, size_t* x) {
+ if (i < 0) {
+ return false;
+ }
+ *x = static_cast<size_t>(i);
+ return true;
+}
+
+// Safely converts a size_t to an int.
+//
+// @param i Size_t input.
+// @param x Integer output.
+// @return True if integer overflow occurred, false otherwise.
+inline bool DoesSizeTSafelyConvertToInt(size_t i, int* x) {
+ if (i > static_cast<size_t>(std::numeric_limits<int>::max())) {
+ return false;
+ }
+ *x = static_cast<int>(i);
+ return true;
+}
+
+// Finds the greatest common divisor between two integer values using the
+// Euclidean algorithm. Always returns a positive integer.
+//
+// @param a First of the two integer values.
+// @param b second of the two integer values.
+// @return The greatest common divisor of the two integer values.
+inline int FindGcd(int a, int b) {
+ a = std::abs(a);
+ b = std::abs(b);
+ int temp_value = 0;
+ while (b != 0) {
+ temp_value = b;
+ b = a % b;
+ a = temp_value;
+ }
+ return a;
+}
+
+// Finds the next power of two from an integer. This method works with values
+// representable by unsigned 32 bit integers.
+//
+// @param input Integer value.
+// @return The next power of two from |input|.
+inline size_t NextPowTwo(size_t input) {
+ // Ensure the value fits in a uint32_t.
+ DCHECK_LT(static_cast<uint64_t>(input),
+ static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()));
+ uint32_t number = static_cast<uint32_t>(--input);
+ number |= number >> 1; // Take care of 2 bit numbers.
+ number |= number >> 2; // Take care of 4 bit numbers.
+ number |= number >> 4; // Take care of 8 bit numbers.
+ number |= number >> 8; // Take care of 16 bit numbers.
+ number |= number >> 16; // Take care of 32 bit numbers.
+ number++;
+ return static_cast<size_t>(number);
+}
+
+// Returns the factorial (!) of x. If x < 0, it returns 0.
+inline float Factorial(int x) {
+ if (x < 0) return 0.0f;
+ float result = 1.0f;
+ for (; x > 0; --x) result *= static_cast<float>(x);
+ return result;
+}
+
+// Returns the double factorial (!!) of x.
+// For odd x: 1 * 3 * 5 * ... * (x - 2) * x
+// For even x: 2 * 4 * 6 * ... * (x - 2) * x
+// If x < 0, it returns 0.
+inline float DoubleFactorial(int x) {
+ if (x < 0) return 0.0f;
+ float result = 1.0f;
+ for (; x > 0; x -= 2) result *= static_cast<float>(x);
+ return result;
+}
+
+// This is a *safe* alternative to std::equal function as a workaround in order
+// to avoid MSVC compiler warning C4996 for unchecked iterators (see
+// https://msdn.microsoft.com/en-us/library/aa985965.aspx).
+// Also note that, an STL equivalent of this function was introduced in C++14 to
+// be replaced with this implementation (see version (5) in
+// http://en.cppreference.com/w/cpp/algorithm/equal).
+template <typename Iterator>
+inline bool EqualSafe(const Iterator& lhs_begin, const Iterator& lhs_end,
+ const Iterator& rhs_begin, const Iterator& rhs_end) {
+ auto lhs_itr = lhs_begin;
+ auto rhs_itr = rhs_begin;
+ while (lhs_itr != lhs_end && rhs_itr != rhs_end) {
+ if (*lhs_itr != *rhs_itr) {
+ return false;
+ }
+ ++lhs_itr;
+ ++rhs_itr;
+ }
+ return lhs_itr == lhs_end && rhs_itr == rhs_end;
+}
+
+// Fast reciprocal of square-root. See: https://goo.gl/fqvstz for details.
+//
+// @param input The number to be inverse rooted.
+// @return An approximation of the reciprocal square root of |input|.
+inline float FastReciprocalSqrt(float input) {
+ const float kThreeHalfs = 1.5f;
+ const uint32_t kMagicNumber = 0x5f3759df;
+
+ // Approximate a logarithm by aliasing to an integer.
+ uint32_t integer;
+ memcpy(&integer, &input, sizeof(float));
+ integer = kMagicNumber - (integer >> 1);
+ float approximation;
+ memcpy(&approximation, &integer, sizeof(float));
+ const float half_input = input * 0.5f;
+ // One iteration of Newton's method.
+ return approximation *
+ (kThreeHalfs - (half_input * approximation * approximation));
+}
+
+// Finds the best-fitting line to a given set of 2D points by minimizing the
+// sum of the squares of the vertical (along y-axis) offsets. The slope and
+// intercept of the fitted line are recorded, as well as the coefficient of
+// determination, which gives the quality of the fitting.
+// See http://mathworld.wolfram.com/LeastSquaresFitting.html for how to compute
+// these values.
+//
+// @param x_array Array of the x coordinates of the points.
+// @param y_array Array of the y coordinates of the points.
+// @param slope Output slope of the fitted line.
+// @param intercept Output slope of the fitted line.
+// @param r_squared Coefficient of determination.
+// @return False if the fitting fails.
+bool LinearLeastSquareFitting(const std::vector<float>& x_array,
+ const std::vector<float>& y_array, float* slope,
+ float* intercept, float* r_squared);
+
+// Computes |base|^|exp|, where |exp| is a *non-negative* integer, with the
+// squared exponentiation (a.k.a double-and-add) method.
+// When T is a floating point type, this has the same semantics as pow(), but
+// is much faster.
+// T can also be any integral type, in which case computations will be
+// performed in the value domain of this integral type, and overflow semantics
+// will be those of T.
+// You can also use any type for which operator*= is defined.
+// See :
+
+// This method is reproduced here so vraudio classes don't need to depend on
+// //util/math/mathutil.h
+//
+// @tparam base Input to the exponent function. Any type for which *= is
+// defined.
+// @param exp Integer exponent, must be greater than or equal to zero.
+// @return |base|^|exp|.
+template <typename T>
+static inline T IntegerPow(T base, int exp) {
+ DCHECK_GE(exp, 0);
+ T result = static_cast<T>(1);
+ while (true) {
+ if (exp & 1) {
+ result *= base;
+ }
+ exp >>= 1;
+ if (!exp) break;
+ base *= base;
+ }
+ return result;
+}
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_BASE_MISC_MATH_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/misc_math_test.cc b/src/3rdparty/resonance-audio/resonance_audio/base/misc_math_test.cc
new file mode 100644
index 000000000..e75c100c6
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/misc_math_test.cc
@@ -0,0 +1,373 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "base/misc_math.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+TEST(MiscMath, WorldPositionEqualityTest) {
+ const WorldPosition kOriginalWorldPosition(0.33f, 0.44f, 0.55f);
+ const WorldPosition kSameWorldPosition = kOriginalWorldPosition;
+ EXPECT_FALSE(kOriginalWorldPosition != kSameWorldPosition);
+}
+
+TEST(MiscMath, WorldPositionInequalityTest) {
+ const WorldPosition kOriginalWorldPosition(0.11f, 0.22f, 0.33f);
+ const std::vector<WorldPosition> kDifferentWorldPositions{
+ {-0.22f, 0.22f, 0.33f}, {0.11f, -0.33f, 0.33f}, {0.11f, 0.22f, -0.22f},
+ {0.11f, 0.33f, -0.44f}, {0.22f, 0.22f, -0.44f}, {0.22f, 0.33f, -0.55f}};
+
+ for (auto& position : kDifferentWorldPositions) {
+ EXPECT_TRUE(kOriginalWorldPosition != position);
+ }
+}
+
+TEST(MiscMath, ConvertAudioFromWorldPositionTest) {
+ static const WorldPosition kWorldPosition(0.5f, -1.2f, 10.f);
+ static const AudioPosition kExpectedAudioPosition(
+ -kWorldPosition[2], -kWorldPosition[0], kWorldPosition[1]);
+ AudioPosition test_position;
+ ConvertAudioFromWorldPosition(kWorldPosition, &test_position);
+
+ EXPECT_TRUE(kExpectedAudioPosition.isApprox(test_position, kEpsilonFloat));
+}
+
+TEST(MiscMath, ConvertWorldFromAudioPositionTest) {
+ static const AudioPosition kAudioPosition(1.0f, 2.0f, -0.2f);
+ static const WorldPosition kExpectedWorldPosition(
+ -kAudioPosition[1], kAudioPosition[2], -kAudioPosition[0]);
+
+ WorldPosition test_position;
+ ConvertWorldFromAudioPosition(kAudioPosition, &test_position);
+
+ EXPECT_TRUE(kExpectedWorldPosition.isApprox(test_position, kEpsilonFloat));
+}
+
+TEST(MiscMath, ConvertAudioFromWorldRotationTest) {
+ static const WorldRotation kWorldRotation(1.0f, 0.5f, -1.2f, 10.f);
+ static const AudioRotation kExpectedAudioRotation(
+ kWorldRotation.w(), -kWorldRotation.x(), kWorldRotation.y(),
+ -kWorldRotation.z());
+ AudioRotation test_rotation;
+ ConvertAudioFromWorldRotation(kWorldRotation, &test_rotation);
+
+ EXPECT_TRUE(kExpectedAudioRotation.isApprox(test_rotation, kEpsilonFloat));
+}
+
+TEST(MiscMath, GetRelativeDirectionTest) {
+ static const WorldPosition kFromPosition(0.0f, 0.0f, 0.0f);
+ static const WorldPosition kFromRotationAxis(0.0f, 0.0f, 1.0f);
+ static const float kFromRotationAngle = static_cast<float>(M_PI / 2.0);
+ WorldRotation kFromRotation =
+ WorldRotation(AngleAxisf(kFromRotationAngle, kFromRotationAxis));
+
+ static const WorldPosition kToPosition(1.0f, 2.0f, 3.0f);
+
+ static const WorldPosition kExpectedRelativeDirection(2.0f, -1.0f, 3.0f);
+ WorldPosition test_relative_direction;
+ GetRelativeDirection(kFromPosition, kFromRotation, kToPosition,
+ &test_relative_direction);
+
+ EXPECT_TRUE(kExpectedRelativeDirection.isApprox(test_relative_direction,
+ kEpsilonFloat));
+}
+
+TEST(MiscMath, GetClosestPositionInAabbInsideTest) {
+ static const WorldPosition kRelativeSourcePosition(0.0f, -0.2f, 0.0f);
+ static const WorldPosition kRoomDimensions(1.0f, 1.0f, 1.0f);
+ static const WorldPosition kExpectedAabbPosition = kRelativeSourcePosition;
+ WorldPosition test_position;
+ GetClosestPositionInAabb(kRelativeSourcePosition, kRoomDimensions,
+ &test_position);
+
+ EXPECT_TRUE(kExpectedAabbPosition.isApprox(test_position, kEpsilonFloat));
+}
+
+TEST(MiscMath, GetClosestPositionInAabbOutsideTest) {
+ static const WorldPosition kRelativeSourcePosition(0.2f, 0.7f, -0.5f);
+ static const WorldPosition kRoomDimensions(1.0f, 1.0f, 1.0f);
+ static const WorldPosition kExpectedAabbPosition(0.2f, 0.5f, -0.5f);
+ WorldPosition test_position;
+ GetClosestPositionInAabb(kRelativeSourcePosition, kRoomDimensions,
+ &test_position);
+
+ EXPECT_TRUE(kExpectedAabbPosition.isApprox(test_position, kEpsilonFloat));
+}
+
+TEST(MiscMath, IsPositionInAabbInsideTest) {
+ static const WorldPosition kSourcePosition(0.5f, 0.3f, 0.2f);
+ static const WorldPosition kRoomPosition(0.5f, 0.5f, 0.5f);
+ static const WorldPosition kRoomDimensions(1.0f, 1.0f, 1.0f);
+
+ EXPECT_TRUE(
+ IsPositionInAabb(kSourcePosition, kRoomPosition, kRoomDimensions));
+}
+
+TEST(MiscMath, IsPositionInAabbOutsideTest) {
+ static const WorldPosition kSourcePosition(0.7f, 1.2f, 0.0f);
+ static const WorldPosition kRoomPosition(0.5f, 0.5f, 0.5f);
+ static const WorldPosition kRoomDimensions(1.0f, 1.0f, 1.0f);
+
+ EXPECT_FALSE(
+ IsPositionInAabb(kSourcePosition, kRoomPosition, kRoomDimensions));
+}
+
+TEST(MiscMath, IntegerMultiplicationOverflowDetection) {
+ static const size_t kMaxValue = std::numeric_limits<size_t>::max();
+ static const size_t kHalfMaxValue = kMaxValue / 2;
+
+ // 2 * 3 == 6 should not lead to an integer overflow.
+ EXPECT_FALSE(DoesIntegerMultiplicationOverflow<size_t>(2, 3, 6));
+
+ EXPECT_FALSE(DoesIntegerMultiplicationOverflow<size_t>(kHalfMaxValue, 2,
+ kHalfMaxValue * 2));
+ EXPECT_TRUE(
+ DoesIntegerMultiplicationOverflow<size_t>(kMaxValue, 2, kMaxValue << 1));
+ EXPECT_FALSE(
+ DoesIntegerMultiplicationOverflow<size_t>(0, kMaxValue, 0 * kMaxValue));
+ EXPECT_FALSE(
+ DoesIntegerMultiplicationOverflow<size_t>(kMaxValue, 0, kMaxValue * 0));
+}
+
+TEST(MiscMath, DoesIntegerAdditionOverflow) {
+ static const size_t kMaxValue = std::numeric_limits<size_t>::max();
+ static const size_t kHalfMaxValue = kMaxValue / 2;
+
+ EXPECT_FALSE(
+ DoesIntegerAdditionOverflow<size_t>(kHalfMaxValue, kHalfMaxValue));
+ EXPECT_TRUE(DoesIntegerAdditionOverflow<size_t>(kMaxValue, kHalfMaxValue));
+ EXPECT_TRUE(DoesIntegerAdditionOverflow<size_t>(1, kMaxValue));
+ EXPECT_FALSE(DoesIntegerAdditionOverflow<size_t>(kMaxValue, 0));
+ EXPECT_FALSE(DoesIntegerAdditionOverflow<size_t>(0, kMaxValue));
+}
+
+TEST(MiscMath, DoesIntSafelyConvertToSizeT) {
+ static const int kMaxIntValue = std::numeric_limits<int>::max();
+ size_t test_size_t;
+ EXPECT_TRUE(DoesIntSafelyConvertToSizeT(kMaxIntValue, &test_size_t));
+ EXPECT_EQ(static_cast<size_t>(kMaxIntValue), test_size_t);
+ EXPECT_TRUE(DoesIntSafelyConvertToSizeT(0, &test_size_t));
+ EXPECT_EQ(0U, test_size_t);
+ EXPECT_FALSE(DoesIntSafelyConvertToSizeT(-1, &test_size_t));
+}
+
+TEST(MiscMath, DoesSizeTSafelyConvertToInt) {
+ static const size_t kMaxIntValue = std::numeric_limits<size_t>::max();
+ int test_int;
+
+ EXPECT_FALSE(DoesSizeTSafelyConvertToInt(kMaxIntValue, &test_int));
+ EXPECT_TRUE(
+ DoesSizeTSafelyConvertToInt(std::numeric_limits<int>::max(), &test_int));
+ EXPECT_EQ(std::numeric_limits<int>::max(), test_int);
+ EXPECT_TRUE(DoesSizeTSafelyConvertToInt(0, &test_int));
+ EXPECT_EQ(0, test_int);
+}
+
+TEST(MiscMath, GreatestCommonDivisorTest) {
+ const std::vector<int> a_values = {2, 10, 3, 5, 48000, 7, -2, 2, -3};
+ const std::vector<int> b_values = {8, 4, 1, 10, 24000, 13, 6, -6, -9};
+ const std::vector<int> expected = {2, 2, 1, 5, 24000, 1, 2, 2, 3};
+
+ for (size_t i = 0; i < expected.size(); ++i) {
+ EXPECT_EQ(expected[i], FindGcd(a_values[i], b_values[i]));
+ }
+}
+
+TEST(MiscMath, NextPowTwoTest) {
+ const std::vector<size_t> inputs = {2, 10, 3, 5, 48000, 7, 23, 32};
+ const std::vector<size_t> expected = {2, 16, 4, 8, 65536, 8, 32, 32};
+
+ for (size_t i = 0; i < inputs.size(); ++i) {
+ EXPECT_EQ(expected[i], NextPowTwo(inputs[i]));
+ }
+}
+
+TEST(MiscMath, EqualSafeEqualArraysTest) {
+ const float kOriginalArray[3] = {0.11f, 0.22f, 0.33f};
+ const float kSameArray[3] = {0.11f, 0.22f, 0.33f};
+
+ EXPECT_TRUE(EqualSafe(std::begin(kOriginalArray), std::end(kOriginalArray),
+ std::begin(kSameArray), std::end(kSameArray)));
+}
+
+TEST(MiscMath, EqualSafeUnequalArraysTest) {
+ const std::vector<float> kOriginalArray{0.11f, 0.22f, 0.33f};
+ const std::vector<std::vector<float>> kDifferentArrays{
+ {-0.22f, 0.22f, 0.33f}, {0.11f, -0.33f, 0.33f}, {0.11f, 0.22f, -0.22f},
+ {0.11f, 0.33f, -0.44f}, {0.22f, 0.22f, -0.44f}, {0.22f, 0.33f, -0.55f}};
+
+ for (auto& array : kDifferentArrays) {
+ EXPECT_FALSE(EqualSafe(std::begin(kOriginalArray), std::end(kOriginalArray),
+ std::begin(array), std::end(array)));
+ }
+}
+
+TEST(MiscMath, FastReciprocalSqrtTest) {
+ const std::vector<float> kNumbers{130.0f, 13.0f, 1.3f,
+ 0.13f, 0.013f, 0.0013f};
+ const float kSqrtEpsilon = 2e-3f;
+ for (auto& number : kNumbers) {
+ const float actual = std::sqrt(number);
+ const float approximate = 1.0f / FastReciprocalSqrt(number);
+ EXPECT_LT(std::abs(actual - approximate) / actual, kSqrtEpsilon);
+ }
+}
+
+TEST(MiscMath, LinearFittingArrayDifferentSizesFails) {
+ const std::vector<float> x_array{1.0f, 2.0f};
+ const std::vector<float> y_array{3.0f, 4.0f, 5.0f};
+ float slope = 0.0f;
+ float intercept = 0.0f;
+ float r_squared = 0.0f;
+ EXPECT_FALSE(LinearLeastSquareFitting(x_array, y_array, &slope, &intercept,
+ &r_squared));
+}
+
+TEST(MiscMath, LinearFittingFewerThanTwoPointsFails) {
+ const std::vector<float> x_array{1.0f};
+ const std::vector<float> y_array{2.0f};
+ float slope = 0.0f;
+ float intercept = 0.0f;
+ float r_squared = 0.0f;
+ EXPECT_FALSE(LinearLeastSquareFitting(x_array, y_array, &slope, &intercept,
+ &r_squared));
+}
+
+TEST(MiscMath, LinearFittingVerticalLineFails) {
+ // All points line up on the y-axis.
+ const std::vector<float> x_array{0.0f, 0.0f, 0.0f};
+ const std::vector<float> y_array{1.0f, 2.0f, 3.0f};
+ float slope = 0.0f;
+ float intercept = 0.0f;
+ float r_squared = 0.0f;
+ EXPECT_FALSE(LinearLeastSquareFitting(x_array, y_array, &slope, &intercept,
+ &r_squared));
+}
+
+TEST(MiscMath, LinearFittingHorizontalLine) {
+ // All points line up on the x-axis.
+ const std::vector<float> x_array{1.0f, 2.0f, 3.0f};
+ const std::vector<float> y_array{0.0f, 0.0f, 0.0f};
+ float slope = 0.0f;
+ float intercept = 0.0f;
+ float r_squared = 0.0f;
+ EXPECT_TRUE(LinearLeastSquareFitting(x_array, y_array, &slope, &intercept,
+ &r_squared));
+ EXPECT_FLOAT_EQ(slope, 0.0f);
+ EXPECT_FLOAT_EQ(intercept, 0.0f);
+ EXPECT_FLOAT_EQ(r_squared, 1.0f);
+}
+
+TEST(MiscMath, LinearFittingSlopedLine) {
+ // All points line up on the line y = 2.0 x + 1.0.
+ const std::vector<float> x_array{1.0f, 2.0f, 3.0f, 4.0f};
+ const std::vector<float> y_array{3.0f, 5.0f, 7.0f, 9.0f};
+ float slope = 0.0f;
+ float intercept = 0.0f;
+ float r_squared = 0.0f;
+ EXPECT_TRUE(LinearLeastSquareFitting(x_array, y_array, &slope, &intercept,
+ &r_squared));
+ EXPECT_FLOAT_EQ(slope, 2.0f);
+ EXPECT_FLOAT_EQ(intercept, 1.0f);
+ EXPECT_FLOAT_EQ(r_squared, 1.0f);
+}
+
+TEST(MiscMath, LinearFittingSlopedLineWithError) {
+ // All points lie close to the line y = 2.0 x + 1.0 with some offsets.
+ const std::vector<float> x_array{1.002f, 2.001f, 2.998f, 4.003f};
+ const std::vector<float> y_array{3.001f, 4.998f, 7.005f, 8.996f};
+ float slope = 0.0f;
+ float intercept = 0.0f;
+ float r_squared = 0.0f;
+ EXPECT_TRUE(LinearLeastSquareFitting(x_array, y_array, &slope, &intercept,
+ &r_squared));
+
+ // Expect that the fitting is close to the line with some error.
+ const float error_tolerance = 1e-3f;
+ EXPECT_NEAR(slope, 2.0f, error_tolerance);
+ EXPECT_NEAR(intercept, 1.0f, error_tolerance);
+ EXPECT_NEAR(r_squared, 1.0f, error_tolerance);
+}
+
+TEST(MiscMath, LinearFittingUncorrelatedPoints) {
+ // All points evenly distributed on a circle y^2 + x^2 = 1.0, which gives
+ // the worst coefficient of determination (almost zero).
+ const size_t num_points = 20;
+ std::vector<float> x_array(num_points, 0.0f);
+ std::vector<float> y_array(num_points, 0.0f);
+ for (size_t i = 0; i < num_points; ++i) {
+ const float theta =
+ kTwoPi * static_cast<float>(i) / static_cast<float>(num_points);
+ x_array[i] = std::cos(theta);
+ y_array[i] = std::sin(theta);
+ }
+
+ float slope = 0.0f;
+ float intercept = 0.0f;
+ float r_squared = 0.0f;
+ EXPECT_TRUE(LinearLeastSquareFitting(x_array, y_array, &slope, &intercept,
+ &r_squared));
+ EXPECT_FLOAT_EQ(r_squared, 0.0f);
+}
+
+TEST(MiscMath, WorldRotation) {
+ // Test rotation around single quaternion axis.
+ const float kAngularRandomOffsetRad = 0.5f;
+ const float kAngularDifferenceRad = 0.3f;
+ Eigen::AngleAxisf rotation_a(kAngularRandomOffsetRad,
+ Eigen::Vector3f::UnitY());
+ Eigen::AngleAxisf rotation_b(kAngularRandomOffsetRad + kAngularDifferenceRad,
+ Eigen::Vector3f::UnitY());
+ EXPECT_FLOAT_EQ(WorldRotation(rotation_a).AngularDifferenceRad(rotation_b),
+ kAngularDifferenceRad);
+
+ // Test rotation between axis.
+ Eigen::AngleAxisf rotation_c(0.0f, Eigen::Vector3f::UnitY());
+ Eigen::AngleAxisf rotation_d(kPi, Eigen::Vector3f::UnitZ());
+ EXPECT_FLOAT_EQ(WorldRotation(rotation_c).AngularDifferenceRad(rotation_d),
+ kPi);
+}
+
+TEST(MiscMath, IntegerPow) {
+ const float kFloatValue = 1.5f;
+ const float kNegativeFloatValue = -3.3f;
+ const size_t kSizeTValue = 11U;
+ const int kIntValue = 5;
+ const int kNegativeIntValue = -13;
+
+ for (int exponent = 0; exponent < 5; ++exponent) {
+ EXPECT_FLOAT_EQ(IntegerPow(kFloatValue, exponent),
+ std::pow(kFloatValue, static_cast<float>(exponent)));
+ EXPECT_FLOAT_EQ(
+ IntegerPow(kNegativeFloatValue, exponent),
+ std::pow(kNegativeFloatValue, static_cast<float>(exponent)));
+ EXPECT_EQ(IntegerPow(kSizeTValue, exponent),
+ std::pow(kSizeTValue, exponent));
+ EXPECT_EQ(IntegerPow(kIntValue, exponent), std::pow(kIntValue, exponent));
+ EXPECT_EQ(IntegerPow(kNegativeIntValue, exponent),
+ std::pow(kNegativeIntValue, exponent));
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/object_transform.h b/src/3rdparty/resonance-audio/resonance_audio/base/object_transform.h
new file mode 100644
index 000000000..1d43e72a0
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/object_transform.h
@@ -0,0 +1,31 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_BASE_OBJECT_TRANSFORM_H_
+#define RESONANCE_AUDIO_BASE_OBJECT_TRANSFORM_H_
+
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+struct ObjectTransform {
+ WorldPosition position;
+ WorldRotation rotation;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_BASE_OBJECT_TRANSFORM_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/simd_macros.h b/src/3rdparty/resonance-audio/resonance_audio/base/simd_macros.h
new file mode 100644
index 000000000..11ae40bf4
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/simd_macros.h
@@ -0,0 +1,65 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_BASE_SIMD_MACROS_H_
+#define RESONANCE_AUDIO_BASE_SIMD_MACROS_H_
+
+#if !defined(DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || \
+ defined(i386) || defined(_M_IX86))
+// SSE1 is enabled.
+#include <xmmintrin.h>
+typedef __m128 SimdVector;
+#define SIMD_SSE
+#define SIMD_LENGTH 4
+#define SIMD_MULTIPLY(a, b) _mm_mul_ps(a, b)
+#define SIMD_ADD(a, b) _mm_add_ps(a, b)
+#define SIMD_SUB(a, b) _mm_sub_ps(a, b)
+#define SIMD_MULTIPLY_ADD(a, b, c) _mm_add_ps(_mm_mul_ps(a, b), c)
+#define SIMD_SQRT(a) _mm_rcp_ps(_mm_rsqrt_ps(a))
+#define SIMD_RECIPROCAL_SQRT(a) _mm_rsqrt_ps(a)
+#define SIMD_LOAD_ONE_FLOAT(p) _mm_set1_ps(p)
+#elif !defined(DISABLE_SIMD) && \
+ (((defined(__arm__) || defined(__TARGET_ARCH_ARM) || defined(_M_ARM)) && defined(__ARM_NEON__)) || \
+ defined(_M_ARM64) || defined(__aarch64__) || defined(__ARM64__))
+// ARM NEON is enabled.
+#include <arm_neon.h>
+typedef float32x4_t SimdVector;
+#define SIMD_NEON
+#define SIMD_LENGTH 4
+#define SIMD_MULTIPLY(a, b) vmulq_f32(a, b)
+#define SIMD_ADD(a, b) vaddq_f32(a, b)
+#define SIMD_SUB(a, b) vsubq_f32(a, b)
+#define SIMD_MULTIPLY_ADD(a, b, c) vmlaq_f32(c, a, b)
+#define SIMD_SQRT(a) vrecpeq_f32(vrsqrteq_f32(a))
+#define SIMD_RECIPROCAL_SQRT(a) vrsqrteq_f32(a)
+#define SIMD_LOAD_ONE_FLOAT(p) vld1q_dup_f32(&(p))
+#else
+// No SIMD optimizations enabled.
+#include "base/misc_math.h"
+typedef float SimdVector;
+#define SIMD_DISABLED
+#define SIMD_LENGTH 1
+#define SIMD_MULTIPLY(a, b) ((a) * (b))
+#define SIMD_ADD(a, b) ((a) + (b))
+#define SIMD_SUB(a, b) ((a) - (b))
+#define SIMD_MULTIPLY_ADD(a, b, c) ((a) * (b) + (c))
+#define SIMD_SQRT(a) (1.0f / FastReciprocalSqrt(a))
+#define SIMD_RECIPROCAL_SQRT(a) FastReciprocalSqrt(a)
+#define SIMD_LOAD_ONE_FLOAT(p) (p)
+#warning "Not using SIMD optimizations!"
+#endif
+
+#endif // RESONANCE_AUDIO_BASE_SIMD_MACROS_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/simd_utils.cc b/src/3rdparty/resonance-audio/resonance_audio/base/simd_utils.cc
new file mode 100644
index 000000000..309ca7ba8
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/simd_utils.cc
@@ -0,0 +1,1299 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "base/simd_utils.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/misc_math.h"
+#include "base/simd_macros.h"
+
+
+namespace vraudio {
+
+namespace {
+
+#ifdef SIMD_NEON
+// Deinterleaving operates on 8 int16s at a time.
+const size_t kSixteenBitSimdLength = SIMD_LENGTH * 2;
+#endif // SIMD_NEON
+
+// Float format of max and min values storable in an int16_t, for clamping.
+const float kInt16Max = static_cast<float>(0x7FFF);
+const float kInt16Min = static_cast<float>(-0x7FFF);
+
+// Conversion factors between float and int16_t (both directions).
+const float kFloatFromInt16 = 1.0f / kInt16Max;
+const float kInt16FromFloat = kInt16Max;
+
+// Expected SIMD alignment in bytes.
+const size_t kSimdSizeBytes = 16;
+
+inline size_t GetNumChunks(size_t length) { return length / SIMD_LENGTH; }
+
+inline size_t GetLeftoverSamples(size_t length) { return length % SIMD_LENGTH; }
+
+template <typename T>
+inline bool IsAlignedTemplated(const T* pointer) {
+ return reinterpret_cast<uintptr_t>(pointer) % kSimdSizeBytes == 0;
+}
+
+#ifdef SIMD_DISABLED
+// Calculates the approximate complex magnude of z = real + i * imaginary.
+inline void ComplexMagnitude(float real, float imaginary, float* output) {
+ *output = real * real + imaginary * imaginary;
+ // The value of |output| is not being recalculated, simply modified.
+ *output = 1.0f / FastReciprocalSqrt(*output);
+}
+#endif // defined(SIMD_DISABLED)
+
+} // namespace
+
+bool IsAligned(const float* pointer) {
+ return IsAlignedTemplated<float>(pointer);
+}
+
+bool IsAligned(const int16_t* pointer) {
+ return IsAlignedTemplated<int16_t>(pointer);
+}
+
+size_t FindNextAlignedArrayIndex(size_t length, size_t type_size_bytes,
+ size_t memory_alignment_bytes) {
+ const size_t byte_length = type_size_bytes * length;
+ const size_t unaligned_bytes = byte_length % memory_alignment_bytes;
+ const size_t bytes_to_next_aligned =
+ (unaligned_bytes == 0) ? 0 : memory_alignment_bytes - unaligned_bytes;
+ return (byte_length + bytes_to_next_aligned) / type_size_bytes;
+}
+
+void AddPointwise(size_t length, const float* input_a, const float* input_b,
+ float* output) {
+ DCHECK(input_a);
+ DCHECK(input_b);
+ DCHECK(output);
+
+ const SimdVector* input_a_vector =
+ reinterpret_cast<const SimdVector*>(input_a);
+ const SimdVector* input_b_vector =
+ reinterpret_cast<const SimdVector*>(input_b);
+ SimdVector* output_vector = reinterpret_cast<SimdVector*>(output);
+#ifdef SIMD_SSE
+ const size_t num_chunks = GetNumChunks(length);
+ const bool inputs_aligned = IsAligned(input_a) && IsAligned(input_b);
+ const bool output_aligned = IsAligned(output);
+ if (inputs_aligned && output_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ output_vector[i] = SIMD_ADD(input_a_vector[i], input_b_vector[i]);
+ }
+ } else if (inputs_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector output_temp =
+ SIMD_ADD(input_a_vector[i], input_b_vector[i]);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], output_temp);
+ }
+ } else if (output_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_a_temp = _mm_load_ps(&input_a[i * SIMD_LENGTH]);
+ const SimdVector input_b_temp = _mm_load_ps(&input_b[i * SIMD_LENGTH]);
+ output_vector[i] = SIMD_ADD(input_a_temp, input_b_temp);
+ }
+ } else {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_a_temp = _mm_load_ps(&input_a[i * SIMD_LENGTH]);
+ const SimdVector input_b_temp = _mm_load_ps(&input_b[i * SIMD_LENGTH]);
+ const SimdVector output_temp = SIMD_ADD(input_a_temp, input_b_temp);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], output_temp);
+ }
+ }
+#else
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ output_vector[i] = SIMD_ADD(input_a_vector[i], input_b_vector[i]);
+ }
+#endif // SIMD_SSE
+
+ // Add samples at the end that were missed by the SIMD chunking.
+ const size_t leftover_samples = GetLeftoverSamples(length);
+ DCHECK_GE(length, leftover_samples);
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ output[i] = input_a[i] + input_b[i];
+ }
+}
+
+void SubtractPointwise(size_t length, const float* input_a,
+ const float* input_b, float* output) {
+ DCHECK(input_a);
+ DCHECK(input_b);
+ DCHECK(output);
+
+ const SimdVector* input_a_vector =
+ reinterpret_cast<const SimdVector*>(input_a);
+ const SimdVector* input_b_vector =
+ reinterpret_cast<const SimdVector*>(input_b);
+ SimdVector* output_vector = reinterpret_cast<SimdVector*>(output);
+
+#ifdef SIMD_SSE
+ const size_t num_chunks = GetNumChunks(length);
+ const bool inputs_aligned = IsAligned(input_a) && IsAligned(input_b);
+ const bool output_aligned = IsAligned(output);
+ if (inputs_aligned && output_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ output_vector[i] = SIMD_SUB(input_b_vector[i], input_a_vector[i]);
+ }
+ } else if (inputs_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector output_temp =
+ SIMD_SUB(input_b_vector[i], input_a_vector[i]);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], output_temp);
+ }
+ } else if (output_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_a_temp = _mm_load_ps(&input_a[i * SIMD_LENGTH]);
+ const SimdVector input_b_temp = _mm_load_ps(&input_b[i * SIMD_LENGTH]);
+ output_vector[i] = SIMD_SUB(input_b_temp, input_a_temp);
+ }
+ } else {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_a_temp = _mm_load_ps(&input_a[i * SIMD_LENGTH]);
+ const SimdVector input_b_temp = _mm_load_ps(&input_b[i * SIMD_LENGTH]);
+ const SimdVector output_temp = SIMD_SUB(input_b_temp, input_a_temp);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], output_temp);
+ }
+ }
+#else
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ output_vector[i] = SIMD_SUB(input_b_vector[i], input_a_vector[i]);
+ }
+#endif // SIMD_SSE
+
+ // Subtract samples at the end that were missed by the SIMD chunking.
+ const size_t leftover_samples = GetLeftoverSamples(length);
+ DCHECK_GE(length, leftover_samples);
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ output[i] = input_b[i] - input_a[i];
+ }
+}
+
+void MultiplyPointwise(size_t length, const float* input_a,
+ const float* input_b, float* output) {
+ DCHECK(input_a);
+ DCHECK(input_b);
+ DCHECK(output);
+
+ const SimdVector* input_a_vector =
+ reinterpret_cast<const SimdVector*>(input_a);
+ const SimdVector* input_b_vector =
+ reinterpret_cast<const SimdVector*>(input_b);
+ SimdVector* output_vector = reinterpret_cast<SimdVector*>(output);
+
+#ifdef SIMD_SSE
+ const size_t num_chunks = GetNumChunks(length);
+ const bool inputs_aligned = IsAligned(input_a) && IsAligned(input_b);
+ const bool output_aligned = IsAligned(output);
+ if (inputs_aligned && output_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ output_vector[i] = SIMD_MULTIPLY(input_a_vector[i], input_b_vector[i]);
+ }
+ } else if (inputs_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector output_temp =
+ SIMD_MULTIPLY(input_a_vector[i], input_b_vector[i]);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], output_temp);
+ }
+ } else if (output_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_a_temp = _mm_loadu_ps(&input_a[i * SIMD_LENGTH]);
+ const SimdVector input_b_temp = _mm_loadu_ps(&input_b[i * SIMD_LENGTH]);
+ output_vector[i] = SIMD_MULTIPLY(input_a_temp, input_b_temp);
+ }
+ } else {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_a_temp = _mm_loadu_ps(&input_a[i * SIMD_LENGTH]);
+ const SimdVector input_b_temp = _mm_loadu_ps(&input_b[i * SIMD_LENGTH]);
+ const SimdVector output_temp = SIMD_MULTIPLY(input_a_temp, input_b_temp);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], output_temp);
+ }
+ }
+#else
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ output_vector[i] = SIMD_MULTIPLY(input_a_vector[i], input_b_vector[i]);
+ }
+#endif // SIMD_SSE
+
+ // Multiply samples at the end that were missed by the SIMD chunking.
+ const size_t leftover_samples = GetLeftoverSamples(length);
+ DCHECK_GE(length, leftover_samples);
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ output[i] = input_a[i] * input_b[i];
+ }
+}
+
+void MultiplyAndAccumulatePointwise(size_t length, const float* input_a,
+ const float* input_b, float* accumulator) {
+ DCHECK(input_a);
+ DCHECK(input_b);
+ DCHECK(accumulator);
+
+ const SimdVector* input_a_vector =
+ reinterpret_cast<const SimdVector*>(input_a);
+ const SimdVector* input_b_vector =
+ reinterpret_cast<const SimdVector*>(input_b);
+ SimdVector* accumulator_vector = reinterpret_cast<SimdVector*>(accumulator);
+
+#ifdef SIMD_SSE
+ const size_t num_chunks = GetNumChunks(length);
+ const bool inputs_aligned = IsAligned(input_a) && IsAligned(input_b);
+ const bool accumulator_aligned = IsAligned(accumulator);
+ if (inputs_aligned && accumulator_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ accumulator_vector[i] = SIMD_MULTIPLY_ADD(
+ input_a_vector[i], input_b_vector[i], accumulator_vector[i]);
+ }
+ } else if (inputs_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ SimdVector accumulator_temp = _mm_loadu_ps(&accumulator[i * SIMD_LENGTH]);
+ accumulator_temp = SIMD_MULTIPLY_ADD(input_a_vector[i], input_b_vector[i],
+ accumulator_temp);
+ _mm_storeu_ps(&accumulator[i * SIMD_LENGTH], accumulator_temp);
+ }
+ } else if (accumulator_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_a_temp = _mm_loadu_ps(&input_a[i * SIMD_LENGTH]);
+ const SimdVector input_b_temp = _mm_loadu_ps(&input_b[i * SIMD_LENGTH]);
+ accumulator_vector[i] =
+ SIMD_MULTIPLY_ADD(input_a_temp, input_b_temp, accumulator_vector[i]);
+ }
+ } else {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_a_temp = _mm_loadu_ps(&input_a[i * SIMD_LENGTH]);
+ const SimdVector input_b_temp = _mm_loadu_ps(&input_b[i * SIMD_LENGTH]);
+ SimdVector accumulator_temp = _mm_loadu_ps(&accumulator[i * SIMD_LENGTH]);
+ accumulator_temp =
+ SIMD_MULTIPLY_ADD(input_a_temp, input_b_temp, accumulator_temp);
+ _mm_storeu_ps(&accumulator[i * SIMD_LENGTH], accumulator_temp);
+ }
+ }
+#else
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ accumulator_vector[i] = SIMD_MULTIPLY_ADD(
+ input_a_vector[i], input_b_vector[i], accumulator_vector[i]);
+ }
+#endif // SIMD_SSE
+
+ // Apply gain and accumulate to samples at the end that were missed by the
+ // SIMD chunking.
+ const size_t leftover_samples = GetLeftoverSamples(length);
+ DCHECK_GE(length, leftover_samples);
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ accumulator[i] += input_a[i] * input_b[i];
+ }
+}
+
+void ScalarMultiply(size_t length, float gain, const float* input,
+ float* output) {
+ DCHECK(input);
+ DCHECK(output);
+
+ const SimdVector* input_vector = reinterpret_cast<const SimdVector*>(input);
+ SimdVector* output_vector = reinterpret_cast<SimdVector*>(output);
+
+ const SimdVector gain_vector = SIMD_LOAD_ONE_FLOAT(gain);
+#ifdef SIMD_SSE
+ const size_t num_chunks = GetNumChunks(length);
+ const bool input_aligned = IsAligned(input);
+ const bool output_aligned = IsAligned(output);
+ if (input_aligned && output_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ output_vector[i] = SIMD_MULTIPLY(gain_vector, input_vector[i]);
+ }
+ } else if (input_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector output_temp =
+ SIMD_MULTIPLY(gain_vector, input_vector[i]);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], output_temp);
+ }
+ } else if (output_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_temp = _mm_loadu_ps(&input[i * SIMD_LENGTH]);
+ output_vector[i] = SIMD_MULTIPLY(gain_vector, input_temp);
+ }
+ } else {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_temp = _mm_loadu_ps(&input[i * SIMD_LENGTH]);
+ const SimdVector output_temp = SIMD_MULTIPLY(gain_vector, input_temp);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], output_temp);
+ }
+ }
+#else
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ output_vector[i] = SIMD_MULTIPLY(gain_vector, input_vector[i]);
+ }
+#endif // SIMD_SSE
+
+ // Apply gain to samples at the end that were missed by the SIMD chunking.
+ const size_t leftover_samples = GetLeftoverSamples(length);
+ DCHECK_GE(length, leftover_samples);
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ output[i] = input[i] * gain;
+ }
+}
+
+void ScalarMultiplyAndAccumulate(size_t length, float gain, const float* input,
+ float* accumulator) {
+ DCHECK(input);
+ DCHECK(accumulator);
+
+ const SimdVector* input_vector = reinterpret_cast<const SimdVector*>(input);
+ SimdVector* accumulator_vector = reinterpret_cast<SimdVector*>(accumulator);
+
+ const SimdVector gain_vector = SIMD_LOAD_ONE_FLOAT(gain);
+#ifdef SIMD_SSE
+ const size_t num_chunks = GetNumChunks(length);
+ const bool input_aligned = IsAligned(input);
+ const bool accumulator_aligned = IsAligned(accumulator);
+ if (input_aligned && accumulator_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ accumulator_vector[i] = SIMD_MULTIPLY_ADD(gain_vector, input_vector[i],
+ accumulator_vector[i]);
+ }
+ } else if (input_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ SimdVector accumulator_temp = _mm_loadu_ps(&accumulator[i * SIMD_LENGTH]);
+ accumulator_temp =
+ SIMD_MULTIPLY_ADD(gain_vector, input_vector[i], accumulator_temp);
+ _mm_storeu_ps(&accumulator[i * SIMD_LENGTH], accumulator_temp);
+ }
+ } else if (accumulator_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_temp = _mm_loadu_ps(&input[i * SIMD_LENGTH]);
+ accumulator_vector[i] =
+ SIMD_MULTIPLY_ADD(gain_vector, input_temp, accumulator_vector[i]);
+ }
+ } else {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_temp = _mm_loadu_ps(&input[i * SIMD_LENGTH]);
+ SimdVector accumulator_temp = _mm_loadu_ps(&accumulator[i * SIMD_LENGTH]);
+ accumulator_temp =
+ SIMD_MULTIPLY_ADD(gain_vector, input_temp, accumulator_temp);
+ _mm_storeu_ps(&accumulator[i * SIMD_LENGTH], accumulator_temp);
+ }
+ }
+#else
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ accumulator_vector[i] =
+ SIMD_MULTIPLY_ADD(gain_vector, input_vector[i], accumulator_vector[i]);
+ }
+#endif // SIMD_SSE
+
+ // Apply gain and accumulate to samples at the end that were missed by the
+ // SIMD chunking.
+ const size_t leftover_samples = GetLeftoverSamples(length);
+ DCHECK_GE(length, leftover_samples);
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ accumulator[i] += input[i] * gain;
+ }
+}
+
+void ReciprocalSqrt(size_t length, const float* input, float* output) {
+ DCHECK(input);
+ DCHECK(output);
+
+#if !defined(SIMD_DISABLED)
+ const SimdVector* input_vector = reinterpret_cast<const SimdVector*>(input);
+ SimdVector* output_vector = reinterpret_cast<SimdVector*>(output);
+#endif // !defined(SIMD_DISABLED)
+
+#ifdef SIMD_SSE
+ const size_t num_chunks = GetNumChunks(length);
+ const bool input_aligned = IsAligned(input);
+ const bool output_aligned = IsAligned(output);
+ if (input_aligned && output_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ output_vector[i] = SIMD_RECIPROCAL_SQRT(input_vector[i]);
+ }
+ } else if (input_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector output_temp = SIMD_RECIPROCAL_SQRT(input_vector[i]);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], output_temp);
+ }
+ } else if (output_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_temp = _mm_loadu_ps(&input[i * SIMD_LENGTH]);
+ output_vector[i] = SIMD_RECIPROCAL_SQRT(input_temp);
+ }
+ } else {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_temp = _mm_loadu_ps(&input[i * SIMD_LENGTH]);
+ const SimdVector output_temp = SIMD_RECIPROCAL_SQRT(input_temp);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], output_temp);
+ }
+ }
+#elif defined SIMD_NEON
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ output_vector[i] = SIMD_RECIPROCAL_SQRT(input_vector[i]);
+ }
+#endif // SIMD_SSE
+
+ // Apply to samples at the end that were missed by the SIMD chunking.
+ const size_t leftover_samples = GetLeftoverSamples(length);
+ DCHECK_GE(length, leftover_samples);
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ output[i] = FastReciprocalSqrt(input[i]);
+ }
+}
+
+void Sqrt(size_t length, const float* input, float* output) {
+ DCHECK(input);
+ DCHECK(output);
+
+#if !defined(SIMD_DISABLED)
+ const SimdVector* input_vector = reinterpret_cast<const SimdVector*>(input);
+ SimdVector* output_vector = reinterpret_cast<SimdVector*>(output);
+#endif // !defined(SIMD_DISABLED)
+
+#ifdef SIMD_SSE
+ const size_t num_chunks = GetNumChunks(length);
+ const bool input_aligned = IsAligned(input);
+ const bool output_aligned = IsAligned(output);
+ if (input_aligned && output_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ output_vector[i] = SIMD_SQRT(input_vector[i]);
+ }
+ } else if (input_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector output_temp = SIMD_SQRT(input_vector[i]);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], output_temp);
+ }
+ } else if (output_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_temp = _mm_loadu_ps(&input[i * SIMD_LENGTH]);
+ output_vector[i] = SIMD_SQRT(input_temp);
+ }
+ } else {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector input_temp = _mm_loadu_ps(&input[i * SIMD_LENGTH]);
+ const SimdVector output_temp = SIMD_SQRT(input_temp);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], output_temp);
+ }
+ }
+#elif defined SIMD_NEON
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ // This should be faster than using a sqrt method : https://goo.gl/XRKwFp
+ output_vector[i] = SIMD_SQRT(input_vector[i]);
+ }
+#endif // SIMD_SSE
+
+ // Apply to samples at the end that were missed by the SIMD chunking.
+ const size_t leftover_samples = GetLeftoverSamples(length);
+ DCHECK_GE(length, leftover_samples);
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ output[i] = 1.0f / FastReciprocalSqrt(input[i]);
+ }
+}
+
+void ApproxComplexMagnitude(size_t length, const float* input, float* output) {
+ DCHECK(input);
+ DCHECK(output);
+
+#if !defined(SIMD_DISABLED)
+ const SimdVector* input_vector = reinterpret_cast<const SimdVector*>(input);
+ SimdVector* output_vector = reinterpret_cast<SimdVector*>(output);
+ const size_t num_chunks = GetNumChunks(length);
+ const bool input_aligned = IsAligned(input);
+ const bool output_aligned = IsAligned(output);
+#endif // !defined(SIMD_DISABLED)
+
+#ifdef SIMD_SSE
+ if (input_aligned && output_aligned) {
+ for (size_t out_index = 0; out_index < num_chunks; ++out_index) {
+ const size_t first_index = out_index * 2;
+ const size_t second_index = first_index + 1;
+ const SimdVector squared_1 =
+ SIMD_MULTIPLY(input_vector[first_index], input_vector[first_index]);
+ const SimdVector squared_2 =
+ SIMD_MULTIPLY(input_vector[second_index], input_vector[second_index]);
+ const SimdVector unshuffled_1 =
+ _mm_shuffle_ps(squared_1, squared_2, _MM_SHUFFLE(2, 0, 2, 0));
+ const SimdVector unshuffled_2 =
+ _mm_shuffle_ps(squared_1, squared_2, _MM_SHUFFLE(3, 1, 3, 1));
+ output_vector[out_index] = SIMD_ADD(unshuffled_1, unshuffled_2);
+ output_vector[out_index] = SIMD_SQRT(output_vector[out_index]);
+ }
+ } else if (input_aligned) {
+ for (size_t out_index = 0; out_index < num_chunks; ++out_index) {
+ const size_t first_index = out_index * 2;
+ const size_t second_index = first_index + 1;
+ const SimdVector squared_1 =
+ SIMD_MULTIPLY(input_vector[first_index], input_vector[first_index]);
+ const SimdVector squared_2 =
+ SIMD_MULTIPLY(input_vector[second_index], input_vector[second_index]);
+ const SimdVector unshuffled_1 =
+ _mm_shuffle_ps(squared_1, squared_2, _MM_SHUFFLE(2, 0, 2, 0));
+ const SimdVector unshuffled_2 =
+ _mm_shuffle_ps(squared_1, squared_2, _MM_SHUFFLE(3, 1, 3, 1));
+ SimdVector output_temp = SIMD_ADD(unshuffled_1, unshuffled_2);
+ output_vector[out_index] = SIMD_SQRT(output_temp);
+ _mm_storeu_ps(&output[out_index * SIMD_LENGTH], output_temp);
+ }
+ } else if (output_aligned) {
+ for (size_t out_index = 0; out_index < num_chunks; ++out_index) {
+ const size_t first_index = out_index * 2;
+ const size_t second_index = first_index + 1;
+ const SimdVector first_temp =
+ _mm_loadu_ps(&input[first_index * SIMD_LENGTH]);
+ const SimdVector second_temp =
+ _mm_loadu_ps(&input[second_index * SIMD_LENGTH]);
+ const SimdVector squared_1 = SIMD_MULTIPLY(first_temp, first_temp);
+ const SimdVector squared_2 = SIMD_MULTIPLY(second_temp, second_temp);
+ const SimdVector unshuffled_1 =
+ _mm_shuffle_ps(squared_1, squared_2, _MM_SHUFFLE(2, 0, 2, 0));
+ const SimdVector unshuffled_2 =
+ _mm_shuffle_ps(squared_1, squared_2, _MM_SHUFFLE(3, 1, 3, 1));
+ output_vector[out_index] = SIMD_ADD(unshuffled_1, unshuffled_2);
+ output_vector[out_index] = SIMD_SQRT(output_vector[out_index]);
+ }
+ } else {
+ for (size_t out_index = 0; out_index < num_chunks; ++out_index) {
+ const size_t first_index = out_index * 2;
+ const size_t second_index = first_index + 1;
+ const SimdVector first_temp =
+ _mm_loadu_ps(&input[first_index * SIMD_LENGTH]);
+ const SimdVector second_temp =
+ _mm_loadu_ps(&input[second_index * SIMD_LENGTH]);
+ const SimdVector squared_1 = SIMD_MULTIPLY(first_temp, first_temp);
+ const SimdVector squared_2 = SIMD_MULTIPLY(second_temp, second_temp);
+ const SimdVector unshuffled_1 =
+ _mm_shuffle_ps(squared_1, squared_2, _MM_SHUFFLE(2, 0, 2, 0));
+ const SimdVector unshuffled_2 =
+ _mm_shuffle_ps(squared_1, squared_2, _MM_SHUFFLE(3, 1, 3, 1));
+ SimdVector output_temp = SIMD_ADD(unshuffled_1, unshuffled_2);
+ output_temp = SIMD_SQRT(output_temp);
+ _mm_storeu_ps(&output[out_index * SIMD_LENGTH], output_temp);
+ }
+ }
+#elif defined SIMD_NEON
+ if (input_aligned && output_aligned) {
+ for (size_t out_index = 0; out_index < num_chunks; ++out_index) {
+ const size_t first_index = out_index * 2;
+ const size_t second_index = first_index + 1;
+ const SimdVector squared_1 =
+ SIMD_MULTIPLY(input_vector[first_index], input_vector[first_index]);
+ const SimdVector squared_2 =
+ SIMD_MULTIPLY(input_vector[second_index], input_vector[second_index]);
+ const float32x4x2_t unshuffled = vuzpq_f32(squared_1, squared_2);
+ output_vector[out_index] = SIMD_ADD(unshuffled.val[0], unshuffled.val[1]);
+ output_vector[out_index] = SIMD_SQRT(output_vector[out_index]);
+ }
+ } else if (input_aligned) {
+ for (size_t out_index = 0; out_index < num_chunks; ++out_index) {
+ const size_t first_index = out_index * 2;
+ const size_t second_index = first_index + 1;
+ const SimdVector squared_1 =
+ SIMD_MULTIPLY(input_vector[first_index], input_vector[first_index]);
+ const SimdVector squared_2 =
+ SIMD_MULTIPLY(input_vector[second_index], input_vector[second_index]);
+ const float32x4x2_t unshuffled = vuzpq_f32(squared_1, squared_2);
+ SimdVector output_temp = SIMD_ADD(unshuffled.val[0], unshuffled.val[1]);
+ output_temp = SIMD_SQRT(output_temp);
+ vst1q_f32(&output[out_index * SIMD_LENGTH], output_temp);
+ }
+ } else if (output_aligned) {
+ for (size_t out_index = 0; out_index < num_chunks; ++out_index) {
+ const size_t first_index = out_index * 2;
+ const size_t second_index = first_index + 1;
+ const SimdVector first_temp =
+ vld1q_f32(&input[first_index * SIMD_LENGTH]);
+ const SimdVector second_temp =
+ vld1q_f32(&input[second_index * SIMD_LENGTH]);
+ const SimdVector squared_1 = SIMD_MULTIPLY(first_temp, first_temp);
+ const SimdVector squared_2 = SIMD_MULTIPLY(second_temp, second_temp);
+ const float32x4x2_t unshuffled = vuzpq_f32(squared_1, squared_2);
+ output_vector[out_index] = SIMD_ADD(unshuffled.val[0], unshuffled.val[1]);
+ output_vector[out_index] = SIMD_SQRT(output_vector[out_index]);
+ }
+ } else {
+ for (size_t out_index = 0; out_index < num_chunks; ++out_index) {
+ const size_t first_index = out_index * 2;
+ const size_t second_index = first_index + 1;
+ const SimdVector first_temp =
+ vld1q_f32(&input[first_index * SIMD_LENGTH]);
+ const SimdVector second_temp =
+ vld1q_f32(&input[second_index * SIMD_LENGTH]);
+ const SimdVector squared_1 = SIMD_MULTIPLY(first_temp, first_temp);
+ const SimdVector squared_2 = SIMD_MULTIPLY(second_temp, second_temp);
+ const float32x4x2_t unshuffled = vuzpq_f32(squared_1, squared_2);
+ SimdVector output_temp = SIMD_ADD(unshuffled.val[0], unshuffled.val[1]);
+ output_temp = SIMD_SQRT(output_temp);
+ vst1q_f32(&output[out_index * SIMD_LENGTH], output_temp);
+ }
+ }
+#endif // SIMD_SSE
+
+ // Apply to samples at the end that were missed by the SIMD chunking.
+ const size_t leftover_samples = GetLeftoverSamples(length);
+ DCHECK_GE(length, leftover_samples);
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ const size_t real_index = i * 2;
+ const size_t imag_index = real_index + 1;
+ const float squared_sum = (input[real_index] * input[real_index]) +
+ (input[imag_index] * input[imag_index]);
+ output[i] = 1.0f / FastReciprocalSqrt(squared_sum);
+ }
+}
+
+void ComplexInterleavedFormatFromMagnitudeAndSinCosPhase(
+ size_t length, const float* magnitude, const float* cos_phase,
+ const float* sin_phase, float* complex_interleaved_format_output) {
+ size_t leftover_samples = 0;
+#ifdef SIMD_NEON
+ if (IsAligned(complex_interleaved_format_output) && IsAligned(cos_phase) &&
+ IsAligned(sin_phase) && IsAligned(magnitude)) {
+ const SimdVector* cos_vec = reinterpret_cast<const SimdVector*>(cos_phase);
+ const SimdVector* sin_vec = reinterpret_cast<const SimdVector*>(sin_phase);
+ const SimdVector* magnitude_vec =
+ reinterpret_cast<const SimdVector*>(magnitude);
+
+ const size_t num_chunks = GetNumChunks(length);
+ float32x4x2_t interleaved_pair;
+
+ SimdVector* interleaved_vec =
+ reinterpret_cast<SimdVector*>(complex_interleaved_format_output);
+ for (size_t i = 0, j = 0; j < num_chunks; ++i, j += 2) {
+ interleaved_pair = vzipq_f32(cos_vec[i], sin_vec[i]);
+ interleaved_vec[j] =
+ SIMD_MULTIPLY(interleaved_pair.val[0], magnitude_vec[i]);
+ interleaved_vec[j + 1] =
+ SIMD_MULTIPLY(interleaved_pair.val[1], magnitude_vec[i]);
+ }
+
+ leftover_samples = GetLeftoverSamples(length);
+ }
+#endif // SIMD_NEON
+ DCHECK_EQ(leftover_samples % 2U, 0U);
+ for (size_t i = leftover_samples, j = leftover_samples / 2; i < length;
+ i += 2, ++j) {
+ const size_t imaginary_offset = i + 1;
+ complex_interleaved_format_output[i] = magnitude[j] * cos_phase[j];
+ complex_interleaved_format_output[imaginary_offset] =
+ magnitude[j] * sin_phase[j];
+ }
+}
+
+void StereoFromMonoSimd(size_t length, const float* mono, float* left,
+ float* right) {
+ ScalarMultiply(length, kInverseSqrtTwo, mono, left);
+ std::copy_n(left, length, right);
+}
+
+void MonoFromStereoSimd(size_t length, const float* left, const float* right,
+ float* mono) {
+ DCHECK(left);
+ DCHECK(right);
+ DCHECK(mono);
+
+ const SimdVector* left_vector = reinterpret_cast<const SimdVector*>(left);
+ const SimdVector* right_vector = reinterpret_cast<const SimdVector*>(right);
+ SimdVector* mono_vector = reinterpret_cast<SimdVector*>(mono);
+
+ const SimdVector inv_root_two_vec = SIMD_LOAD_ONE_FLOAT(kInverseSqrtTwo);
+#ifdef SIMD_SSE
+ const size_t num_chunks = GetNumChunks(length);
+ const bool inputs_aligned = IsAligned(left) && IsAligned(right);
+ const bool mono_aligned = IsAligned(mono);
+ if (inputs_aligned && mono_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ mono_vector[i] = SIMD_MULTIPLY(inv_root_two_vec,
+ SIMD_ADD(left_vector[i], right_vector[i]));
+ }
+ } else if (inputs_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector mono_temp = SIMD_MULTIPLY(
+ inv_root_two_vec, SIMD_ADD(left_vector[i], right_vector[i]));
+ _mm_storeu_ps(&mono[i * SIMD_LENGTH], mono_temp);
+ }
+ } else if (mono_aligned) {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector left_temp = _mm_loadu_ps(&left[i * SIMD_LENGTH]);
+ const SimdVector right_temp = _mm_loadu_ps(&right[i * SIMD_LENGTH]);
+ mono_vector[i] =
+ SIMD_MULTIPLY(inv_root_two_vec, SIMD_ADD(left_temp, right_temp));
+ }
+ } else {
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const SimdVector left_temp = _mm_loadu_ps(&left[i * SIMD_LENGTH]);
+ const SimdVector right_temp = _mm_loadu_ps(&right[i * SIMD_LENGTH]);
+ const SimdVector mono_temp =
+ SIMD_MULTIPLY(inv_root_two_vec, SIMD_ADD(left_temp, right_temp));
+ _mm_storeu_ps(&mono[i * SIMD_LENGTH], mono_temp);
+ }
+ }
+#else
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ mono_vector[i] = SIMD_MULTIPLY(inv_root_two_vec,
+ SIMD_ADD(left_vector[i], right_vector[i]));
+ }
+#endif // SIMD_SSE
+ const size_t leftover_samples = GetLeftoverSamples(length);
+ // Downmix samples at the end that were missed by the SIMD chunking.
+ DCHECK_GE(length, leftover_samples);
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ mono[i] = kInverseSqrtTwo * (left[i] + right[i]);
+ }
+}
+
+#ifdef SIMD_NEON
+
+void Int16FromFloat(size_t length, const float* input, int16_t* output) {
+ DCHECK(input);
+ DCHECK(output);
+
+ // if (input_aligned || output_aligned) {
+ const SimdVector* input_vector = reinterpret_cast<const SimdVector*>(input);
+ int16x4_t* output_vector = reinterpret_cast<int16x4_t*>(output);
+
+ // A temporary 32 bit integer vector is needed as we only have intrinsics to
+ // convert from 32 bit floats to 32 bit ints. Then truncate to 16 bit ints.
+ int32x4_t temporary_wide_vector;
+ SimdVector temporary_float_vector;
+
+ const SimdVector scaling_vector = SIMD_LOAD_ONE_FLOAT(kInt16FromFloat);
+
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ temporary_float_vector = SIMD_MULTIPLY(scaling_vector, input_vector[i]);
+ temporary_wide_vector = vcvtq_s32_f32(temporary_float_vector);
+ output_vector[i] = vqmovn_s32(temporary_wide_vector);
+ }
+
+ // The remainder.
+ const size_t leftover_samples = GetLeftoverSamples(length);
+ DCHECK_GE(length, leftover_samples);
+ float temp_float;
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ temp_float = input[i] * kInt16FromFloat;
+ temp_float = std::min(kInt16Max, std::max(kInt16Min, temp_float));
+ output[i] = static_cast<int16_t>(temp_float);
+ }
+}
+
+void FloatFromInt16(size_t length, const int16_t* input, float* output) {
+ DCHECK(input);
+ DCHECK(output);
+
+ size_t leftover_samples = length;
+ const bool input_aligned = IsAligned(input);
+ const bool output_aligned = IsAligned(output);
+ if (input_aligned || output_aligned) {
+ const int16x4_t* input_vector = reinterpret_cast<const int16x4_t*>(input);
+ SimdVector* output_vector = reinterpret_cast<SimdVector*>(output);
+
+ int16x4_t temporary_narrow_vector;
+ SimdVector temporary_float_vector;
+ int32x4_t temporary_wide_vector;
+ const SimdVector scaling_vector = SIMD_LOAD_ONE_FLOAT(kFloatFromInt16);
+
+ if (input_aligned && output_aligned) {
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ temporary_wide_vector = vmovl_s16(input_vector[i]);
+ output_vector[i] = vcvtq_f32_s32(temporary_wide_vector);
+ output_vector[i] = SIMD_MULTIPLY(scaling_vector, output_vector[i]);
+ }
+ } else if (input_aligned) {
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ temporary_wide_vector = vmovl_s16(input_vector[i]);
+ temporary_float_vector = vcvtq_f32_s32(temporary_wide_vector);
+ temporary_float_vector =
+ SIMD_MULTIPLY(scaling_vector, temporary_float_vector);
+ vst1q_f32(&output[i * SIMD_LENGTH], temporary_float_vector);
+ }
+ } else {
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ temporary_narrow_vector = vld1_s16(&input[i * SIMD_LENGTH]);
+ temporary_wide_vector = vmovl_s16(temporary_narrow_vector);
+ output_vector[i] = vcvtq_f32_s32(temporary_wide_vector);
+ output_vector[i] = SIMD_MULTIPLY(scaling_vector, output_vector[i]);
+ }
+ }
+ leftover_samples = GetLeftoverSamples(length);
+ }
+
+ // The remainder.
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ output[i] = static_cast<float>(input[i]) * kFloatFromInt16;
+ }
+}
+
+#elif (defined SIMD_SSE && !defined(_MSC_VER))
+
+void Int16FromFloat(size_t length, const float* input, int16_t* output) {
+ DCHECK(input);
+ DCHECK(output);
+
+ size_t leftover_samples = length;
+ const bool input_aligned = IsAligned(input);
+ const bool output_aligned = IsAligned(output);
+ if (output_aligned) {
+ const SimdVector* input_vector = reinterpret_cast<const SimdVector*>(input);
+ __m64* output_vector = reinterpret_cast<__m64*>(output);
+
+ const SimdVector scaling_vector = SIMD_LOAD_ONE_FLOAT(kInt16FromFloat);
+ const SimdVector min_vector = SIMD_LOAD_ONE_FLOAT(kInt16Min);
+ const SimdVector max_vector = SIMD_LOAD_ONE_FLOAT(kInt16Max);
+
+ SimdVector temporary_float_vector;
+
+ if (input_aligned) {
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ temporary_float_vector = SIMD_MULTIPLY(scaling_vector, input_vector[i]);
+ temporary_float_vector = _mm_max_ps(temporary_float_vector, min_vector);
+ temporary_float_vector = _mm_min_ps(temporary_float_vector, max_vector);
+ output_vector[i] = _mm_cvtps_pi16(temporary_float_vector);
+ }
+ } else {
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ temporary_float_vector = _mm_loadu_ps(&input[i * SIMD_LENGTH]);
+ temporary_float_vector =
+ SIMD_MULTIPLY(scaling_vector, temporary_float_vector);
+ temporary_float_vector = _mm_max_ps(temporary_float_vector, min_vector);
+ temporary_float_vector = _mm_min_ps(temporary_float_vector, max_vector);
+ output_vector[i] = _mm_cvtps_pi16(temporary_float_vector);
+ }
+ }
+ // There is no easy way to simply store the 16 bit ints so we dont have an
+ // |input_aligned| only case.
+ leftover_samples = GetLeftoverSamples(length);
+ }
+
+ // The remainder.
+ float temp_float;
+ for (size_t i = length - GetLeftoverSamples(length); i < length; ++i) {
+ temp_float = input[i] * kInt16FromFloat;
+ temp_float = std::min(kInt16Max, std::max(kInt16Min, temp_float));
+ output[i] = static_cast<int16_t>(temp_float);
+ }
+}
+
+void FloatFromInt16(size_t length, const int16_t* input, float* output) {
+ DCHECK(input);
+ DCHECK(output);
+
+ size_t leftover_samples = length;
+ const bool input_aligned = IsAligned(input);
+ const bool output_aligned = IsAligned(output);
+ if (input_aligned) {
+ SimdVector* output_vector = reinterpret_cast<SimdVector*>(output);
+ const __m64* input_vector = reinterpret_cast<const __m64*>(input);
+
+ const SimdVector scaling_vector = SIMD_LOAD_ONE_FLOAT(kFloatFromInt16);
+
+ if (output_aligned) {
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ output_vector[i] = _mm_cvtpi16_ps(input_vector[i]);
+ output_vector[i] = SIMD_MULTIPLY(scaling_vector, output_vector[i]);
+ }
+ } else {
+ SimdVector temporary_float_vector;
+ for (size_t i = 0; i < GetNumChunks(length); ++i) {
+ temporary_float_vector = _mm_cvtpi16_ps(input_vector[i]);
+ temporary_float_vector =
+ SIMD_MULTIPLY(scaling_vector, temporary_float_vector);
+ _mm_storeu_ps(&output[i * SIMD_LENGTH], temporary_float_vector);
+ }
+ }
+ // There is no easy way to simply load the 16 bit ints so we dont have an
+ // |output_aligned| only case.
+ leftover_samples = GetLeftoverSamples(length);
+ }
+
+ // The remainder.
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ output[i] = static_cast<float>(input[i]) * kFloatFromInt16;
+ }
+}
+
+#else // SIMD disabled or Windows build.
+
+void Int16FromFloat(size_t length, const float* input, int16_t* output) {
+ DCHECK(input);
+ DCHECK(output);
+
+ float temp_float;
+ for (size_t i = 0; i < length; ++i) {
+ temp_float = input[i] * kInt16FromFloat;
+ temp_float = std::min(kInt16Max, std::max(kInt16Min, temp_float));
+ output[i] = static_cast<int16_t>(temp_float);
+ }
+}
+
+void FloatFromInt16(size_t length, const int16_t* input, float* output) {
+ DCHECK(input);
+ DCHECK(output);
+
+ for (size_t i = 0; i < length; ++i) {
+ output[i] = static_cast<float>(input[i]) * kFloatFromInt16;
+ }
+}
+
+#endif // SIMD_NEON
+
+void InterleaveStereo(size_t length, const int16_t* channel_0,
+ const int16_t* channel_1, int16_t* interleaved_buffer) {
+ DCHECK(interleaved_buffer);
+ DCHECK(channel_0);
+ DCHECK(channel_1);
+
+ size_t leftover_samples = length;
+#ifdef SIMD_NEON
+ if (IsAligned(interleaved_buffer) && IsAligned(channel_0) &&
+ IsAligned(channel_1)) {
+ const int16x8_t* channel_0_vec =
+ reinterpret_cast<const int16x8_t*>(channel_0);
+ const int16x8_t* channel_1_vec =
+ reinterpret_cast<const int16x8_t*>(channel_1);
+
+ const size_t num_chunks = length / kSixteenBitSimdLength;
+ int16x8x2_t interleaved_pair;
+
+ int16x8_t* interleaved_vec =
+ reinterpret_cast<int16x8_t*>(interleaved_buffer);
+ for (size_t i = 0, j = 0; i < num_chunks; ++i, j += 2) {
+ interleaved_pair = vzipq_s16(channel_0_vec[i], channel_1_vec[i]);
+ interleaved_vec[j] = interleaved_pair.val[0];
+ interleaved_vec[j + 1] = interleaved_pair.val[1];
+ }
+
+ leftover_samples = length % kSixteenBitSimdLength;
+ }
+#endif // SIMD_NEON
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ const size_t interleaved_index = kNumStereoChannels * i;
+ interleaved_buffer[interleaved_index] = channel_0[i];
+ interleaved_buffer[interleaved_index + 1] = channel_1[i];
+ }
+}
+
+void InterleaveStereo(size_t length, const float* channel_0,
+ const float* channel_1, float* interleaved_buffer) {
+ DCHECK(interleaved_buffer);
+ DCHECK(channel_0);
+ DCHECK(channel_1);
+
+ size_t leftover_samples = length;
+#ifdef SIMD_NEON
+ if (IsAligned(interleaved_buffer) && IsAligned(channel_0) &&
+ IsAligned(channel_1)) {
+ const SimdVector* channel_0_vec =
+ reinterpret_cast<const SimdVector*>(channel_0);
+ const SimdVector* channel_1_vec =
+ reinterpret_cast<const SimdVector*>(channel_1);
+
+ const size_t num_chunks = GetNumChunks(length);
+ float32x4x2_t interleaved_pair;
+
+ SimdVector* interleaved_vec =
+ reinterpret_cast<SimdVector*>(interleaved_buffer);
+ for (size_t i = 0, j = 0; i < num_chunks; ++i, j += 2) {
+ interleaved_pair = vzipq_f32(channel_0_vec[i], channel_1_vec[i]);
+ interleaved_vec[j] = interleaved_pair.val[0];
+ interleaved_vec[j + 1] = interleaved_pair.val[1];
+ }
+
+ leftover_samples = GetLeftoverSamples(length);
+ }
+#endif // SIMD_NEON
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ const size_t interleaved_index = kNumStereoChannels * i;
+ interleaved_buffer[interleaved_index] = channel_0[i];
+ interleaved_buffer[interleaved_index + 1] = channel_1[i];
+ }
+}
+
+void InterleaveStereo(size_t length, const float* channel_0,
+ const float* channel_1, int16_t* interleaved_buffer) {
+ DCHECK(interleaved_buffer);
+ DCHECK(channel_0);
+ DCHECK(channel_1);
+
+ size_t leftover_samples = length;
+#ifdef SIMD_NEON
+ if (IsAligned(interleaved_buffer) && IsAligned(channel_0) &&
+ IsAligned(channel_1)) {
+ const SimdVector* channel_0_vec =
+ reinterpret_cast<const SimdVector*>(channel_0);
+ const SimdVector* channel_1_vec =
+ reinterpret_cast<const SimdVector*>(channel_1);
+
+ const size_t num_chunks = GetNumChunks(length);
+ float32x4x2_t interleaved_pair;
+ int32x4_t temporary_wide_vector;
+
+ const SimdVector scaling_vector = SIMD_LOAD_ONE_FLOAT(kInt16FromFloat);
+ const SimdVector min_vector = SIMD_LOAD_ONE_FLOAT(kInt16Min);
+ const SimdVector max_vector = SIMD_LOAD_ONE_FLOAT(kInt16Max);
+
+ int16x4_t* interleaved_vec =
+ reinterpret_cast<int16x4_t*>(interleaved_buffer);
+ for (size_t i = 0; i < num_chunks; ++i) {
+ const size_t interleaved_index = kNumStereoChannels * i;
+ interleaved_pair = vzipq_f32(channel_0_vec[i], channel_1_vec[i]);
+ interleaved_pair.val[0] =
+ SIMD_MULTIPLY(scaling_vector, interleaved_pair.val[0]);
+ interleaved_pair.val[0] = vmaxq_f32(interleaved_pair.val[0], min_vector);
+ interleaved_pair.val[0] = vminq_f32(interleaved_pair.val[0], max_vector);
+ temporary_wide_vector = vcvtq_s32_f32(interleaved_pair.val[0]);
+ interleaved_vec[interleaved_index] = vqmovn_s32(temporary_wide_vector);
+ interleaved_pair.val[1] =
+ SIMD_MULTIPLY(scaling_vector, interleaved_pair.val[1]);
+ interleaved_pair.val[1] = vmaxq_f32(interleaved_pair.val[1], min_vector);
+ interleaved_pair.val[1] = vminq_f32(interleaved_pair.val[1], max_vector);
+ temporary_wide_vector = vcvtq_s32_f32(interleaved_pair.val[1]);
+ interleaved_vec[interleaved_index + 1] =
+ vqmovn_s32(temporary_wide_vector);
+ }
+
+ leftover_samples = GetLeftoverSamples(length);
+ }
+#endif // SIMD_NEON
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ const size_t interleaved_index = kNumStereoChannels * i;
+ interleaved_buffer[interleaved_index] = static_cast<int16_t>(std::max(
+ kInt16Min, std::min(kInt16Max, kInt16FromFloat * channel_0[i])));
+ interleaved_buffer[interleaved_index + 1] = static_cast<int16_t>(std::max(
+ kInt16Min, std::min(kInt16Max, kInt16FromFloat * channel_1[i])));
+ }
+}
+
+void DeinterleaveStereo(size_t length, const int16_t* interleaved_buffer,
+ int16_t* channel_0, int16_t* channel_1) {
+ DCHECK(interleaved_buffer);
+ DCHECK(channel_0);
+ DCHECK(channel_1);
+
+ size_t leftover_samples = length;
+#ifdef SIMD_NEON
+ if (IsAligned(interleaved_buffer) && IsAligned(channel_0) &&
+ IsAligned(channel_1)) {
+ const size_t num_chunks = length / kSixteenBitSimdLength;
+ leftover_samples = length % kSixteenBitSimdLength;
+ int16x8_t* channel_0_vec = reinterpret_cast<int16x8_t*>(channel_0);
+ int16x8_t* channel_1_vec = reinterpret_cast<int16x8_t*>(channel_1);
+ int16x8x2_t deinterleaved_pair;
+ const int16x8_t* interleaved_vec =
+ reinterpret_cast<const int16x8_t*>(interleaved_buffer);
+ for (size_t chunk = 0; chunk < num_chunks; ++chunk) {
+ const size_t interleaved_index = chunk * kNumStereoChannels;
+ deinterleaved_pair = vuzpq_s16(interleaved_vec[interleaved_index],
+ interleaved_vec[interleaved_index + 1]);
+ channel_0_vec[chunk] = deinterleaved_pair.val[0];
+ channel_1_vec[chunk] = deinterleaved_pair.val[1];
+ }
+ }
+#endif // SIMD_NEON
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ const size_t interleaved_index = kNumStereoChannels * i;
+ channel_0[i] = interleaved_buffer[interleaved_index];
+ channel_1[i] = interleaved_buffer[interleaved_index + 1];
+ }
+}
+
+void DeinterleaveStereo(size_t length, const float* interleaved_buffer,
+ float* channel_0, float* channel_1) {
+ DCHECK(interleaved_buffer);
+ DCHECK(channel_0);
+ DCHECK(channel_1);
+
+ size_t leftover_samples = length;
+#ifdef SIMD_NEON
+ if (IsAligned(interleaved_buffer) && IsAligned(channel_0) &&
+ IsAligned(channel_1)) {
+ const size_t num_chunks = GetNumChunks(length);
+ leftover_samples = GetLeftoverSamples(length);
+ SimdVector* channel_0_vec = reinterpret_cast<SimdVector*>(channel_0);
+ SimdVector* channel_1_vec = reinterpret_cast<SimdVector*>(channel_1);
+ float32x4x2_t deinterleaved_pair;
+
+ const SimdVector* interleaved_vec =
+ reinterpret_cast<const SimdVector*>(interleaved_buffer);
+ for (size_t chunk = 0; chunk < num_chunks; ++chunk) {
+ const size_t interleaved_index = chunk * kNumStereoChannels;
+ deinterleaved_pair = vuzpq_f32(interleaved_vec[interleaved_index],
+ interleaved_vec[interleaved_index + 1]);
+ channel_0_vec[chunk] = deinterleaved_pair.val[0];
+ channel_1_vec[chunk] = deinterleaved_pair.val[1];
+ }
+ }
+#endif // SIMD_NEON
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ const size_t interleaved_index = kNumStereoChannels * i;
+ channel_0[i] = interleaved_buffer[interleaved_index];
+ channel_1[i] = interleaved_buffer[interleaved_index + 1];
+ }
+}
+
+void DeinterleaveStereo(size_t length, const int16_t* interleaved_buffer,
+ float* channel_0, float* channel_1) {
+ DCHECK(interleaved_buffer);
+ DCHECK(channel_0);
+ DCHECK(channel_1);
+
+ size_t leftover_samples = length;
+#ifdef SIMD_NEON
+ if (IsAligned(interleaved_buffer) && IsAligned(channel_0) &&
+ IsAligned(channel_1)) {
+ const size_t num_chunks = GetNumChunks(length);
+ leftover_samples = GetLeftoverSamples(length);
+ SimdVector* channel_0_vec = reinterpret_cast<SimdVector*>(channel_0);
+ SimdVector* channel_1_vec = reinterpret_cast<SimdVector*>(channel_1);
+ int16x4x2_t deinterleaved_pair;
+ int32x4_t temporary_wide;
+ const SimdVector scaling_vector = SIMD_LOAD_ONE_FLOAT(kFloatFromInt16);
+
+ const int16x4_t* interleaved_vec =
+ reinterpret_cast<const int16x4_t*>(interleaved_buffer);
+ for (size_t chunk = 0; chunk < num_chunks; ++chunk) {
+ const size_t interleaved_index = chunk * kNumStereoChannels;
+ deinterleaved_pair = vuzp_s16(interleaved_vec[interleaved_index],
+ interleaved_vec[interleaved_index + 1]);
+ temporary_wide = vmovl_s16(deinterleaved_pair.val[0]);
+ channel_0_vec[chunk] = vcvtq_f32_s32(temporary_wide);
+ channel_0_vec[chunk] =
+ SIMD_MULTIPLY(scaling_vector, channel_0_vec[chunk]);
+ temporary_wide = vmovl_s16(deinterleaved_pair.val[1]);
+ channel_1_vec[chunk] = vcvtq_f32_s32(temporary_wide);
+ channel_1_vec[chunk] =
+ SIMD_MULTIPLY(scaling_vector, channel_1_vec[chunk]);
+ }
+ }
+#endif // SIMD_NEON
+ for (size_t i = length - leftover_samples; i < length; ++i) {
+ const size_t interleaved_index = kNumStereoChannels * i;
+ channel_0[i] = static_cast<float>(interleaved_buffer[interleaved_index]) *
+ kFloatFromInt16;
+ channel_1[i] =
+ static_cast<float>(interleaved_buffer[interleaved_index + 1]) *
+ kFloatFromInt16;
+ }
+}
+
+void InterleaveQuad(size_t length, const int16_t* channel_0,
+ const int16_t* channel_1, const int16_t* channel_2,
+ const int16_t* channel_3, int16_t* workspace,
+ int16_t* interleaved_buffer) {
+#ifdef SIMD_NEON
+ DCHECK(IsAligned(workspace));
+ const size_t double_length = length * 2;
+ int16_t* workspace_half_point =
+ workspace + FindNextAlignedArrayIndex(double_length, sizeof(int16_t),
+ kMemoryAlignmentBytes);
+ InterleaveStereo(length, channel_0, channel_2, workspace);
+ InterleaveStereo(length, channel_1, channel_3, workspace_half_point);
+ InterleaveStereo(double_length, workspace, workspace_half_point,
+ interleaved_buffer);
+#else
+ for (size_t i = 0; i < length; ++i) {
+ const size_t interleaved_index = kNumFirstOrderAmbisonicChannels * i;
+ interleaved_buffer[interleaved_index] = channel_0[i];
+ interleaved_buffer[interleaved_index + 1] = channel_1[i];
+ interleaved_buffer[interleaved_index + 2] = channel_2[i];
+ interleaved_buffer[interleaved_index + 3] = channel_3[i];
+ }
+#endif // SIMD_NEON
+}
+
+void InterleaveQuad(size_t length, const float* channel_0,
+ const float* channel_1, const float* channel_2,
+ const float* channel_3, float* workspace,
+ float* interleaved_buffer) {
+#ifdef SIMD_NEON
+ DCHECK(IsAligned(workspace));
+ const size_t double_length = length * 2;
+ float* workspace_half_point =
+ workspace + FindNextAlignedArrayIndex(double_length, sizeof(float),
+ kMemoryAlignmentBytes);
+ DCHECK(IsAligned(workspace_half_point));
+ InterleaveStereo(length, channel_0, channel_2, workspace);
+ InterleaveStereo(length, channel_1, channel_3, workspace_half_point);
+ InterleaveStereo(double_length, workspace, workspace_half_point,
+ interleaved_buffer);
+#else
+ for (size_t i = 0; i < length; ++i) {
+ const size_t interleaved_index = kNumFirstOrderAmbisonicChannels * i;
+ interleaved_buffer[interleaved_index] = channel_0[i];
+ interleaved_buffer[interleaved_index + 1] = channel_1[i];
+ interleaved_buffer[interleaved_index + 2] = channel_2[i];
+ interleaved_buffer[interleaved_index + 3] = channel_3[i];
+ }
+#endif // SIMD_NEON
+}
+
+void DeinterleaveQuad(size_t length, const int16_t* interleaved_buffer,
+ int16_t* workspace, int16_t* channel_0,
+ int16_t* channel_1, int16_t* channel_2,
+ int16_t* channel_3) {
+#ifdef SIMD_NEON
+ DCHECK(IsAligned(workspace));
+ const size_t double_length = length * 2;
+ int16_t* workspace_half_point =
+ workspace + FindNextAlignedArrayIndex(double_length, sizeof(int16_t),
+ kMemoryAlignmentBytes);
+ DCHECK(IsAligned(workspace_half_point));
+ DeinterleaveStereo(double_length, interleaved_buffer, workspace,
+ workspace_half_point);
+ DeinterleaveStereo(length, workspace, channel_0, channel_2);
+ DeinterleaveStereo(length, workspace_half_point, channel_1, channel_3);
+#else
+ for (size_t i = 0; i < length; ++i) {
+ const size_t interleaved_index = kNumFirstOrderAmbisonicChannels * i;
+ channel_0[i] = interleaved_buffer[interleaved_index];
+ channel_1[i] = interleaved_buffer[interleaved_index + 1];
+ channel_2[i] = interleaved_buffer[interleaved_index + 2];
+ channel_3[i] = interleaved_buffer[interleaved_index + 3];
+ }
+#endif // SIMD_NEON
+}
+
+void DeinterleaveQuad(size_t length, const float* interleaved_buffer,
+ float* workspace, float* channel_0, float* channel_1,
+ float* channel_2, float* channel_3) {
+#ifdef SIMD_NEON
+ DCHECK(IsAligned(workspace));
+ const size_t double_length = length * 2;
+ float* workspace_half_point =
+ workspace + FindNextAlignedArrayIndex(double_length, sizeof(float),
+ kMemoryAlignmentBytes);
+ DCHECK(IsAligned(workspace_half_point));
+ DeinterleaveStereo(double_length, interleaved_buffer, workspace,
+ workspace_half_point);
+ DeinterleaveStereo(length, workspace, channel_0, channel_2);
+ DeinterleaveStereo(length, workspace_half_point, channel_1, channel_3);
+#else
+ for (size_t i = 0; i < length; ++i) {
+ const size_t interleaved_index = kNumFirstOrderAmbisonicChannels * i;
+ channel_0[i] = interleaved_buffer[interleaved_index];
+ channel_1[i] = interleaved_buffer[interleaved_index + 1];
+ channel_2[i] = interleaved_buffer[interleaved_index + 2];
+ channel_3[i] = interleaved_buffer[interleaved_index + 3];
+ }
+#endif // SIMD_NEON
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/simd_utils.h b/src/3rdparty/resonance-audio/resonance_audio/base/simd_utils.h
new file mode 100644
index 000000000..64fb9c6d1
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/simd_utils.h
@@ -0,0 +1,296 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_BASE_SIMD_UTILS_H_
+#define RESONANCE_AUDIO_BASE_SIMD_UTILS_H_
+
+#include <cstddef>
+#include <cstdint>
+
+namespace vraudio {
+
+// Checks if the pointer provided is correctly aligned for SIMD.
+//
+// @param pointer Pointer to check.
+// @return True if the pointer is correctly aligned.
+bool IsAligned(const float* pointer);
+bool IsAligned(const int16_t* pointer);
+
+// Rounds a number of frames up to the next aligned memory address
+// based on |memory_alignment_bytes|. This allows for aligning offset pointers
+// into a single chunk of allocated memory.
+//
+// @param length Number of samples before the desired offset pointer.
+// @param type_size_bytes Size of the type of each entry in the array.
+// @param memory_alignment_bytes Number of bytes to which an address is aligned.
+// @return Number of samples into the memory chunk to ensure aligned memory.
+size_t FindNextAlignedArrayIndex(size_t length, size_t type_size_bytes,
+ size_t memory_alignment_bytes);
+
+// Adds a float array |input_a| to another float array |input_b| and stores the
+// result in |output|.
+//
+// @param length Number of floats.
+// @param input_a Pointer to the first float in input_a array.
+// @param input_b Pointer to the first float in input_b array.
+// @param output Pointer to the first float in output array.
+void AddPointwise(size_t length, const float* input_a, const float* input_b,
+ float* output);
+
+// Subtracts a float array |input|, pointwise from another float array |output|.
+//
+// @param length Number of floats.
+// @param input Pointer to the first float in input_a array.
+// @param output Pointer to the first float in input_b array.
+// @param output Pointer to the first float in output array.
+void SubtractPointwise(size_t length, const float* input_a,
+ const float* input_b, float* output);
+
+// Pointwise multiplies a float array |input_a| with another float array
+// |input_b| and stores the result in |output|.
+//
+// @param length Number of floats.
+// @param input Pointer to the first float in input_a array.
+// @param input Pointer to the first float in input_b array.
+// @param output Pointer to the first float in output array.
+void MultiplyPointwise(size_t length, const float* input_a,
+ const float* input_b, float* output);
+
+// Pointwise multiplies a float array |input_a| with another float array
+// |input_b| and adds the result onto |accumulator|.
+//
+// @param length Number of floats.
+// @param input_a Pointer to the first float in input_a array.
+// @param input_b Pointer to the first float in input_b array.
+// @param accumulator Pointer to the first float in accumulator array.
+void MultiplyAndAccumulatePointwise(size_t length, const float* input_a,
+ const float* input_b, float* accumulator);
+
+// Multiplies a float array |input| by a scalar |gain| over |length| samples.
+//
+// @param length Number of floats.
+// @param gain Scalar value with which to multiply the input.
+// @param input Pointer to the first float in input array.
+// @param output Pointer to the first float in output array.
+void ScalarMultiply(size_t length, float gain, const float* input,
+ float* output);
+
+// Multiplies a float array |input| by a scalar |gain| over |length| samples and
+// adds the result onto |accumulator|.
+//
+// @param length Number of floats.
+// @param gain Scalar value with which to multiply the input.
+// @param input Pointer to the first float in input array.
+// @param output Pointer to the first float in accumulator array.
+void ScalarMultiplyAndAccumulate(size_t length, float gain, const float* input,
+ float* accumulator);
+
+// Calculates an approximmate reciprocal square root.
+//
+// @param length Number of floats.
+// @param input Pointer to the first float in input array.
+// @param output Pointer to the first float in output array.
+void ReciprocalSqrt(size_t length, const float* input, float* output);
+
+// Calculates an approximate square root.
+//
+// @param length Number of floats.
+// @param input Pointer to the first float in input array.
+// @param output Pointer to the first float in output array.
+void Sqrt(size_t length, const float* input, float* output);
+
+// Calculates the approximate magnitudes of interleaved complex numbers.
+//
+// @param length Number of complex numbers in the input array,
+// (i.e. half its length).
+// @param input Pointer to the first float in input array. Length: 2 * |length|.
+// @param output Pointer to the first float in output array, Length: |length|.
+void ApproxComplexMagnitude(size_t length, const float* input, float* output);
+
+// Calculates the complex values in interleaved format (real, imaginary), from a
+// vector of magnitudes and of sines and cosines of phase.
+//
+// @param length Number of total entries (real & imaginary) in the input array.
+// @param magnitude Pointer to the first float in the magnitude array, Length:
+// |length| / 2
+// @param cos_phase Pointer to the first float in the cosine phase array,
+// Length: |length| / 2
+// @param sin_phase Pointer to the first float in the sine phase array, Length:
+// |length| / 2
+// @param complex_interleaved_format_output Pointer to the first float in the
+// output array. Length: |length|.
+void ComplexInterleavedFormatFromMagnitudeAndSinCosPhase(
+ size_t length, const float* magnitude, const float* cos_phase,
+ const float* sin_phase, float* complex_interleaved_format_output);
+
+// Generates an identical left and right pair of stereo channels from a mono
+// input channel, where each channel is the mono channel times 1/sqrt(2).
+//
+// @param length Number of floats.
+// @param mono Pointer to the first float in an input mono array.
+// @param left Pointer to the first float in the left output array.
+// @param right Pointer to the first float in the right output array.
+void StereoFromMonoSimd(size_t length, const float* mono, float* left,
+ float* right);
+
+// Generates a mono downmix from a pair of stereo channels, where the output is
+// equal to the sum of the two inputs times 1/sqrt(2).
+//
+// @param length Number of floats.
+// @param left Pointer to the first float in the left input array.
+// @param right Pointer to the first float in the right input array.
+// @param mono Pointer to the first float in an output mono array.
+void MonoFromStereoSimd(size_t length, const float* left, const float* right,
+ float* mono);
+
+// Converts an array of 32 bit float input to clamped 16 bit int output.
+//
+// @param length Number of floats in the input array and int16_ts in the output.
+// @param input Float array.
+// @param output Int array.
+void Int16FromFloat(size_t length, const float* input, int16_t* output);
+
+// Converts an array of 16 bit int input to 32 bit float output.
+//
+// @param length Number of int16_ts in the input array and floats in the output.
+// @param input Int array.
+// @param output Float array.
+void FloatFromInt16(size_t length, const int16_t* input, float* output);
+
+// Interleaves a pair of mono buffers of int_16 data into a stereo buffer.
+//
+// @param length Number of frames per mono channel. The interleaved buffer must
+// be twice this size.
+// @param channel_0 Input buffer of mono data for the first channel.
+// @param channel_1 Input buffer of mono data for the second channel.
+// @param interleaved_buffer Output buffer of stereo interleaved data.
+void InterleaveStereo(size_t length, const int16_t* channel_0,
+ const int16_t* channel_1, int16_t* interleaved_buffer);
+
+// Interleaves a pair of mono buffers of float data into a stereo buffer.
+//
+// @param length Number of frames per mono channel. The interleaved buffer must
+// be twice this size.
+// @param channel_0 Input buffer of mono data for the first channel.
+// @param channel_1 Input buffer of mono data for the second channel.
+// @param interleaved_buffer Output buffer of stereo interleaved data.
+void InterleaveStereo(size_t length, const float* channel_0,
+ const float* channel_1, float* interleaved_buffer);
+
+// Interleaves a pair of mono buffers of float data into a stereo buffer of
+// int16_t data.
+//
+// @param length Number of frames per mono channel. The interleaved buffer must
+// be twice this size.
+// @param channel_0 Input buffer of mono data for the first channel (float).
+// @param channel_1 Input buffer of mono data for the second channel (float).
+// @param interleaved_buffer Output buffer of stereo interleaved data (int16_t).
+void InterleaveStereo(size_t length, const float* channel_0,
+ const float* channel_1, int16_t* interleaved_buffer);
+
+// Deinterleaves a stereo buffer of int16_t data into a pair of mono buffers.
+//
+// @param length Number of frames per mono channel. The interleaved buffer must
+// be twice this size.
+// @param interleaved_buffer Input buffer of stereo interleaved data.
+// @param channel_0 Output buffer of mono data for the first channel.
+// @param channel_1 Output buffer of mono data for the second channel.
+void DeinterleaveStereo(size_t length, const int16_t* interleaved_buffer,
+ int16_t* channel_0, int16_t* channel_1);
+
+// Deinterleaves a stereo buffer of float data into a pair of mono buffers.
+//
+// @param length Number of frames per mono channel. The interleaved buffer must
+// be twice this size.
+// @param interleaved_buffer Input buffer of stereo interleaved data.
+// @param channel_0 Output buffer of mono data for the first channel.
+// @param channel_1 Output buffer of mono data for the second channel.
+void DeinterleaveStereo(size_t length, const float* interleaved_buffer,
+ float* channel_0, float* channel_1);
+
+// Deinterleaves a stereo buffer of int16_t data into a pair of mono float
+// buffers, performing the int16 to floating point conversion.
+//
+// @param length Number of frames per mono channel. The interleaved buffer must
+// be twice this size.
+// @param interleaved_buffer Input buffer of stereo interleaved data (int16_t).
+// @param channel_0 Output buffer of mono data for the first channel (float).
+// @param channel_1 Output buffer of mono data for the second channel (float).
+void DeinterleaveStereo(size_t length, const int16_t* interleaved_buffer,
+ float* channel_0, float* channel_1);
+
+// Interleaves four mono buffers of int16_t data into a quad buffer.
+//
+// @param length Number of frames per mono channel. The interleaved buffer must
+// be four times this size and the workspace must be five times this size.
+// @param channel_0 Input buffer of mono data for the first channel.
+// @param channel_1 Input buffer of mono data for the second channel.
+// @param channel_2 Input buffer of mono data for the third channel.
+// @param channel_3 Input buffer of mono data for the fourth channel.
+// @param workspace Aligned buffer of 5 * |length| samples in length.
+// @param interleaved_buffer Output buffer of quad interleaved data.
+void InterleaveQuad(size_t length, const int16_t* channel_0,
+ const int16_t* channel_1, const int16_t* channel_2,
+ const int16_t* channel_3, int16_t* workspace,
+ int16_t* interleaved_buffer);
+
+// Interleaves four mono buffers of float data into a quad buffer.
+//
+// @param length Number of frames per mono channel. The interleaved buffer must
+// be four times this size and the workspace must be five times this size.
+// @param channel_0 Input buffer of mono data for the first channel.
+// @param channel_1 Input buffer of mono data for the second channel.
+// @param channel_2 Input buffer of mono data for the third channel.
+// @param channel_3 Input buffer of mono data for the fourth channel.
+// @param workspace Aligned buffer of 5 * |length| samples in length.
+// @param interleaved_buffer Output buffer of quad interleaved data.
+void InterleaveQuad(size_t length, const float* channel_0,
+ const float* channel_1, const float* channel_2,
+ const float* channel_3, float* workspace,
+ float* interleaved_buffer);
+
+// Deinterleaves a quad buffer of int16_t data into four mono buffers.
+//
+// @param length Number of frames per mono channel. The interleaved buffer must
+// be four times this size and the workspace must be five times this size.
+// @param interleaved_buffer Input buffer of quad interleaved data.
+// @param workspace Aligned buffer of 5 * |length| samples in length.
+// @param channel_0 Output buffer of mono data for the first channel.
+// @param channel_1 Output buffer of mono data for the second channel.
+// @param channel_2 Output buffer of mono data for the third channel.
+// @param channel_3 Output buffer of mono data for the fourth channel.
+void DeinterleaveQuad(size_t length, const int16_t* interleaved_buffer,
+ int16_t* workspace, int16_t* channel_0,
+ int16_t* channel_1, int16_t* channel_2,
+ int16_t* channel_3);
+
+// Deinterleaves a quad buffer of float data into four mono buffers.
+//
+// @param length Number of frames per mono channel. The interleaved buffer must
+// be four times this size and the workspace must be five times this size.
+// @param interleaved_buffer Input buffer of quad interleaved data.
+// @param workspace Aligned buffer of 5 * |length| samples in length.
+// @param channel_0 Output buffer of mono data for the first channel.
+// @param channel_1 Output buffer of mono data for the second channel.
+// @param channel_2 Output buffer of mono data for the third channel.
+// @param channel_3 Output buffer of mono data for the fourth channel.
+void DeinterleaveQuad(size_t length, const float* interleaved_buffer,
+ float* workspace, float* channel_0, float* channel_1,
+ float* channel_2, float* channel_3);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_BASE_SIMD_UTILS_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/simd_utils_test.cc b/src/3rdparty/resonance-audio/resonance_audio/base/simd_utils_test.cc
new file mode 100644
index 000000000..7fb8e83b5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/simd_utils_test.cc
@@ -0,0 +1,711 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "base/simd_utils.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+// Input lengths (purposefully chosen not to be a multiple of SIMD_LENGTH).
+const size_t kInputSize = 7;
+const size_t kNumTestChannels = 3;
+const size_t kNumQuadChannels = 4;
+
+// Length of deinterleaved and interleaved buffers.
+const size_t kHalfSize = 21;
+const size_t kFullSize = kHalfSize * 2;
+const size_t kQuadSize = kHalfSize * 4;
+const size_t kPentSize = kHalfSize * 5;
+
+// The int16 values for the deinterleaving test.
+const int16_t kOne = 0x0001;
+const int16_t kTwo = 0x0002;
+const int16_t kThree = 0x0003;
+const int16_t kFour = 0x0004;
+const int16_t kMax = 0x7FFF;
+const int16_t kMin = -0x7FFF;
+
+// Epsilon for conversion from int16_t back to float.
+const float kFloatEpsilon = 1e-4f;
+const int16_t kIntEpsilon = 1;
+
+// Intereleaved data.
+const int16_t kInterleavedInput[kFullSize] = {
+ kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne,
+ kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo,
+ kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne,
+ kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo, kOne, kTwo};
+
+// Corresponding values for float and 16 bit int.
+const float kFloatInput[kInputSize] = {0.5f, -0.5f, 1.0f, -1.0f,
+ 1.0f, -1.0f, 0.0f};
+const int16_t kIntInput[kInputSize] = {0x4000, -0x4000, 0x7FFF, -0x7FFF,
+ 0x7FFF, -0x7FFF, 0};
+
+TEST(SimdUtilsTest, IsAlignedTest) {
+ AudioBuffer aligned_audio_buffer(kNumMonoChannels, kInputSize);
+ const float* aligned_ptr = aligned_audio_buffer[0].begin();
+ const float* unaligned_ptr = aligned_ptr + 1;
+ EXPECT_TRUE(IsAligned(aligned_ptr));
+ EXPECT_FALSE(IsAligned(unaligned_ptr));
+}
+
+TEST(SimdUtilsTest, AddPointwiseTest) {
+ const float kResult = 3.0f;
+ AudioBuffer aligned_audio_buffer(kNumTestChannels, kInputSize);
+ aligned_audio_buffer.Clear();
+ for (size_t i = 0; i < kInputSize; ++i) {
+ aligned_audio_buffer[0][i] = 1.0f;
+ aligned_audio_buffer[1][i] = 2.0f;
+ }
+ AddPointwise(kInputSize, &aligned_audio_buffer[0][0],
+ &aligned_audio_buffer[1][0], &aligned_audio_buffer[2][0]);
+ for (size_t i = 0; i < kInputSize; ++i) {
+ EXPECT_FLOAT_EQ(aligned_audio_buffer[2][i], kResult);
+ }
+}
+
+TEST(SimdUtilsTest, AddPointwiseInPlaceTest) {
+ AudioBuffer aligned_audio_buffer(kNumStereoChannels, kInputSize);
+ aligned_audio_buffer.Clear();
+ for (size_t i = 0; i < kInputSize; ++i) {
+ aligned_audio_buffer[0][i] = static_cast<float>(i);
+ }
+ const size_t kRuns = 2;
+ for (size_t i = 0; i < kRuns; ++i) {
+ AddPointwise(kInputSize, &aligned_audio_buffer[0][0],
+ &aligned_audio_buffer[1][0], &aligned_audio_buffer[1][0]);
+ }
+ for (size_t i = 0; i < kInputSize; ++i) {
+ EXPECT_FLOAT_EQ(aligned_audio_buffer[1][i], static_cast<float>(i * kRuns));
+ }
+}
+
+TEST(SimdUtilsTest, SubtractPointwiseTest) {
+ AudioBuffer aligned_audio_buffer(kNumStereoChannels, kInputSize);
+ aligned_audio_buffer.Clear();
+ for (size_t i = 0; i < kInputSize; ++i) {
+ aligned_audio_buffer[0][i] = static_cast<float>(i);
+ aligned_audio_buffer[1][i] = static_cast<float>(2 * i);
+ }
+ SubtractPointwise(kInputSize, &aligned_audio_buffer[0][0],
+ &aligned_audio_buffer[1][0], &aligned_audio_buffer[1][0]);
+ for (size_t i = 0; i < kInputSize; ++i) {
+ EXPECT_FLOAT_EQ(aligned_audio_buffer[1][i], aligned_audio_buffer[0][i]);
+ }
+}
+
+TEST(SimdUtilsTest, MultiplyPointwiseTest) {
+ AudioBuffer aligned_audio_buffer(kNumStereoChannels, kInputSize);
+ aligned_audio_buffer.Clear();
+ for (size_t i = 0; i < kInputSize; ++i) {
+ aligned_audio_buffer[0][i] = static_cast<float>(i);
+ aligned_audio_buffer[1][i] = static_cast<float>(i);
+ }
+ MultiplyPointwise(kInputSize, &aligned_audio_buffer[0][0],
+ &aligned_audio_buffer[1][0], &aligned_audio_buffer[1][0]);
+ for (size_t i = 0; i < kInputSize; ++i) {
+ EXPECT_FLOAT_EQ(aligned_audio_buffer[1][i],
+ aligned_audio_buffer[0][i] * aligned_audio_buffer[0][i]);
+ }
+}
+
+TEST(SimdUtilsTest, MultiplyAndAccumulatePointwiseTest) {
+ const float kInitialOutput = 1.0f;
+ AudioBuffer aligned_input_buffer(kNumStereoChannels, kInputSize);
+ aligned_input_buffer.Clear();
+ AudioBuffer aligned_output_buffer(kNumMonoChannels, kInputSize);
+ aligned_output_buffer.Clear();
+ for (size_t i = 0; i < kInputSize; ++i) {
+ aligned_input_buffer[0][i] = static_cast<float>(i);
+ aligned_input_buffer[1][i] = static_cast<float>(i);
+ aligned_output_buffer[0][i] = kInitialOutput;
+ }
+ MultiplyAndAccumulatePointwise(kInputSize, &aligned_input_buffer[0][0],
+ &aligned_input_buffer[1][0],
+ &aligned_output_buffer[0][0]);
+ for (size_t i = 0; i < kInputSize; ++i) {
+ EXPECT_FLOAT_EQ(aligned_output_buffer[0][i],
+ kInitialOutput + aligned_input_buffer[0][i] *
+ aligned_input_buffer[1][i]);
+ }
+}
+
+TEST(SimdUtilsTest, ScalarMultiplyTest) {
+ AudioBuffer aligned_audio_buffer(kNumStereoChannels, kInputSize);
+ aligned_audio_buffer.Clear();
+ for (size_t i = 0; i < kInputSize; ++i) {
+ aligned_audio_buffer[0][i] = 1.0f;
+ aligned_audio_buffer[1][i] = 0.0f;
+ }
+ const float gain = 0.5f;
+ ScalarMultiply(kInputSize, gain, &aligned_audio_buffer[0][0],
+ &aligned_audio_buffer[1][0]);
+ for (size_t i = 0; i < kInputSize; ++i) {
+ EXPECT_FLOAT_EQ(aligned_audio_buffer[1][i],
+ aligned_audio_buffer[0][i] * gain);
+ }
+}
+
+TEST(SimdUtilsTest, ScalarMultiplyAndAccumuateTest) {
+ const float kResult = 2.0f;
+ AudioBuffer aligned_audio_buffer(kNumStereoChannels, kInputSize);
+ aligned_audio_buffer.Clear();
+ for (size_t i = 0; i < kInputSize; ++i) {
+ aligned_audio_buffer[0][i] = 0.5f;
+ aligned_audio_buffer[1][i] = 1.0f;
+ }
+ const float gain = 2.0f;
+ ScalarMultiplyAndAccumulate(kInputSize, gain, &aligned_audio_buffer[0][0],
+ &aligned_audio_buffer[1][0]);
+ for (size_t i = 0; i < kInputSize; ++i) {
+ EXPECT_FLOAT_EQ(aligned_audio_buffer[1][i], kResult);
+ }
+}
+
+TEST(SimdUtilsTest, SqrtTest) {
+ const std::vector<float> kNumbers{130.0f, 13.0f, 1.3f,
+ 0.13f, 0.013f, 0.0013f};
+ AudioBuffer numbers(kNumMonoChannels, kNumbers.size());
+ AudioBuffer approximate(kNumMonoChannels, kNumbers.size());
+ numbers[0] = kNumbers;
+ const float kSqrtEpsilon = 2e-3f;
+
+ Sqrt(kNumbers.size(), numbers[0].begin(), approximate[0].begin());
+
+ for (size_t i = 0; i < kNumbers.size(); ++i) {
+ const float actual = std::sqrt(kNumbers[i]);
+ EXPECT_LT(std::abs(actual - approximate[0][i]) / actual, kSqrtEpsilon);
+ }
+}
+
+TEST(SimdUtilsTest, ReciprocalSqrtTest) {
+ const std::vector<float> kNumbers{130.0f, 13.0f, 1.3f,
+ 0.13f, 0.013f, 0.0013f};
+ AudioBuffer numbers(kNumMonoChannels, kNumbers.size());
+ AudioBuffer sqrt(kNumMonoChannels, kNumbers.size());
+ AudioBuffer recip_sqrt(kNumMonoChannels, kNumbers.size());
+
+ Sqrt(kNumbers.size(), numbers[0].begin(), sqrt[0].begin());
+ ReciprocalSqrt(kNumbers.size(), numbers[0].begin(), recip_sqrt[0].begin());
+
+ for (size_t i = 0; i < kNumbers.size(); ++i) {
+ EXPECT_FLOAT_EQ(1.0f / recip_sqrt[0][i], sqrt[0][i]);
+ }
+}
+
+// Tests that the correct complex magnitudes are calculated for a range of
+// complex numbers with both positive and negative imaginary part.
+TEST(SimdUtilsTest, ApproxComplexMagnitudeTest) {
+ const size_t kFramesPerBuffer = 17;
+ // Check that we are correct to within 0.5% of each value.
+ const float kErrEpsilon = 5e-3f;
+ AudioBuffer complex_buffer(kNumMonoChannels, 2 * kFramesPerBuffer);
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ const size_t j = 2 * i;
+ complex_buffer[0][j] = static_cast<float>(i);
+ complex_buffer[0][j + 1] = ((i % 2) ? -1.0f : 1.0f) * static_cast<float>(i);
+ }
+ AudioBuffer magnitude_buffer(kNumMonoChannels, kFramesPerBuffer);
+
+ ApproxComplexMagnitude(kFramesPerBuffer, complex_buffer[0].begin(),
+ magnitude_buffer[0].begin());
+
+ for (size_t sample = 0; sample < kFramesPerBuffer; ++sample) {
+ const float expected = static_cast<float>(sample) * kSqrtTwo;
+ // Check its correct to within 0.5%.
+ EXPECT_NEAR(magnitude_buffer[0][sample], expected, kErrEpsilon * expected);
+ }
+}
+
+// Tests that the ComplexInterleavedFormatFromMagnitudeAndSinCosPhase() method
+// correctly recovers the frequency response from magnitude and phase.
+TEST(SimdUtilsTest, ComplexInterleavedFormatFromMagnitudeAndSinCosPhaseTest) {
+ // The folowing vectors contain the inverse sines and cosines of the numbers
+ // 0 to 0.75 in steps of 0.05 (calculated in MATLAB).
+ const size_t kLength = 16;
+ AudioBuffer cos_vec(kNumMonoChannels, kLength);
+ cos_vec[0] = {1.5708f, 1.5208f, 1.4706f, 1.4202f, 1.3694f, 1.3181f,
+ 1.2661f, 1.2132f, 1.1593f, 1.1040f, 1.0472f, 0.9884f,
+ 0.9273f, 0.8632f, 0.7954f, 0.7227f};
+ AudioBuffer sin_vec(kNumMonoChannels, kLength);
+ sin_vec[0] = {0.0000f, 0.0500f, 0.1002f, 0.1506f, 0.2014f, 0.2527f,
+ 0.3047f, 0.3576f, 0.4115f, 0.4668f, 0.5236f, 0.5824f,
+ 0.6435f, 0.7076f, 0.7754f, 0.8481f};
+ const float kMagnitude = 10.0f;
+ AudioBuffer magnitude(kNumMonoChannels, kLength);
+ std::fill(magnitude[0].begin(), magnitude[0].end(), kMagnitude);
+ const size_t output_size = 2 * sin_vec.num_frames();
+ AudioBuffer output(kNumMonoChannels, output_size);
+ output.Clear();
+
+ ComplexInterleavedFormatFromMagnitudeAndSinCosPhase(
+ output_size, &magnitude[0][0], &cos_vec[0][0], &sin_vec[0][0],
+ &output[0][0]);
+
+ for (size_t i = 0, j = 0; i < output_size; i += 2, ++j) {
+ EXPECT_FLOAT_EQ(output[0][i], kMagnitude * cos_vec[0][j]);
+ EXPECT_FLOAT_EQ(output[0][i + 1], kMagnitude * sin_vec[0][j]);
+ }
+}
+
+TEST(SimdUtilsTest, StereoMonoTest) {
+ const float kResult = 2.0f / std::sqrt(2.0f);
+ AudioBuffer aligned_audio_buffer(kNumTestChannels, kInputSize);
+ aligned_audio_buffer.Clear();
+ for (size_t i = 0; i < kInputSize; ++i) {
+ aligned_audio_buffer[0][i] = 1.0f;
+ aligned_audio_buffer[1][i] = 1.0f;
+ }
+ MonoFromStereoSimd(kInputSize, &aligned_audio_buffer[0][0],
+ &aligned_audio_buffer[1][0], &aligned_audio_buffer[2][0]);
+ for (size_t i = 0; i < kInputSize; ++i) {
+ EXPECT_FLOAT_EQ(aligned_audio_buffer[2][i], kResult);
+ }
+ // Perform inverse operation.
+ StereoFromMonoSimd(kInputSize, &aligned_audio_buffer[2][0],
+ &aligned_audio_buffer[0][0], &aligned_audio_buffer[1][0]);
+ for (size_t i = 0; i < kInputSize; ++i) {
+ EXPECT_FLOAT_EQ(aligned_audio_buffer[0][i], 1.0f);
+ EXPECT_FLOAT_EQ(aligned_audio_buffer[1][i], 1.0f);
+ }
+}
+
+TEST(SimdUtilsTest, InterleaveAlignedInt16Test) {
+ AudioBuffer::AlignedInt16Vector interleaved(kFullSize);
+ AudioBuffer::AlignedInt16Vector channel_0(kHalfSize);
+ AudioBuffer::AlignedInt16Vector channel_1(kHalfSize);
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ channel_0[i] = kOne;
+ channel_1[i] = kTwo;
+ }
+
+ InterleaveStereo(kHalfSize, channel_0.data(), channel_1.data(),
+ interleaved.data());
+
+ for (size_t i = 0; i < kFullSize; ++i) {
+ const int16_t value = (i % 2 == 0) ? kOne : kTwo;
+ EXPECT_EQ(interleaved[i], value);
+ }
+}
+
+TEST(SimdUtilsTest, InterleaveUnalignedInt16Test) {
+ AudioBuffer::AlignedInt16Vector interleaved(kFullSize);
+ AudioBuffer::AlignedInt16Vector channel_0(kHalfSize);
+ AudioBuffer::AlignedInt16Vector channel_1(kHalfSize);
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ channel_0[i] = kOne;
+ channel_1[i] = kTwo;
+ }
+
+ InterleaveStereo(kHalfSize, channel_0.data(), channel_1.data(),
+ interleaved.data());
+
+ for (size_t i = 0; i < kFullSize; ++i) {
+ const int16_t value = (i % 2 == 0) ? kOne : kTwo;
+ EXPECT_EQ(interleaved[i], value);
+ }
+}
+
+TEST(SimdUtilsTest, DeinterleaveAlignedInt16Test) {
+ AudioBuffer::AlignedInt16Vector interleaved(kFullSize);
+ AudioBuffer::AlignedInt16Vector channel_0(kHalfSize);
+ AudioBuffer::AlignedInt16Vector channel_1(kHalfSize);
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kFullSize; ++i) {
+ interleaved[i] = kInterleavedInput[i];
+ }
+
+ // Clear the output buffers.
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ channel_0[i] = static_cast<int16_t>(0);
+ channel_1[i] = static_cast<int16_t>(0);
+ }
+
+ // Test the case where input is aligned.
+ DeinterleaveStereo(kHalfSize, interleaved.data(), channel_0.data(),
+ channel_1.data());
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ EXPECT_EQ(channel_0[i], kOne);
+ EXPECT_EQ(channel_1[i], kTwo);
+ }
+}
+
+TEST(SimdUtilsTest, DeinterleaveUnalignedInt16Test) {
+ AudioBuffer::AlignedInt16Vector channel_0(kHalfSize);
+ AudioBuffer::AlignedInt16Vector channel_1(kHalfSize);
+
+ // Clear the output buffers.
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ channel_0[i] = static_cast<int16_t>(0);
+ channel_1[i] = static_cast<int16_t>(0);
+ }
+
+ // Test the case where input is unaligned.
+ DeinterleaveStereo(kHalfSize, kInterleavedInput, channel_0.data(),
+ channel_1.data());
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ EXPECT_EQ(channel_0[i], kOne);
+ EXPECT_EQ(channel_1[i], kTwo);
+ }
+}
+
+TEST(SimdUtilsTest, DeinterleaveAlignedInt16ConvertToFloatTest) {
+ AudioBuffer::AlignedInt16Vector interleaved(kFullSize);
+ AudioBuffer planar(kNumStereoChannels, kHalfSize);
+ AudioBuffer::Channel& channel_0 = planar[0];
+ AudioBuffer::Channel& channel_1 = planar[1];
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kFullSize; ++i) {
+ interleaved[i] = i % 2 ? kMin : kMax;
+ }
+
+ // Clear the output buffers.
+ planar.Clear();
+
+ // Test the case where input is aligned.
+ DeinterleaveStereo(kHalfSize, interleaved.data(), channel_0.begin(),
+ channel_1.begin());
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ EXPECT_NEAR(channel_0[i], 1.0f, kEpsilonFloat);
+ EXPECT_NEAR(channel_1[i], -1.0f, kEpsilonFloat);
+ }
+}
+
+TEST(SimdUtilsTest, DeinterleaveUnalignedInt16ConvertToFloatTest) {
+ int16_t interleaved[kFullSize];
+ AudioBuffer planar(kNumStereoChannels, kHalfSize);
+ AudioBuffer::Channel& channel_0 = planar[0];
+ AudioBuffer::Channel& channel_1 = planar[1];
+
+ // Fill the unaligned input buffer.
+ for (size_t i = 0; i < kFullSize; ++i) {
+ interleaved[i] = i % 2 ? kMin : kMax;
+ }
+
+ // Clear the output buffers.
+ planar.Clear();
+
+ // Test the case where input is unaligned.
+ DeinterleaveStereo(kHalfSize, interleaved, channel_0.begin(),
+ channel_1.begin());
+
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ EXPECT_NEAR(channel_0[i], 1.0f, kEpsilonFloat);
+ EXPECT_NEAR(channel_1[i], -1.0f, kEpsilonFloat);
+ }
+}
+
+TEST(SimdUtilsTest, InterleaveAlignedFloatTest) {
+ AudioBuffer interleaved(kNumMonoChannels, kFullSize);
+ AudioBuffer planar(kNumStereoChannels, kHalfSize);
+ AudioBuffer::Channel& channel_0 = planar[0];
+ AudioBuffer::Channel& channel_1 = planar[1];
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ channel_0[i] = 1.0f;
+ channel_1[i] = 2.0f;
+ }
+
+ InterleaveStereo(kHalfSize, channel_0.begin(), channel_1.begin(),
+ interleaved[0].begin());
+
+ for (size_t i = 0; i < kFullSize; ++i) {
+ const float value = (i % 2 == 0) ? 1.0f : 2.0f;
+ EXPECT_FLOAT_EQ(interleaved[0][i], value);
+ }
+}
+
+TEST(SimdUtilsTest, InterleaveUnalignedFloatTest) {
+ float interleaved[kFullSize];
+ AudioBuffer planar(kNumStereoChannels, kHalfSize);
+ AudioBuffer::Channel& channel_0 = planar[0];
+ AudioBuffer::Channel& channel_1 = planar[1];
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ channel_0[i] = 1.0f;
+ channel_1[i] = 2.0f;
+ }
+
+ InterleaveStereo(kHalfSize, channel_0.begin(), channel_1.begin(),
+ interleaved);
+
+ for (size_t i = 0; i < kFullSize; ++i) {
+ const float value = (i % 2 == 0) ? 1.0f : 2.0f;
+ EXPECT_FLOAT_EQ(interleaved[i], value);
+ }
+}
+
+TEST(SimdUtilsTest, InterleaveAlignedFloatConvertToInt16Test) {
+ AudioBuffer::AlignedInt16Vector interleaved(kFullSize);
+ AudioBuffer planar(kNumStereoChannels, kHalfSize);
+ AudioBuffer::Channel& channel_0 = planar[0];
+ AudioBuffer::Channel& channel_1 = planar[1];
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ channel_0[i] = 1.0f;
+ channel_1[i] = -1.0f;
+ }
+
+ InterleaveStereo(kHalfSize, channel_0.begin(), channel_1.begin(),
+ interleaved.data());
+
+ for (size_t i = 0; i < kFullSize; ++i) {
+ const int16_t value = i % 2 ? kMin : kMax;
+ EXPECT_NEAR(interleaved[i], value, kIntEpsilon);
+ }
+}
+
+TEST(SimdUtilsTest, InterleaveUnalignedFloatConvertToInt16Test) {
+ int16_t interleaved[kFullSize];
+ AudioBuffer planar(kNumStereoChannels, kHalfSize);
+ AudioBuffer::Channel& channel_0 = planar[0];
+ AudioBuffer::Channel& channel_1 = planar[1];
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ channel_0[i] = 1.0f;
+ channel_1[i] = -1.0f;
+ }
+
+ InterleaveStereo(kHalfSize, channel_0.begin(), channel_1.begin(),
+ interleaved);
+
+ for (size_t i = 0; i < kFullSize; ++i) {
+ const int16_t value = i % 2 ? kMin : kMax;
+ EXPECT_NEAR(interleaved[i], value, kIntEpsilon);
+ }
+}
+
+TEST(SimdUtilsTest, DeinterleaveAlignedFloatTest) {
+ AudioBuffer interleaved(kNumMonoChannels, kFullSize);
+ AudioBuffer planar(kNumStereoChannels, kHalfSize);
+ AudioBuffer::Channel& channel_0 = planar[0];
+ AudioBuffer::Channel& channel_1 = planar[1];
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kFullSize; ++i) {
+ interleaved[0][i] = (i % 2 == 0) ? 1.0f : 2.0f;
+ }
+
+ // Clear the output buffers.
+ planar.Clear();
+
+ // Test the case where input is aligned.
+ DeinterleaveStereo(kHalfSize, interleaved[0].begin(), channel_0.begin(),
+ channel_1.begin());
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ EXPECT_FLOAT_EQ(channel_0[i], 1.0f);
+ EXPECT_FLOAT_EQ(channel_1[i], 2.0f);
+ }
+}
+
+TEST(SimdUtilsTest, DeinterleaveUnalignedFloatTest) {
+ float interleaved[kFullSize];
+ AudioBuffer planar(kNumStereoChannels, kHalfSize);
+ AudioBuffer::Channel& channel_0 = planar[0];
+ AudioBuffer::Channel& channel_1 = planar[1];
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kFullSize; ++i) {
+ interleaved[i] = (i % 2 == 0) ? 1.0f : 2.0f;
+ }
+
+ // Clear the output buffers.
+ planar.Clear();
+
+ // Test the case where input is unaligned.
+ DeinterleaveStereo(kHalfSize, interleaved, channel_0.begin(),
+ channel_1.begin());
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ EXPECT_FLOAT_EQ(channel_0[i], 1.0f);
+ EXPECT_FLOAT_EQ(channel_1[i], 2.0f);
+ }
+}
+
+TEST(SimdUtilsTest, InterleaveQuadInt16Test) {
+ AudioBuffer::AlignedInt16Vector interleaved(kQuadSize);
+ AudioBuffer::AlignedInt16Vector workspace(kPentSize);
+ AudioBuffer::AlignedInt16Vector channel_0(kHalfSize);
+ AudioBuffer::AlignedInt16Vector channel_1(kHalfSize);
+ AudioBuffer::AlignedInt16Vector channel_2(kHalfSize);
+ AudioBuffer::AlignedInt16Vector channel_3(kHalfSize);
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ channel_0[i] = kOne;
+ channel_1[i] = kTwo;
+ channel_2[i] = kThree;
+ channel_3[i] = kFour;
+ }
+
+ InterleaveQuad(kHalfSize, channel_0.data(), channel_1.data(),
+ channel_2.data(), channel_3.data(), workspace.data(),
+ interleaved.data());
+
+ for (size_t i = 0; i < kQuadSize; ++i) {
+ const int16_t value = static_cast<int16_t>(1 + (i % kNumQuadChannels));
+ EXPECT_FLOAT_EQ(interleaved[i], value);
+ }
+}
+
+TEST(SimdUtilsTest, DeinterleaveQuadInt16Test) {
+ AudioBuffer::AlignedInt16Vector interleaved(kQuadSize);
+ AudioBuffer::AlignedInt16Vector workspace(kPentSize);
+ AudioBuffer::AlignedInt16Vector channel_0(kHalfSize);
+ AudioBuffer::AlignedInt16Vector channel_1(kHalfSize);
+ AudioBuffer::AlignedInt16Vector channel_2(kHalfSize);
+ AudioBuffer::AlignedInt16Vector channel_3(kHalfSize);
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kQuadSize; ++i) {
+ interleaved[i] = static_cast<int16_t>(1 + (i % kNumQuadChannels));
+ }
+
+ // Clear the output buffers.
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ channel_0[i] = static_cast<int16_t>(0);
+ channel_1[i] = static_cast<int16_t>(0);
+ channel_2[i] = static_cast<int16_t>(0);
+ channel_3[i] = static_cast<int16_t>(0);
+ }
+
+ // Test the case where input is aligned.
+ DeinterleaveQuad(kHalfSize, interleaved.data(), workspace.data(),
+ channel_0.data(), channel_1.data(), channel_2.data(),
+ channel_3.data());
+
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ EXPECT_EQ(channel_0[i], kOne);
+ EXPECT_EQ(channel_1[i], kTwo);
+ EXPECT_EQ(channel_2[i], kThree);
+ EXPECT_EQ(channel_3[i], kFour);
+ }
+}
+
+TEST(SimdUtilsTest, InterleaveQuadFloatTest) {
+ AudioBuffer interleaved(kNumMonoChannels, kQuadSize);
+ AudioBuffer workspace(kNumMonoChannels, kPentSize);
+ AudioBuffer planar(kNumQuadChannels, kHalfSize);
+ AudioBuffer::Channel& channel_0 = planar[0];
+ AudioBuffer::Channel& channel_1 = planar[1];
+ AudioBuffer::Channel& channel_2 = planar[2];
+ AudioBuffer::Channel& channel_3 = planar[3];
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ channel_0[i] = 1.0f;
+ channel_1[i] = 2.0f;
+ channel_2[i] = 3.0f;
+ channel_3[i] = 4.0f;
+ }
+
+ InterleaveQuad(kHalfSize, channel_0.begin(), channel_1.begin(),
+ channel_2.begin(), channel_3.begin(), workspace[0].begin(),
+ interleaved[0].begin());
+
+ for (size_t i = 0; i < kQuadSize; ++i) {
+ const float value = static_cast<float>(1 + (i % kNumQuadChannels));
+ EXPECT_FLOAT_EQ(interleaved[0][i], value);
+ }
+}
+
+TEST(SimdUtilsTest, DeinterleaveQuadFloatTest) {
+ AudioBuffer interleaved(kNumMonoChannels, kQuadSize);
+ AudioBuffer workspace(kNumMonoChannels, kPentSize);
+ AudioBuffer planar(kNumQuadChannels, kHalfSize);
+ AudioBuffer::Channel& channel_0 = planar[0];
+ AudioBuffer::Channel& channel_1 = planar[1];
+ AudioBuffer::Channel& channel_2 = planar[2];
+ AudioBuffer::Channel& channel_3 = planar[3];
+
+ // Fill the aligned input buffer.
+ for (size_t i = 0; i < kQuadSize; ++i) {
+ interleaved[0][i] = static_cast<float>(1 + (i % kNumQuadChannels));
+ }
+
+ // Clear the output buffers.
+ planar.Clear();
+
+ // Test the case where input is aligned.
+ DeinterleaveQuad(kHalfSize, interleaved[0].begin(), workspace[0].begin(),
+ channel_0.begin(), channel_1.begin(), channel_2.begin(),
+ channel_3.begin());
+
+ for (size_t i = 0; i < kHalfSize; ++i) {
+ EXPECT_FLOAT_EQ(channel_0[i], 1.0f);
+ EXPECT_FLOAT_EQ(channel_1[i], 2.0f);
+ EXPECT_FLOAT_EQ(channel_2[i], 3.0f);
+ EXPECT_FLOAT_EQ(channel_3[i], 4.0f);
+ }
+}
+
+TEST(SimdUtilsTest, Int16FromFloatTest) {
+ AudioBuffer float_buffer(kNumMonoChannels, kInputSize);
+ float_buffer.Clear();
+
+ AudioBuffer::AlignedInt16Vector int_buffer(kInputSize);
+
+ for (size_t i = 0; i < kInputSize; ++i) {
+ float_buffer[0][i] = kFloatInput[i];
+ }
+
+ Int16FromFloat(kInputSize, &(float_buffer[0][0]), int_buffer.data());
+
+ for (size_t i = 0; i < kInputSize; ++i) {
+ EXPECT_NEAR(int_buffer[i], kIntInput[i], kIntEpsilon);
+ }
+}
+
+TEST(SimdUtilsTest, FloatFromInt16Test) {
+ AudioBuffer float_buffer(kNumMonoChannels, kInputSize);
+ float_buffer.Clear();
+
+ AudioBuffer::AlignedInt16Vector int_buffer(kInputSize);
+
+ for (size_t i = 0; i < kInputSize; ++i) {
+ int_buffer[i] = static_cast<int16_t>(kIntInput[i]);
+ }
+
+ FloatFromInt16(kInputSize, int_buffer.data(), &(float_buffer[0][0]));
+
+ for (size_t i = 0; i < kInputSize; i += 2) {
+ EXPECT_NEAR(float_buffer[0][i], kFloatInput[i], kFloatEpsilon);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/source_parameters.h b/src/3rdparty/resonance-audio/resonance_audio/base/source_parameters.h
new file mode 100644
index 000000000..bf24a424e
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/source_parameters.h
@@ -0,0 +1,101 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_BASE_SOURCE_PARAMETERS_H_
+#define RESONANCE_AUDIO_BASE_SOURCE_PARAMETERS_H_
+
+#include "api/resonance_audio_api.h"
+
+#include "base/constants_and_types.h"
+#include "base/object_transform.h"
+
+namespace vraudio {
+
+// Gain attenuation types for audio sources.
+enum AttenuationType {
+ kInput = 0,
+ kDirect,
+ kReflections,
+ kReverb,
+ kNumAttenuationTypes
+};
+
+// Parameters describing an audio source.
+struct SourceParameters {
+ // Object transform associated with this buffer.
+ ObjectTransform object_transform;
+
+ // Angular spread in degrees. Range [0, 360].
+ float spread_deg = 0.0f;
+
+ // Source gain factor.
+ float gain = 1.0f;
+
+ // Source gain attenuation factors to be calculated per each buffer.
+ float attenuations[kNumAttenuationTypes];
+
+ // Distance attenuation. Value 1 represents no attenuation should be applied,
+ // value 0 will fully attenuate the volume. Range [0, 1].
+ float distance_attenuation = 1.0f;
+
+ // Distance attenuation rolloff model to use.
+ DistanceRolloffModel distance_rolloff_model =
+ DistanceRolloffModel::kLogarithmic;
+
+ // Minimum distance at which to apply distance attenuation.
+ float minimum_distance = 0.0f;
+
+ // Maximum distance at which to apply distance attenuation.
+ float maximum_distance = 500.0f;
+
+ // Alpha weighting of source's directivity pattern. This sets the balance
+ // between the dipole and omnidirectional directivity patterns which combine
+ // to produce the single directivity output value. Range [0, 1], where 0 is
+ // fully omnidirectional and 1 is fully dipole.
+ float directivity_alpha = 0.0f;
+
+ // Source directivity order. Increasing this value increases the directivity
+ // towards the front of the source. Range [1, inf).
+ float directivity_order = 1.0f;
+
+ // Alpha weighting of listener's directivity pattern. This sets the balance
+ // between the dipole and omnidirectional pickup patterns which combine to
+ // produce the single output value. Range [0, 1], where 0 is fully
+ // omnidirectional and 1 is fully dipole.
+ float listener_directivity_alpha = 0.0f;
+
+ // Listener directivity order. Increasing this value increases the directivity
+ // towards the front of the listener. Range [1, inf).
+ float listener_directivity_order = 1.0f;
+
+ // Occlusion intensity. Value 0 represents no occlusion, values greater than 1
+ // represent multiple occlusions. The intensity of each occlusion is scaled
+ // in range [0, 1].
+ float occlusion_intensity = 0.0f;
+
+ // Near field effect gain. Range [0, 9].
+ float near_field_gain = 0.0f;
+
+ // Source gain factor for the room effects.
+ float room_effects_gain = 1.0f;
+
+ // Whether the source uses binaural rendering or stereo panning.
+ bool enable_hrtf = true;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_BASE_SOURCE_PARAMETERS_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/spherical_angle.cc b/src/3rdparty/resonance-audio/resonance_audio/base/spherical_angle.cc
new file mode 100644
index 000000000..4b028979d
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/spherical_angle.cc
@@ -0,0 +1,78 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "base/spherical_angle.h"
+
+#include <cmath>
+
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+SphericalAngle::SphericalAngle(float azimuth, float elevation)
+ : azimuth_(azimuth), elevation_(elevation) {}
+
+SphericalAngle::SphericalAngle() : SphericalAngle(0.0f, 0.0f) {}
+
+SphericalAngle::SphericalAngle(const SphericalAngle& other)
+ : azimuth_(other.azimuth_), elevation_(other.elevation_) {}
+
+SphericalAngle& SphericalAngle::operator=(const SphericalAngle other) {
+ if (&other == this) {
+ return *this;
+ }
+ this->azimuth_ = other.azimuth_;
+ this->elevation_ = other.elevation_;
+ return *this;
+}
+
+SphericalAngle SphericalAngle::FromWorldPosition(
+ const WorldPosition& world_position) {
+ return SphericalAngle(
+ std::atan2(-world_position[0], -world_position[2]),
+ std::atan2(world_position[1],
+ std::sqrt(world_position[0] * world_position[0] +
+ world_position[2] * world_position[2])));
+}
+
+SphericalAngle SphericalAngle::FromDegrees(float azimuth_degrees,
+ float elevation_degrees) {
+ return SphericalAngle(azimuth_degrees * kRadiansFromDegrees,
+ elevation_degrees * kRadiansFromDegrees);
+}
+
+SphericalAngle SphericalAngle::FlipAzimuth() const {
+ return SphericalAngle(-azimuth_, elevation_);
+}
+
+WorldPosition SphericalAngle::GetWorldPositionOnUnitSphere() const {
+ return WorldPosition(-std::cos(elevation_) * std::sin(azimuth_),
+ std::sin(elevation_),
+ -std::cos(elevation_) * std::cos(azimuth_));
+}
+
+SphericalAngle SphericalAngle::Rotate(const WorldRotation& rotation) const {
+ const WorldPosition original_world_position = GetWorldPositionOnUnitSphere();
+ const WorldPosition rotated_world_position =
+ rotation * original_world_position;
+ return FromWorldPosition(rotated_world_position);
+}
+
+bool SphericalAngle::operator==(const SphericalAngle& other) const {
+ return (azimuth_ == other.azimuth_) && (elevation_ == other.elevation_);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/spherical_angle.h b/src/3rdparty/resonance-audio/resonance_audio/base/spherical_angle.h
new file mode 100644
index 000000000..4559f19d1
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/spherical_angle.h
@@ -0,0 +1,87 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_BASE_SPHERICAL_ANGLE_H_
+#define RESONANCE_AUDIO_BASE_SPHERICAL_ANGLE_H_
+
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+// Represents angular position on a sphere in terms of azimuth and elevation.
+class SphericalAngle {
+ public:
+ // Constructs a spherical angle with the given azimuth and elevation.
+ SphericalAngle(float azimuth, float elevation);
+
+ // Constructs a default spherical angle (azimuth = 0, elevation = 0).
+ SphericalAngle();
+
+ // Constructs a spherical angle from a given one.
+ SphericalAngle(const SphericalAngle& other);
+
+ SphericalAngle& operator=(const SphericalAngle other);
+
+ // Returns a spherical angle representation of given |world_position| (World
+ // Space).
+ //
+ // @param world_position 3D position in world space.
+ // @return Spherical angle that represents the |world_position|.
+ static SphericalAngle FromWorldPosition(const WorldPosition& world_position);
+
+ // Returns a spherical angle from azimuth and elevation in degrees.
+ static SphericalAngle FromDegrees(float azimuth_degrees,
+ float elevation_degrees);
+
+ // Returns another spherical angle with the same elevation but the azimuth
+ // sign flipped.
+ //
+ // @return Horizontally flipped version of the spherical angle.
+ SphericalAngle FlipAzimuth() const;
+
+ // Returns the |WorldPosition| coordinates (World Space) on the unit sphere
+ // corresponding to this spherical angle. The transformation is
+ // defined as such:
+ // x = -cos(elevation) * sin(azimuth)
+ // y = sin(elevation)
+ // z = -cos(elevation) * cos(azimuth)
+ //
+ // @return 3D position in world space.
+ WorldPosition GetWorldPositionOnUnitSphere() const;
+
+ // Returns the rotated version of the spherical angle using given
+ // |WorldRotation|.
+ //
+ // @param rotation Rotation to be applied to the spherical angle.
+ // @return Rotated version of the spherical angle.
+ SphericalAngle Rotate(const WorldRotation& rotation) const;
+
+ void set_azimuth(float azimuth) { azimuth_ = azimuth; }
+ void set_elevation(float elevation) { elevation_ = elevation; }
+
+ float azimuth() const { return azimuth_; }
+ float elevation() const { return elevation_; }
+
+ bool operator==(const SphericalAngle& other) const;
+
+ private:
+ float azimuth_;
+ float elevation_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_BASE_SPHERICAL_ANGLE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/spherical_angle_test.cc b/src/3rdparty/resonance-audio/resonance_audio/base/spherical_angle_test.cc
new file mode 100644
index 000000000..49ee16d5d
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/spherical_angle_test.cc
@@ -0,0 +1,122 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "base/spherical_angle.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+// Spherical angle to be used in the rotation tests.
+const float kAzimuth = 0.0f;
+const float kElevation = 0.0f;
+const SphericalAngle kSphericalAngle(0.0f, 0.0f);
+
+// Arbitrary rotation angle to be used in the rotation tests.
+const float kRotationAngle = 10.0f * kRadiansFromDegrees;
+
+// Tests that the GetWorldPositionOnUnitSphere() and FromWorldPosition()
+// functions act as perfect inverses of one another for angles defined on the
+// unit sphere (in this case the vraudio cube speaker layout).
+TEST(SphericalAngleTest, CartesianToSphericalAndBackTest) {
+ // Azimuth and elevation angles of the cubic spherical loudspeaker array.
+ const std::vector<SphericalAngle> kCubeAngles = {
+ SphericalAngle::FromDegrees(45.0f, 35.26f),
+ SphericalAngle::FromDegrees(-45.0f, 35.26f),
+ SphericalAngle::FromDegrees(-135.0f, 35.26f),
+ SphericalAngle::FromDegrees(135.0f, 35.26f),
+ SphericalAngle::FromDegrees(45.0f, -35.26f),
+ SphericalAngle::FromDegrees(-45.0f, -35.26f),
+ SphericalAngle::FromDegrees(-135.0f, -35.26f),
+ SphericalAngle::FromDegrees(135.0f, -35.26f)};
+
+ for (size_t i = 0; i < kCubeAngles.size(); ++i) {
+ const WorldPosition position =
+ kCubeAngles[i].GetWorldPositionOnUnitSphere();
+ const SphericalAngle angle = SphericalAngle::FromWorldPosition(position);
+ EXPECT_EQ(kCubeAngles[i].azimuth(), angle.azimuth());
+ EXPECT_EQ(kCubeAngles[i].elevation(), angle.elevation());
+ }
+}
+
+// Tests the horizontal angle flip across the median plane.
+TEST(SphericalAngleTest, FlipTest) {
+ const std::vector<SphericalAngle> kTestAngles = {
+ SphericalAngle::FromDegrees(45.0f, 35.26f),
+ SphericalAngle::FromDegrees(-15.0f, -10.0f)};
+
+ for (size_t i = 0; i < kTestAngles.size(); ++i) {
+ SphericalAngle flipped_spherical_angle = kTestAngles[i].FlipAzimuth();
+
+ // Check if the flipped spherical anglee is correct.
+ EXPECT_NEAR(kTestAngles[i].azimuth(), -flipped_spherical_angle.azimuth(),
+ kEpsilonFloat);
+ EXPECT_NEAR((kTestAngles[i].elevation()),
+ flipped_spherical_angle.elevation(), kEpsilonFloat);
+ }
+}
+
+// Tests that the Rotate() function correctly rotates the spherical angle
+// against the x axis (right facing).
+TEST(SphericalAngleTest, RotateXTest) {
+ const WorldPosition kAxis = {1.0f, 0.0f, 0.0f};
+ const WorldRotation kRotation(AngleAxisf(kRotationAngle, kAxis));
+ // Rotate against the x axis (right facing).
+
+ const SphericalAngle kXrotatedSphericalAngle =
+ kSphericalAngle.Rotate(kRotation);
+
+ // Check if the rotated spherical angle is correct.
+ EXPECT_NEAR(kAzimuth, kXrotatedSphericalAngle.azimuth(), kEpsilonFloat);
+ EXPECT_NEAR((kElevation + kRotationAngle),
+ kXrotatedSphericalAngle.elevation(), kEpsilonFloat);
+}
+
+// Tests that the Rotate() function correctly rotates the spherical angle
+// against the y axis (upward facing).
+TEST(SphericalAngleTest, RotateYTest) {
+ const WorldPosition kAxis(0.0f, 1.0f, 0.0f);
+ const WorldRotation kRotation(AngleAxisf(kRotationAngle, kAxis));
+ // Rotate against the y axis (upward facing).
+ const SphericalAngle kYrotatedSphericalAngle =
+ kSphericalAngle.Rotate(kRotation);
+
+ // Check if the rotated spherical angle is correct.
+ EXPECT_NEAR((kAzimuth + kRotationAngle), kYrotatedSphericalAngle.azimuth(),
+ kEpsilonFloat);
+ EXPECT_NEAR(kElevation, kYrotatedSphericalAngle.elevation(), kEpsilonFloat);
+}
+
+// Tests that the Rotate() function correctly rotates the spherical angle
+// against the Z axis (forward facing).
+TEST(SphericalAngleTest, RotateZTest) {
+ const WorldPosition kAxis = {0.0f, 0.0f, 1.0f};
+ const WorldRotation kRotation(AngleAxisf(kRotationAngle, kAxis));
+ // Rotate against the z axis (forward facing).
+ const SphericalAngle kZrotatedSphericalAngle =
+ kSphericalAngle.Rotate(kRotation);
+
+ // Check if the rotated spherical angle is correct.
+ EXPECT_NEAR(kAzimuth, kZrotatedSphericalAngle.azimuth(), kEpsilonFloat);
+ EXPECT_NEAR(kElevation, kZrotatedSphericalAngle.elevation(), kEpsilonFloat);
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/base/unique_ptr_wrapper.h b/src/3rdparty/resonance-audio/resonance_audio/base/unique_ptr_wrapper.h
new file mode 100644
index 000000000..b8e848135
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/base/unique_ptr_wrapper.h
@@ -0,0 +1,33 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_BASE_UNIQUE_PTR_WRAPPER_H_
+#define RESONANCE_AUDIO_BASE_UNIQUE_PTR_WRAPPER_H_
+
+#include <memory>
+
+// Wrapper around std::unique_ptr to enable the binding of unique_ptr buffers to
+// std:function and/or lambda function.
+template <typename T>
+struct UniquePtrWrapper {
+ UniquePtrWrapper(const UniquePtrWrapper& other) : ptr(std::move(other.ptr)) {}
+ UniquePtrWrapper(UniquePtrWrapper&& other) : ptr(std::move(other.ptr)) {}
+ explicit UniquePtrWrapper(std::unique_ptr<T> buffer)
+ : ptr(std::move(buffer)) {}
+ mutable std::unique_ptr<T> ptr;
+};
+
+#endif // RESONANCE_AUDIO_BASE_UNIQUE_PTR_WRAPPER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/config/global_config.h b/src/3rdparty/resonance-audio/resonance_audio/config/global_config.h
new file mode 100644
index 000000000..4af629e89
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/config/global_config.h
@@ -0,0 +1,37 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_CONFIG_GLOBAL_CONFIG_H_
+#define RESONANCE_AUDIO_CONFIG_GLOBAL_CONFIG_H_
+
+#include "graph/graph_manager_config.h"
+
+namespace vraudio {
+
+inline GraphManagerConfig GlobalConfig() {
+ GraphManagerConfig config;
+ config.configuration_name = "Global Config";
+
+ config.max_ambisonic_order = 3;
+ config.sh_hrir_filenames = {{1, "WAV/Subject_002/SH/sh_hrir_order_1.wav"},
+ {2, "WAV/Subject_002/SH/sh_hrir_order_2.wav"},
+ {3, "WAV/Subject_002/SH/sh_hrir_order_3.wav"}};
+ return config;
+}
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_CONFIG_GLOBAL_CONFIG_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/config/source_config.cc b/src/3rdparty/resonance-audio/resonance_audio/config/source_config.cc
new file mode 100644
index 000000000..a8e8c75f7
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/config/source_config.cc
@@ -0,0 +1,76 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "config/source_config.h"
+
+namespace vraudio {
+
+SourceGraphConfig StereoPanningConfig() {
+ SourceGraphConfig config;
+ config.configuration_name = "Stereo Panning";
+
+ config.ambisonic_order = 1;
+ config.enable_hrtf = false;
+ config.enable_direct_rendering = true;
+
+ return config;
+}
+
+SourceGraphConfig BinauralLowQualityConfig() {
+ SourceGraphConfig config;
+ config.configuration_name = "Binaural Low Quality";
+
+ config.ambisonic_order = 1;
+ config.enable_hrtf = true;
+ config.enable_direct_rendering = true;
+
+ return config;
+}
+
+SourceGraphConfig BinauralMediumQualityConfig() {
+ SourceGraphConfig config;
+ config.configuration_name = "Binaural Medium Quality";
+
+ config.ambisonic_order = 2;
+ config.enable_hrtf = true;
+ config.enable_direct_rendering = true;
+
+ return config;
+}
+
+SourceGraphConfig BinauralHighQualityConfig() {
+ SourceGraphConfig config;
+ config.configuration_name = "Binaural High Quality";
+
+ config.ambisonic_order = 3;
+ config.enable_hrtf = true;
+ config.enable_direct_rendering = true;
+
+ return config;
+}
+
+SourceGraphConfig RoomEffectsOnlyConfig() {
+ SourceGraphConfig config;
+ config.configuration_name = "Room Effects Only";
+
+ config.ambisonic_order = 1;
+ config.enable_hrtf = false;
+ config.enable_direct_rendering = false;
+
+ return config;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/config/source_config.h b/src/3rdparty/resonance-audio/resonance_audio/config/source_config.h
new file mode 100644
index 000000000..21b71fc0b
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/config/source_config.h
@@ -0,0 +1,32 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_CONFIG_SOURCE_CONFIG_H_
+#define RESONANCE_AUDIO_CONFIG_SOURCE_CONFIG_H_
+
+#include "graph/source_graph_config.h"
+
+namespace vraudio {
+
+SourceGraphConfig StereoPanningConfig();
+SourceGraphConfig BinauralLowQualityConfig();
+SourceGraphConfig BinauralMediumQualityConfig();
+SourceGraphConfig BinauralHighQualityConfig();
+SourceGraphConfig RoomEffectsOnlyConfig();
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_CONFIG_SOURCE_CONFIG_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/biquad_filter.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/biquad_filter.cc
new file mode 100644
index 000000000..ca489cce3
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/biquad_filter.cc
@@ -0,0 +1,149 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/biquad_filter.h"
+
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+// This value has been chosen empirically after a series of experiments
+// performed by kellyi@ and alperg@. It was found that crossfading over 256
+// samples yielded no audible glitching artefacts and an acceptable amount of
+// delay.
+
+const size_t kIdealSamplesToIterate = 256U;
+
+} // namespace
+
+BiquadFilter::BiquadFilter(const BiquadCoefficients& coefficients,
+ size_t frames_per_buffer)
+ : biquad_delay_line_({{0.0f, 0.0f}}),
+ interpolate_flag_(false),
+ interpolate_counter_(0),
+ old_delay_line_({{0.0f, 0.0f}}),
+ samples_to_iterate_over_(frames_per_buffer > kIdealSamplesToIterate
+ ? kIdealSamplesToIterate
+ : frames_per_buffer),
+ fade_scale_(1.0f / static_cast<float>(samples_to_iterate_over_)),
+ old_coefficients_() {
+ DCHECK_GT(frames_per_buffer, 0U);
+ CHECK_GT(coefficients_.a[0], kEpsilonFloat);
+ SetCoefficients(coefficients);
+}
+
+void BiquadFilter::SetCoefficients(const BiquadCoefficients& coefficients) {
+ coefficients_ = coefficients;
+ // Normalize the coefficients for use in the |FilterSample()| function.
+ coefficients_.a[1] /= coefficients_.a[0];
+ coefficients_.a[2] /= coefficients_.a[0];
+ coefficients_.b[0] /= coefficients_.a[0];
+ coefficients_.b[1] /= coefficients_.a[0];
+ coefficients_.b[2] /= coefficients_.a[0];
+}
+
+void BiquadFilter::Filter(const AudioBuffer::Channel& input_channel,
+ AudioBuffer::Channel* output_channel) {
+ DCHECK(output_channel);
+ DCHECK_EQ(input_channel.size(), output_channel->size());
+
+ if (interpolate_flag_) {
+ for (size_t frame = 0; frame < input_channel.size(); ++frame) {
+ // Biquad coefficients are updated here.
+ UpdateInterpolate();
+ (*output_channel)[frame] = InterpolateFilterSample(input_channel[frame]);
+ }
+ } else {
+ for (size_t frame = 0; frame < input_channel.size(); ++frame) {
+ (*output_channel)[frame] = FilterSample(
+ input_channel[frame], &biquad_delay_line_, coefficients_);
+ }
+ }
+}
+
+void BiquadFilter::InterpolateToCoefficients(
+ const BiquadCoefficients& coefficients) {
+ interpolate_flag_ = true;
+ // Reset the counter so we perform the update over samples_to_iterate_over_
+ // samples.
+ interpolate_counter_ = 0;
+ // Store the old coefficients, update the new ones and transfer data between
+ // delay lines.
+ old_coefficients_ = coefficients_;
+ coefficients_ = coefficients;
+ old_delay_line_ = biquad_delay_line_;
+}
+
+void BiquadFilter::Clear() {
+ biquad_delay_line_ = {{0.0f, 0.0f}};
+ interpolate_flag_ = false;
+ interpolate_counter_ = 0;
+ old_delay_line_ = {{0.0f, 0.0f}};
+}
+
+float BiquadFilter::FilterSample(float input, std::array<float, 2>* delay_line,
+ const BiquadCoefficients& coefficients) {
+ // Using A Direct Form II Implementation Difference equation:
+ // Source: Digital Signal Processing Principles Algorithms and Applications
+ // Fourth Edition. John G. Prolakis and Dimitris G. Manolakis - Chapter 9
+ // w[n] = x[n] - (a1/a0)*w[n-1] - (a2/a0)*w[n-2]
+ // y(n) = (b0/a0)*w[n] + (b1/a0)*w[n-1] + (b2/a0)*w[n-2]
+ // where x[n] is input, w[n] is storage and y[n] is output.
+ // The division by a0 has been performed in Biquad::SetCoefficients.
+
+ // define a temporary storage value w.
+ const float w = input - (*delay_line)[0] * coefficients.a[1] -
+ (*delay_line)[1] * coefficients.a[2];
+
+ // Do second half of the calculation to generate output.
+ const float output = w * coefficients.b[0] +
+ (*delay_line)[0] * coefficients.b[1] +
+ (*delay_line)[1] * coefficients.b[2];
+
+ // Update delay line.
+ (*delay_line)[1] = (*delay_line)[0];
+ (*delay_line)[0] = w;
+
+ return output;
+}
+
+void BiquadFilter::UpdateInterpolate() {
+ if (++interpolate_counter_ > samples_to_iterate_over_) {
+ interpolate_flag_ = false;
+ }
+}
+
+float BiquadFilter::InterpolateFilterSample(float input) {
+ const float new_filter_output =
+ FilterSample(input, &biquad_delay_line_, coefficients_);
+
+ if (!interpolate_flag_) {
+ return new_filter_output;
+ } else {
+ // Process the "old" filter values.
+ const float old_filter_output =
+ FilterSample(input, &old_delay_line_, old_coefficients_);
+ // A linear crossfade between old_filter_output and new_filter_output,
+ // stepsize is fade_scale_.
+ const float weight = fade_scale_ * static_cast<float>(interpolate_counter_);
+ const float sample_diff = new_filter_output - old_filter_output;
+ return weight * sample_diff + old_filter_output;
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/biquad_filter.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/biquad_filter.h
new file mode 100644
index 000000000..2e05c3276
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/biquad_filter.h
@@ -0,0 +1,137 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_BIQUAD_FILTER_H_
+#define RESONANCE_AUDIO_DSP_BIQUAD_FILTER_H_
+
+#include <array>
+
+#include "base/audio_buffer.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+// Set of transfer function coefficients.
+struct BiquadCoefficients {
+ // Constructor takes as its arguments 6 floats representing the transfer
+ // function coefficients of a biquad filter.
+ BiquadCoefficients(float a0, float a1, float a2, float b0, float b1, float b2)
+ : a({{a0, a1, a2}}), b({{b0, b1, b2}}) {}
+
+ // Default constructor that sets a0 and b0 == 1 so that if the coefficients
+ // are used, the filter is stable and has no effect on input. i.e. input
+ // appears to just pass through.
+ BiquadCoefficients() : BiquadCoefficients(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) {}
+
+ // The denominator quadratic coefficients.
+ std::array<float, 3> a;
+ // The numerator quadratic coefficients.
+ std::array<float, 3> b;
+};
+
+// Class representing a biquad filter as its transfer function coefficients.
+// This class also performs filtering.
+class BiquadFilter {
+ public:
+ // Constructs a BiquadFilter using a BiquadCoefficients struct.
+ //
+ // @param coefficients A BiquadCoefficients to set the internal
+ // |coefficients_|.
+ // @param frames_per_buffer The number of frames in each data buffer to be
+ // processed. This is used to set the number of samples to iterate over
+ // during a change of filter state.
+ BiquadFilter(const BiquadCoefficients& coefficients,
+ size_t frames_per_buffer);
+
+ // Filter method for use with AudioBuffer::Channel.
+ //
+ // @param input_channel An AudioBuffer::Channel of input.
+ // @param output_channel A pointer to an AudioBuffer::Channel for output.
+ void Filter(const AudioBuffer::Channel& input_channel,
+ AudioBuffer::Channel* output_channel);
+
+ // Sets the fiter's coefficents to those passed, with all
+ // coefficients bar a0 scaled by 1/a0.
+ //
+ // @param coefficients A set of BiquadCoefficients.
+ void SetCoefficients(const BiquadCoefficients& coefficients);
+
+ // Sets the target coefficients to be interpolated.
+ //
+ // @param coefficents The BiquadCoefficients we wish to interpolate to over
+ // samples_to_iterate_over_ samples of the next input buffer.
+ void InterpolateToCoefficients(const BiquadCoefficients& coefficients);
+
+ // Clears the internal state of the filter, except for coefficients.
+ void Clear();
+
+ private:
+ friend class BiquadFilterInterpolateTest;
+
+ // Filters a single sample of input.
+ //
+ // @param input A single input sample from a Planar or Interleaved buffer.
+ // @param delay_line The delay_line to use (important for interpolation).
+ // @param coefficients The biquad coeffients for use in filtering.
+ // @return An output value to be placed in a Planar or Interleaved buffer.
+ float FilterSample(float input, std::array<float, 2>* delay_line,
+ const BiquadCoefficients& coefficients);
+
+ // If InterpolateToState() has been called to assign new filter coefficients,
+ // this function will be called samples_to_iterate_over_ times within the next
+ // call of the Filter() function to slowly transition to the new coefficients
+ // which were passed to InterpolateToState() previously.
+ void UpdateInterpolate();
+
+ // Filters a single sample of input while transitioning between coefficients.
+ // Performs a linear crossfade over samples_to_iterate_over_ samples.
+ //
+ // @param input A single input sample from a Planar or Interleaved buffer.
+ // @return Output value to be placed in an audio buffer.
+ float InterpolateFilterSample(float input);
+
+ // Stores the memory state of the biquad
+ std::array<float, 2> biquad_delay_line_;
+
+ // Flag that denotes whether or not we are transitioning to another set of
+ // coefficients via interpolation.
+ bool interpolate_flag_;
+
+ // Counter that denotes how far we are into transitioning to another set of
+ // coefficients via interpolation.
+ size_t interpolate_counter_;
+
+ // Stores the memory state of the biquad.
+ std::array<float, 2> old_delay_line_;
+
+ // Number of samples over which to apply the filter coefficients.
+ size_t samples_to_iterate_over_;
+
+ // Value used to crossfade between filter outputs.
+ float fade_scale_;
+
+ // A set of coefficient which are updated as we interpolate between filter
+ // coefficients.
+ BiquadCoefficients old_coefficients_;
+
+ // Represents and maintains the state of the biquad filter in terms of its
+ // transfer function coefficients.
+ BiquadCoefficients coefficients_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_BIQUAD_FILTER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/biquad_filter_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/biquad_filter_test.cc
new file mode 100644
index 000000000..c8ac6de78
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/biquad_filter_test.cc
@@ -0,0 +1,303 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/biquad_filter.h"
+
+#include <algorithm>
+#include <cmath>
+#include <numeric>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+const size_t kTestInputDataSize = 16;
+
+const float kTestFilterCoefficients[] = {1.0f, 0.0f, 0.0, 1.0f, 0.1f, 0.1f};
+
+const size_t kIdealSamplesToIterate = 256;
+
+// For use with std::transform in the StabilityTest.
+float operator_abs(float f) { return std::abs(f); }
+
+} // namespace
+
+// Tests that the filtering of a mono signal produces the correct output.
+TEST(BiquadFilterTest, FilterTest) {
+ const size_t kNumIterations = 1;
+
+ const BiquadCoefficients kCoefficients(
+ kTestFilterCoefficients[0], kTestFilterCoefficients[1],
+ kTestFilterCoefficients[2], kTestFilterCoefficients[3],
+ kTestFilterCoefficients[4], kTestFilterCoefficients[5]);
+
+ AudioBuffer input(kNumMonoChannels, kTestInputDataSize);
+ AudioBuffer output(kNumMonoChannels, kTestInputDataSize);
+ // Set the input AudioBuffer to a Kronecker delta.
+ input.Clear();
+ input[0][0] = 1.0f;
+
+ // This vector will accumulate the output from the filter over time (Only
+ // works with mono channel).
+ std::vector<float> output_accumulator;
+ output_accumulator.reserve(kNumIterations * kTestInputDataSize);
+
+ // Create a biquad filter initialized with transfer function coefficients.
+ BiquadFilter biquad(kCoefficients, kTestInputDataSize);
+
+ // Perform filtering.
+ for (size_t i = 0; i < kNumIterations; ++i) {
+ biquad.Filter(input[0], &output[0]);
+ output_accumulator.insert(output_accumulator.end(), output[0].begin(),
+ output[0].end());
+ }
+
+ // Since the denominator of the biquad coefficients is [1 0 0] we can expect
+ // the impulse response to be equal to the numerator.
+ for (size_t i = 0; i < output_accumulator.size(); ++i) {
+ if (i < 3U) {
+ EXPECT_NEAR(kTestFilterCoefficients[3 + i], output_accumulator[i],
+ kEpsilonFloat);
+ } else {
+ EXPECT_EQ(0.0f, output_accumulator[i]);
+ }
+ }
+}
+
+// Tests that the filtering of a mono signal produces the correct output.
+TEST(BiquadFilterTest, InplaceFilterTest) {
+ const size_t kNumIterations = 1;
+
+ const BiquadCoefficients kCoefficients(
+ kTestFilterCoefficients[0], kTestFilterCoefficients[1],
+ kTestFilterCoefficients[2], kTestFilterCoefficients[3],
+ kTestFilterCoefficients[4], kTestFilterCoefficients[5]);
+
+ AudioBuffer input(kNumMonoChannels, kTestInputDataSize);
+ // Set the input AudioBuffer to a Kronecker delta.
+ input.Clear();
+ input[0][0] = 1.0f;
+
+ // This vector will accumulate the output from the filter over time (Only
+ // works with mono channel).
+ std::vector<float> output_accumulator;
+ output_accumulator.reserve(kTestInputDataSize);
+
+ // Create a biquad filter initialized with transfer function coefficients.
+ BiquadFilter biquad(kCoefficients, kTestInputDataSize);
+
+ // Perform inplace filtering of |input| vector.
+ for (size_t i = 0; i < kNumIterations; ++i) {
+ biquad.Filter(input[0], &input[0]);
+ output_accumulator.insert(output_accumulator.end(), input[0].begin(),
+ input[0].end());
+ }
+
+ // Since the denominator of the biquad coefficients is [1 0 0] we can expect
+ // the impulse response to be equal to the numerator.
+ for (size_t i = 0; i < output_accumulator.size(); ++i) {
+ if (i < 3) {
+ EXPECT_NEAR(kTestFilterCoefficients[3 + i], output_accumulator[i],
+ kEpsilonFloat);
+ } else {
+ EXPECT_EQ(0.0f, output_accumulator[i]);
+ }
+ }
+}
+
+// Tests whether the interpolation stops after kIdealSamplesToIterate samples,
+// given a long enough buffer.
+TEST(BiquadFilterTest, InterpolationStopsTest) {
+ const size_t kFramesPerBuffer = 512;
+
+ std::vector<std::vector<float>> planar_input_data(
+ kNumMonoChannels, std::vector<float>(kFramesPerBuffer));
+ planar_input_data[0][0] = 1.0f;
+ planar_input_data[0][256] = 1.0f;
+
+ AudioBuffer input_planar(kNumMonoChannels, kFramesPerBuffer);
+ AudioBuffer output_planar(kNumMonoChannels, kFramesPerBuffer);
+ input_planar = planar_input_data;
+
+ // Instantiate Biquad for planar data. The impulse response of the default is:
+ // 1, 0, 0, ......
+ BiquadFilter biquad(BiquadCoefficients(), kFramesPerBuffer);
+
+ // Coefficients we wish to interpolate to. The impulse response of this filter
+ // is: 1, 0, 1, ......
+ const BiquadCoefficients kNextCoefficients = {1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 1.0f};
+
+ biquad.InterpolateToCoefficients(kNextCoefficients);
+ biquad.Filter(input_planar[0], &output_planar[0]);
+
+ // Based on the known impulse responses we can see that if elements 256, 257
+ // and 258 have the values 1, 0, 1, then only the new filter coefficients are
+ // contributing and we have stopped crossfading. Note: The value of 256 here
+ // comes from kIdealSamplesToIterate defined in biquad_filter.cc
+ EXPECT_EQ(1.0f, output_planar[0][kIdealSamplesToIterate]);
+ EXPECT_EQ(0.0f, output_planar[0][kIdealSamplesToIterate + 1]);
+ EXPECT_EQ(1.0f, output_planar[0][kIdealSamplesToIterate + 2]);
+}
+
+// Tests whether the |BiquadFilter| remains stable when its z-domain poles lie
+// very close to the unit circle.
+TEST(BiquadFilterTest, StabilityTest) {
+ static const size_t kBufferSize = 1024;
+ const size_t kIterations = 200;
+ // The following filter was designed in MATLAB with a very slow decay rate.
+ // (There are both poles and zeros with magnitude 0.999). Even 200'000 samples
+ // after the initial impulse the response will have only died away, in an
+ // oscillating manner, to ~1/700 of its peak value.
+ static const BiquadCoefficients kHighQCoefficients(
+ 1.0f, -0.907804951302441f, 0.999869108872718f, 15279.8745150008f, 0.0f,
+ -15279.8745150008f);
+ BiquadFilter biquad(kHighQCoefficients, kBufferSize);
+
+ AudioBuffer input(kNumMonoChannels, kBufferSize);
+ AudioBuffer output(kNumMonoChannels, kBufferSize);
+ // Set the input AudioBuffer to a Kronecker delta.
+ input.Clear();
+ input[0][0] = 1.0f;
+
+ // This vector will accumulate the output from the filter over time (Only
+ // works with mono channel).
+ std::vector<float> output_accumulator;
+ output_accumulator.reserve(kBufferSize * kIterations);
+
+ // Filter once with the Kronecker delta.
+ biquad.Filter(input[0], &output[0]);
+ output_accumulator.insert(output_accumulator.end(), output[0].begin(),
+ output[0].end());
+
+ // Perform filtering with all zero input.
+ input[0][0] = 0.0f;
+ for (size_t i = 1; i < kIterations; ++i) {
+ biquad.Filter(input[0], &output[0]);
+ output_accumulator.insert(output_accumulator.end(), output[0].begin(),
+ output[0].end());
+ }
+
+ // Test that the signal is decaying over time (i.e. filter is stable).
+ for (size_t i = 0; i < output_accumulator.size() - kBufferSize;
+ i += kBufferSize) {
+ std::vector<float> absolute_first_half(kBufferSize / 2);
+ std::transform(output_accumulator.begin() + i,
+ output_accumulator.begin() + i + kBufferSize / 2,
+ absolute_first_half.begin(), operator_abs);
+ std::vector<float> absolute_second_half(kBufferSize / 2);
+ std::transform(output_accumulator.begin() + 1 + i + kBufferSize / 2,
+ output_accumulator.begin() + i + kBufferSize,
+ absolute_second_half.begin(), operator_abs);
+ const float sum_first_half = std::accumulate(
+ absolute_first_half.begin(), absolute_first_half.end(), 0.0f);
+ const float sum_second_half = std::accumulate(
+ absolute_second_half.begin(), absolute_second_half.end(), 0.0f);
+ EXPECT_LT(sum_second_half, sum_first_half);
+ }
+}
+
+class BiquadFilterInterpolateTest : public ::testing::Test {
+ protected:
+ BiquadFilterInterpolateTest() {}
+ // Virtual methods from ::testing::Test
+ ~BiquadFilterInterpolateTest() override {}
+ void SetUp() override {}
+ void TearDown() override {}
+
+ // Returns true if |filter|'s internal coefficients are equal to those of
+ // |expected_coefficients| after scaling by a0.
+ bool TestInternalCoefficients(
+ BiquadFilter* filter, const BiquadCoefficients& expected_coefficients) {
+ bool return_value = true;
+ return_value &=
+ expected_coefficients.a[0] - filter->coefficients_.a[0] < kEpsilonFloat;
+
+ // From this point we must take account of the scaling by 1/a0.
+ const float a0 = filter->coefficients_.a[0];
+ return_value &=
+ expected_coefficients.b[0] - filter->coefficients_.b[0] * a0 <
+ kEpsilonFloat;
+ for (int i = 1; i < 3; ++i) {
+ return_value &=
+ expected_coefficients.b[i] - filter->coefficients_.b[i] * a0 <
+ kEpsilonFloat;
+ return_value &=
+ expected_coefficients.a[i] - filter->coefficients_.a[i] * a0 <
+ kEpsilonFloat;
+ }
+ return return_value;
+ }
+};
+
+// Tests whether updating the filter's coefficients with the
+// InterpolateToCoefficients reaches the correct coefficients after filtering a
+// block of data and whether the samples_to_iterate_over_ value is set
+// correctly.
+TEST_F(BiquadFilterInterpolateTest, InterpolatedUpdateTest) {
+ const size_t kFramesPerBuffer = 8;
+
+ const std::vector<std::vector<float>> kPlanarInputData = {
+ {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f}};
+
+ const std::vector<float> kExpectedOutputAfterChange = {
+ 8.0f, 10.0f, 4.0f, 6.0f, 8.0f, 10.0f, 12.0f, 14.0f};
+
+ // Default constructor sets a = {1.0, 0.0, 0.0} and b = {1.0, 0.0, 0.0}.
+ const BiquadCoefficients kCoefficients;
+
+ // Create input and output AudioBuffers.
+ AudioBuffer input_planar(kNumMonoChannels, kFramesPerBuffer);
+ AudioBuffer output_planar(kNumMonoChannels, kFramesPerBuffer);
+ input_planar = kPlanarInputData;
+
+ // Instantiate Biquad for planar data.
+ BiquadFilter biquad(kCoefficients, kFramesPerBuffer);
+
+ // Perform filtering on kNumMonoChannels interleaved.
+ biquad.Filter(input_planar[0], &output_planar[0]);
+
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ EXPECT_NEAR(output_planar[0][i], kPlanarInputData[0][i], kEpsilonFloat);
+ }
+
+ // Coefficients we wish to interpolate to.
+ static const BiquadCoefficients kNextCoefficients = {1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 1.0f};
+
+ biquad.InterpolateToCoefficients(kNextCoefficients);
+
+ // Filter once to transition and once to flush out the state.
+ biquad.Filter(input_planar[0], &output_planar[0]);
+ biquad.Filter(input_planar[0], &output_planar[0]);
+
+ // Now the output should be just from the new state.
+ biquad.Filter(input_planar[0], &output_planar[0]);
+
+ // Now check that we transitioned properly, i.e. the output is as expected.
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ EXPECT_NEAR(output_planar[0][i], kExpectedOutputAfterChange[i],
+ kEpsilonFloat);
+ }
+
+ // Now check that we have the new coefficients.
+ EXPECT_TRUE(TestInternalCoefficients(&biquad, kNextCoefficients));
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/channel_converter.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/channel_converter.cc
new file mode 100644
index 000000000..375001bac
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/channel_converter.cc
@@ -0,0 +1,45 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/channel_converter.h"
+
+#include <cmath>
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/simd_utils.h"
+
+namespace vraudio {
+
+void ConvertStereoFromMono(const AudioBuffer& input, AudioBuffer* output) {
+ DCHECK(output);
+ DCHECK_EQ(input.num_channels(), kNumMonoChannels);
+ DCHECK_EQ(output->num_channels(), kNumStereoChannels);
+ DCHECK_EQ(input.num_frames(), output->num_frames());
+ StereoFromMonoSimd(input.num_frames(), &input[0][0], &(*output)[0][0],
+ &(*output)[1][0]);
+}
+
+void ConvertMonoFromStereo(const AudioBuffer& input, AudioBuffer* output) {
+ DCHECK(output);
+ DCHECK_EQ(input.num_channels(), kNumStereoChannels);
+ DCHECK_EQ(output->num_channels(), kNumMonoChannels);
+ DCHECK_EQ(input.num_frames(), output->num_frames());
+ MonoFromStereoSimd(input.num_frames(), &input[0][0], &input[1][0],
+ &(*output)[0][0]);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/channel_converter.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/channel_converter.h
new file mode 100644
index 000000000..26d825ae4
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/channel_converter.h
@@ -0,0 +1,40 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_CHANNEL_CONVERTER_H_
+#define RESONANCE_AUDIO_DSP_CHANNEL_CONVERTER_H_
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Converts a mono |input| buffer to a stereo |output| buffer by preserving the
+// signal energy.
+//
+// @param input Mono input buffer.
+// @param output Pointer to a stereo output buffer.
+void ConvertStereoFromMono(const AudioBuffer& input, AudioBuffer* output);
+
+// Converts a stereo |input| buffer to a mono |output| buffer by preserving the
+// signal energy.
+//
+// @param input Stereo input buffer.
+// @param output Pointer to a mono output buffer.
+void ConvertMonoFromStereo(const AudioBuffer& input, AudioBuffer* output);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_CHANNEL_CONVERTER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/channel_converter_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/channel_converter_test.cc
new file mode 100644
index 000000000..3d6928ad5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/channel_converter_test.cc
@@ -0,0 +1,80 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/channel_converter.h"
+
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+// Number of frames for test buffers.
+const size_t kNumFrames = 5;
+
+// Tests that the correct stereo output buffer is obtained from the converter
+// given an arbitrary mono input buffer.
+TEST(ChannelConverterTest, ConvertStereoFromMonoTest) {
+ const std::vector<std::vector<float>> kMonoInput = {
+ {1.0f, 2.0f, 3.0f, 4.0f, 5.0f}};
+ const std::vector<std::vector<float>> kExpectedStereoOutput = {
+ {0.707107f, 1.414214f, 2.121320f, 2.828427f, 3.535534f},
+ {0.707107f, 1.414214f, 2.121320f, 2.828427f, 3.535534f}};
+
+ // Initialize the test buffers.
+ AudioBuffer mono_input(kNumMonoChannels, kNumFrames);
+ mono_input = kMonoInput;
+ AudioBuffer stereo_output(kNumStereoChannels, kNumFrames);
+ // Process the input buffer.
+ ConvertStereoFromMono(mono_input, &stereo_output);
+ // Compare the output buffer against the expected output.
+ for (size_t channel = 0; channel < kNumStereoChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumFrames; ++frame) {
+ EXPECT_NEAR(stereo_output[channel][frame],
+ kExpectedStereoOutput[channel][frame], kEpsilonFloat);
+ }
+ }
+}
+
+// Tests that the correct mono output buffer is obtained from the converter
+// given an arbitrary stereo input buffer.
+TEST(ChannelConverterTest, ConvertMonoFromStereoTest) {
+ const std::vector<std::vector<float>> kStereoInput = {
+ {0.707107f, 1.414214f, 2.121320f, 2.828427f, 3.535534f},
+ {0.707107f, 1.414214f, 2.121320f, 2.828427f, 3.535534f}};
+
+ const std::vector<std::vector<float>> kExpectedMonoOutput = {
+ {1.0f, 2.0f, 3.0f, 4.0f, 5.0f}};
+
+ // Initialize the test buffers.
+ AudioBuffer stereo_input(kNumStereoChannels, kNumFrames);
+ stereo_input = kStereoInput;
+ AudioBuffer mono_output(kNumMonoChannels, kNumFrames);
+ // Process the input buffer.
+ ConvertMonoFromStereo(stereo_input, &mono_output);
+ // Compare the output buffer against the expected output.
+ for (size_t frame = 0; frame < kNumFrames; ++frame) {
+ EXPECT_NEAR(mono_output[0][frame], kExpectedMonoOutput[0][frame],
+ kEpsilonFloat);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/circular_buffer.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/circular_buffer.cc
new file mode 100644
index 000000000..5ef2be2c9
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/circular_buffer.cc
@@ -0,0 +1,120 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/circular_buffer.h"
+
+#include <algorithm>
+
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+CircularBuffer::CircularBuffer(size_t buffer_length, size_t num_input_frames,
+ size_t num_output_frames)
+ : num_input_frames_(num_input_frames),
+ num_output_frames_(num_output_frames),
+ buffer_(kNumMonoChannels, buffer_length),
+ write_cursor_(0),
+ read_cursor_(0),
+ num_valid_frames_(0) {
+ CHECK_GE(buffer_length, num_input_frames + num_output_frames);
+}
+
+bool CircularBuffer::InsertBuffer(const AudioBuffer::Channel& input) {
+ DCHECK_EQ(input.size(), num_input_frames_);
+
+ if (num_valid_frames_ + num_input_frames_ > buffer_.num_frames()) {
+ return false;
+ }
+ // Remaining space after the write cursor.
+ const size_t forward_write_space = read_cursor_ <= write_cursor_
+ ? buffer_.num_frames() - write_cursor_
+ : read_cursor_ - write_cursor_;
+
+ // Copy the input into the buffer.
+ AudioBuffer::Channel* buffer_channel = &buffer_[0];
+ if (forward_write_space >= num_input_frames_) {
+ DCHECK_LE(buffer_channel->begin() + write_cursor_ + num_input_frames_,
+ buffer_channel->end());
+ std::copy(input.begin(), input.end(),
+ buffer_channel->begin() + write_cursor_);
+ } else {
+ DCHECK_LE(buffer_channel->begin() + write_cursor_ + forward_write_space,
+ buffer_channel->end());
+ DCHECK_LT(input.begin() + forward_write_space, input.end());
+ std::copy(input.begin(), input.begin() + forward_write_space,
+ buffer_channel->begin() + write_cursor_);
+ DCHECK_LE(buffer_channel->begin() + forward_write_space,
+ buffer_channel->end());
+ std::copy(input.begin() + forward_write_space, input.end(),
+ buffer_channel->begin());
+ }
+
+ write_cursor_ = (write_cursor_ + num_input_frames_) % buffer_.num_frames();
+ num_valid_frames_ += num_input_frames_;
+ return true;
+}
+
+bool CircularBuffer::RetrieveBuffer(AudioBuffer::Channel* output) {
+ return RetrieveBufferWithOffset(/*offset=*/0, output);
+}
+
+bool CircularBuffer::RetrieveBufferWithOffset(size_t offset,
+ AudioBuffer::Channel* output) {
+ DCHECK_LE(output->begin() + num_output_frames_ + offset, output->end());
+
+ if (num_valid_frames_ < num_output_frames_) {
+ return false;
+ }
+ // Remaining space after the read cursor.
+ const size_t forward_read_space = read_cursor_ < write_cursor_
+ ? write_cursor_ - read_cursor_
+ : buffer_.num_frames() - read_cursor_;
+
+ // Copy the buffer values into the output.
+ AudioBuffer::Channel* buffer_channel = &buffer_[0];
+ if (forward_read_space >= num_output_frames_) {
+ DCHECK_LE(buffer_channel->begin() + read_cursor_ + num_output_frames_,
+ buffer_channel->end());
+ std::copy(buffer_channel->begin() + read_cursor_,
+ buffer_channel->begin() + read_cursor_ + num_output_frames_,
+ output->begin() + offset);
+ } else {
+ DCHECK_LE(buffer_channel->begin() + read_cursor_ + forward_read_space,
+ buffer_channel->end());
+ DCHECK_LE(output->begin() + forward_read_space + offset, output->end());
+ std::copy(buffer_channel->begin() + read_cursor_,
+ buffer_channel->begin() + read_cursor_ + forward_read_space,
+ output->begin() + offset);
+ DCHECK_GE(buffer_channel->begin() + num_output_frames_ - forward_read_space,
+ buffer_channel->begin());
+ DCHECK_LE(output->begin() + offset + num_output_frames_, output->end());
+ std::copy(buffer_channel->begin(),
+ buffer_channel->begin() + num_output_frames_ - forward_read_space,
+ output->begin() + offset + forward_read_space);
+ }
+ read_cursor_ = (read_cursor_ + num_output_frames_) % buffer_.num_frames();
+ num_valid_frames_ -= num_output_frames_;
+ return true;
+}
+
+void CircularBuffer::Clear() {
+ read_cursor_ = 0;
+ write_cursor_ = 0;
+ num_valid_frames_ = 0;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/circular_buffer.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/circular_buffer.h
new file mode 100644
index 000000000..e21f5817d
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/circular_buffer.h
@@ -0,0 +1,96 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_CIRCULAR_BUFFER_H_
+#define RESONANCE_AUDIO_DSP_CIRCULAR_BUFFER_H_
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+// Class that implements a simple mono circular buffer, accepting input and
+// output of different length.
+class CircularBuffer {
+ public:
+ // Constructs a circular buffer.
+ //
+ // @param buffer_length The length of the Circular buffer. This value must be
+ // at least |num_input_frames| + |num_output_frames| so that we can always
+ // either add or remove data.
+ // @param num_input_frames Length of the input buffers in frames.
+ // @param num_output_frames Length of the output buffers in frames.
+ CircularBuffer(size_t buffer_length, size_t num_input_frames,
+ size_t num_output_frames);
+
+ // Inserts a buffer of mono input into the |CircularBuffer| if space permits.
+ //
+ // @param input A channel of input data |num_input_frames| in length.
+ // @return True if there was space in the buffer and the input was
+ // successfully inserted, false otherwise.
+ bool InsertBuffer(const AudioBuffer::Channel& input);
+
+ // Retrieves a buffer of output from the |CircularBuffer| if it contains
+ // sufficient data.
+ //
+ // @param output A channel to hold |num_output_frames| of output data. The
+ // channel may be greater in length than |num_output_frames|, in this
+ // case only the first |num_output_frames| will be overwritten.
+ // @return True if there was sufficient data in the buffer and the output was
+ // successfully retrieved.
+ bool RetrieveBuffer(AudioBuffer::Channel* output);
+
+ // Retrieves a buffer of output from the |CircularBuffer| to an offset
+ // location in an output channel, provided it contains sufficient data.
+ //
+ // @param offset Number of samples of offset into the |output| channel.
+ // @param output A channel to hold |num_output_frames| of output data. The
+ // channel may be greater in length than |num_output_frames| + |offset|,
+ // in this case only the first |num_output_frames| after |offset| will be
+ // overwritten.
+ // @return True if there was sufficient data in the buffer and the output was
+ // successfully retrieved.
+ bool RetrieveBufferWithOffset(size_t offset, AudioBuffer::Channel* output);
+
+ // Returns the number of samples of data currently in the |CircularBuffer|.
+ //
+ // @return The number of samples of data currently in the buffer.
+ size_t GetOccupancy() const { return num_valid_frames_; }
+
+ // Resets the |CircularBuffer|.
+ void Clear();
+
+ private:
+ // Number of input frames to be inserted into the buffer.
+ const size_t num_input_frames_;
+
+ // Number of output frames to be retrieved from the buffer.
+ const size_t num_output_frames_;
+
+ // Mono audio buffer to hold the data.
+ AudioBuffer buffer_;
+
+ // Position at which we are writing into the buffer.
+ size_t write_cursor_;
+
+ // position at which we are reading from the buffer.
+ size_t read_cursor_;
+
+ // Number of frames of data currently stored within the buffer.
+ size_t num_valid_frames_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_CIRCULAR_BUFFER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/circular_buffer_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/circular_buffer_test.cc
new file mode 100644
index 000000000..5b53246e6
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/circular_buffer_test.cc
@@ -0,0 +1,230 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/circular_buffer.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+// Tests that when the output from the circular buffer is of a smaller length
+// than the input, the |CircularBuffer|correctly accepts and rejects inserts
+// and retrievals.
+TEST(CircularBufferTest, RemainingReadSpaceTest) {
+ const size_t kInputSize = 5;
+ const size_t kOutputSize = 4;
+ const size_t kBufferSize = 9;
+ CircularBuffer circular_buffer(kBufferSize, kInputSize, kOutputSize);
+ AudioBuffer input_a(kNumMonoChannels, kInputSize);
+ input_a[0] = {0.0f, 1.0f, 2.0f, 3.0f, 4.0f};
+ AudioBuffer output(kNumMonoChannels, kOutputSize);
+ output.Clear();
+
+ EXPECT_TRUE(circular_buffer.InsertBuffer(input_a[0]));
+ // There should not be enough space to write another buffer.
+ EXPECT_FALSE(circular_buffer.InsertBuffer(input_a[0]));
+
+ EXPECT_TRUE(circular_buffer.RetrieveBuffer(&output[0]));
+ // There should not be enough data to read from the buffer.
+ EXPECT_FALSE(circular_buffer.RetrieveBuffer(&output[0]));
+
+ // The output buffer should contain the first |kOutputSize| entries from the
+ // input buffer.
+ for (size_t i = 0; i < kOutputSize; ++i) {
+ EXPECT_FLOAT_EQ(output[0][i], input_a[0][i]);
+ }
+
+ AudioBuffer input_b(kNumMonoChannels, kInputSize);
+ input_b[0] = {5.0f, 6.0f, 7.0f, 8.0f, 9.0f};
+
+ EXPECT_TRUE(circular_buffer.InsertBuffer(input_b[0]));
+ // There should not be enough space to write another buffer.
+ EXPECT_FALSE(circular_buffer.InsertBuffer(input_b[0]));
+
+ EXPECT_TRUE(circular_buffer.RetrieveBuffer(&output[0]));
+ // There should not be enough data to read from the buffer.
+ EXPECT_FALSE(circular_buffer.RetrieveBuffer(&output[0]));
+
+ // The output buffer should contain the final entry from the first input
+ // buffer and then |kOutputSize| - 1 entries from the second.
+ EXPECT_FLOAT_EQ(output[0][0], input_a[0][kInputSize - 1]);
+ for (size_t i = 1; i < kOutputSize; ++i) {
+ EXPECT_FLOAT_EQ(output[0][i], input_b[0][i - 1]);
+ }
+}
+
+// Tests that when the output from the circular buffer is of a greater length
+// than the input, the |CircularBuffer|correctly accepts and rejects inserts
+// and retrievals.
+TEST(CircularBufferTest, RemainingWriteSpaceTest) {
+ const size_t kInputSize = 4;
+ const size_t kOutputSize = 5;
+ const size_t kBufferSize = 10;
+ CircularBuffer circular_buffer(kBufferSize, kInputSize, kOutputSize);
+ AudioBuffer input_a(kNumMonoChannels, kInputSize);
+ input_a[0] = {0.0f, 1.0f, 2.0f, 3.0f};
+ AudioBuffer input_b(kNumMonoChannels, kInputSize);
+ input_b[0] = {4.0f, 5.0f, 6.0f, 7.0f};
+ AudioBuffer input_c(kNumMonoChannels, kInputSize);
+ input_c[0] = {8.0f, 9.0f, 10.0f, 11.0f};
+ AudioBuffer input_d(kNumMonoChannels, kInputSize);
+ input_d[0] = {12.0f, 13.0f, 14.0f, 15.0f};
+ AudioBuffer input_e(kNumMonoChannels, kInputSize);
+ input_e[0] = {16.0f, 17.0f, 18.0f, 19.0f};
+ AudioBuffer output(kNumMonoChannels, kOutputSize);
+ output.Clear();
+
+ EXPECT_TRUE(circular_buffer.InsertBuffer(input_a[0]));
+ EXPECT_TRUE(circular_buffer.InsertBuffer(input_b[0]));
+ // There should not be enough space to write another buffer.
+ EXPECT_FALSE(circular_buffer.InsertBuffer(input_c[0]));
+
+ EXPECT_TRUE(circular_buffer.RetrieveBuffer(&output[0]));
+ // There should not be enough data to read from the buffer.
+ EXPECT_FALSE(circular_buffer.RetrieveBuffer(&output[0]));
+
+ float value = 0.0f;
+ for (size_t i = 0; i < kOutputSize; ++i) {
+ EXPECT_FLOAT_EQ(output[0][i], value);
+ value += 1.0f;
+ }
+
+ // Add another 4 samples of input in.
+ EXPECT_TRUE(circular_buffer.InsertBuffer(input_c[0]));
+ // There should not be enough space to write another buffer.
+ EXPECT_FALSE(circular_buffer.InsertBuffer(input_d[0]));
+
+ EXPECT_TRUE(circular_buffer.RetrieveBuffer(&output[0]));
+ // There should not be enough data to read from the buffer.
+ EXPECT_FALSE(circular_buffer.RetrieveBuffer(&output[0]));
+
+ for (size_t i = 0; i < kOutputSize; ++i) {
+ EXPECT_FLOAT_EQ(output[0][i], value);
+ value += 1.0f;
+ }
+
+ // Add another 8 samples of input in.
+ EXPECT_TRUE(circular_buffer.InsertBuffer(input_d[0]));
+ EXPECT_TRUE(circular_buffer.InsertBuffer(input_e[0]));
+ // There should not be enough space to write another buffer.
+ EXPECT_FALSE(circular_buffer.InsertBuffer(input_a[0]));
+
+ // We should be able to get 10 samples of output.
+ EXPECT_TRUE(circular_buffer.RetrieveBuffer(&output[0]));
+ for (size_t i = 0; i < kOutputSize; ++i) {
+ EXPECT_FLOAT_EQ(output[0][i], value);
+ value += 1.0f;
+ }
+ EXPECT_TRUE(circular_buffer.RetrieveBuffer(&output[0]));
+ for (size_t i = 0; i < kOutputSize; ++i) {
+ EXPECT_FLOAT_EQ(output[0][i], value);
+ value += 1.0f;
+ }
+
+ // There should not be enough data to read from the buffer.
+ EXPECT_FALSE(circular_buffer.RetrieveBuffer(&output[0]));
+
+ // The buffer should now be completely empty.
+ EXPECT_EQ(circular_buffer.GetOccupancy(), 0U);
+}
+
+// Tests tha a call to RetrieveBuffer will work when the output buffer is
+// oversized, but that only the first kOutputSize samples are filled.
+TEST(CircularBufferTest, LongerWriteSpaceTest) {
+ const size_t kInputSize = 5;
+ const size_t kOutputSize = 3;
+ const size_t kBufferSize = 10;
+ CircularBuffer circular_buffer(kBufferSize, kInputSize, kOutputSize);
+ AudioBuffer input_a(kNumMonoChannels, kInputSize);
+ input_a[0] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f};
+ AudioBuffer output(kNumMonoChannels, kBufferSize);
+ output.Clear();
+
+ EXPECT_TRUE(circular_buffer.InsertBuffer(input_a[0]));
+ EXPECT_TRUE(circular_buffer.RetrieveBuffer(&output[0]));
+
+ for (size_t i = 0; i < kBufferSize; ++i) {
+ if (i < kOutputSize) {
+ EXPECT_FLOAT_EQ(output[0][i], input_a[0][i]);
+ } else {
+ EXPECT_FLOAT_EQ(output[0][i], 0.0f);
+ }
+ }
+}
+
+// Tests tha a call to RetrieveBufferOffset will work when the output buffer is
+// oversized, but that the first kOutputSize samples after kOffset are filled.
+TEST(CircularBufferTest, OffsetRerieveTest) {
+ const size_t kInputSize = 5;
+ const size_t kOutputSize = 3;
+ const size_t kBufferSize = 10;
+ const size_t kOffset = 2;
+ CircularBuffer circular_buffer(kBufferSize, kInputSize, kOutputSize);
+ AudioBuffer input_a(kNumMonoChannels, kInputSize);
+ input_a[0] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f};
+ AudioBuffer output(kNumMonoChannels, kBufferSize);
+ output.Clear();
+
+ EXPECT_TRUE(circular_buffer.InsertBuffer(input_a[0]));
+ EXPECT_TRUE(circular_buffer.RetrieveBufferWithOffset(kOffset, &output[0]));
+
+ for (size_t i = 0; i < kBufferSize; ++i) {
+ if (i < kOffset) {
+ EXPECT_FLOAT_EQ(output[0][i], 0.0f);
+ } else if (i < kOffset + kOutputSize) {
+ EXPECT_FLOAT_EQ(output[0][i], input_a[0][i - kOffset]);
+ } else {
+ EXPECT_FLOAT_EQ(output[0][i], 0.0f);
+ }
+ }
+}
+
+// Tests that the circular buffer handles odd input buffer lengths in the
+// manner that it will be passed in the spectral reverb
+// (see: dsp/spectral_reverb.cc)
+TEST(CircularBufferTest, Strange) {
+ const size_t kNumRuns = 100;
+ // An odd non pwer of two input buffer size.
+ const size_t kOddInputSize = 713;
+ // Output size is equal to the SpectralReverb's internal buffer size.
+ const size_t kOutputSize = 1024;
+ // SpectralReverb's internal forier transform length.
+ const size_t kFFTSize = 4096;
+ CircularBuffer input(kFFTSize + kOddInputSize, kOddInputSize, kOutputSize);
+ CircularBuffer output(kOutputSize + kOddInputSize, kOutputSize,
+ kOddInputSize);
+ AudioBuffer in(kNumMonoChannels, kOddInputSize);
+ AudioBuffer out(kNumMonoChannels, kOutputSize);
+
+ // AudioBuffers will be input and output in the same manner as in
+ // dsp/spectral_reverb.cc.
+ EXPECT_TRUE(output.InsertBuffer(out[0]));
+ for (size_t i = 0; i < kNumRuns; ++i) {
+ EXPECT_TRUE(input.InsertBuffer(in[0]));
+ while (input.GetOccupancy() >= kOutputSize) {
+ EXPECT_TRUE(input.RetrieveBuffer(&out[0]));
+ EXPECT_TRUE(output.InsertBuffer(out[0]));
+ }
+ EXPECT_TRUE(output.RetrieveBuffer(&in[0]));
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/delay_filter.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/delay_filter.cc
new file mode 100644
index 000000000..b448de58b
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/delay_filter.cc
@@ -0,0 +1,180 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/delay_filter.h"
+
+#include <cmath>
+
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+
+
+namespace vraudio {
+// The following explains the behaviour of the delay line when a change to the
+// delay length is made. Initially |delay| is 2, |frames_per_buffer_|
+// is 4, and |delay_line_| is 8 in length.
+//
+// delay = 2
+//
+// input0 [1, 2, 3, 4] output0 [0, 0, 1, 2]
+//
+// 0 1 2 3 4 5 6 7
+// delay line [1, 2, 3, 4, 0, 0, 0, 0]
+// | |
+// w r
+// ----------------------------------------------------------
+//
+// delay = 2
+//
+// input1 [5, 6, 7, 8] output1 [3, 4, 5, 6]
+//
+// 0 1 2 3 4 5 6 7
+// delay line [1, 2, 3, 4, 5, 6, 7, 8]
+// | |
+// w r
+// ----------------------------------------------------------
+//
+// delay = 1
+//
+// 0 1 2 3 4 5 6 7
+// delay line [1, 2, 3, 4, 5, 6, 7, 8]
+// | |
+// w r
+// ----------------------------------------------------------
+//
+// delay = 1
+//
+// input2 [9, 10, 11, 12] output2 [8, 9, 10, 11]
+//
+// 0 1 2 3 4 5 6 7
+// delay line [9, 10, 11, 12, 5, 6, 7, 8]
+// | |
+// w r
+// ----------------------------------------------------------
+
+DelayFilter::DelayFilter(size_t max_delay_length, size_t frames_per_buffer)
+ : frames_per_buffer_(frames_per_buffer),
+ delay_line_(nullptr),
+ write_cursor_(0) {
+ DCHECK_GT(frames_per_buffer_, 0U);
+ SetMaximumDelay(max_delay_length);
+}
+
+void DelayFilter::SetMaximumDelay(size_t max_delay_length) {
+ max_delay_length_ = max_delay_length;
+ const size_t new_buffer_size = frames_per_buffer_ + max_delay_length_;
+
+ if (delay_line_ == nullptr) {
+ delay_line_.reset(new AudioBuffer(kNumMonoChannels, new_buffer_size));
+ delay_line_->Clear();
+ return;
+ }
+ AudioBuffer::Channel* delay_channel = &(*delay_line_)[0];
+ // If |delay_line_| is not large enough, resize.
+ const size_t current_buffer_size = delay_line_->num_frames();
+ if (max_delay_length_ > current_buffer_size - frames_per_buffer_) {
+ // Allocate |new_delay_line| and populate it with the current |delay_line_|
+ // data, so that replacing the buffer does not affect the already stored
+ // samples.
+ auto new_delay_line = std::unique_ptr<AudioBuffer>(
+ new AudioBuffer(kNumMonoChannels, new_buffer_size));
+ new_delay_line->Clear();
+ std::copy(delay_channel->begin() + write_cursor_, delay_channel->end(),
+ (*new_delay_line)[0].begin());
+ if (write_cursor_ > 0) {
+ // Positive |write_cursor_| means that we still have remaining samples to
+ // be moved to |new_delay_line_| all of which reside left of the cursor.
+ std::copy(
+ delay_channel->begin(), delay_channel->begin() + write_cursor_,
+ (*new_delay_line)[0].begin() + current_buffer_size - write_cursor_);
+ write_cursor_ = current_buffer_size;
+ }
+ delay_line_ = std::move(new_delay_line);
+ }
+}
+
+void DelayFilter::InsertData(const AudioBuffer::Channel& input) {
+
+ DCHECK_EQ(input.size(), frames_per_buffer_);
+
+ const size_t delay_buffer_size = delay_line_->num_frames();
+
+ // Record the remaining space in the |delay_line_| after the write cursor.
+ const size_t remaining_size_write = delay_buffer_size - write_cursor_;
+ AudioBuffer::Channel* delay_channel = &(*delay_line_)[0];
+
+ // Copy the input into the delay line.
+ if (remaining_size_write >= frames_per_buffer_) {
+ DCHECK_LE(delay_channel->begin() + write_cursor_ + frames_per_buffer_,
+ delay_channel->end());
+ std::copy(input.begin(), input.end(),
+ delay_channel->begin() + write_cursor_);
+ } else {
+ DCHECK_LE(delay_channel->begin() + write_cursor_ + remaining_size_write,
+ delay_channel->end());
+ DCHECK_LE(input.begin() + remaining_size_write, input.end());
+ std::copy(input.begin(), input.begin() + remaining_size_write,
+ delay_channel->begin() + write_cursor_);
+ DCHECK_LE(delay_channel->begin() + remaining_size_write,
+ delay_channel->end());
+ std::copy(input.begin() + remaining_size_write, input.end(),
+ delay_channel->begin());
+ }
+
+ write_cursor_ = (write_cursor_ + frames_per_buffer_) % delay_buffer_size;
+}
+
+void DelayFilter::GetDelayedData(size_t delay_samples,
+ AudioBuffer::Channel* buffer) {
+
+ DCHECK(buffer);
+ DCHECK_GE(delay_samples, 0U);
+ DCHECK_LE(delay_samples, max_delay_length_);
+
+ const size_t delay_buffer_size = delay_line_->num_frames();
+ // Position in the delay line to begin reading from.
+ DCHECK_GE(write_cursor_ + delay_buffer_size,
+ delay_samples + frames_per_buffer_);
+ const size_t read_cursor =
+ (write_cursor_ + delay_buffer_size - delay_samples - frames_per_buffer_) %
+ delay_buffer_size;
+ // Record the remaining space in the |delay_line_| after the read cursor.
+ const size_t remaining_size_read = delay_buffer_size - read_cursor;
+ AudioBuffer::Channel* delay_channel = &(*delay_line_)[0];
+
+ // Extract a portion of the delay line into the buffer.
+ if (remaining_size_read >= frames_per_buffer_) {
+ DCHECK_LE(buffer->begin() + frames_per_buffer_, buffer->end());
+ DCHECK_LE(delay_channel->begin() + read_cursor + frames_per_buffer_,
+ delay_channel->end());
+ std::copy(delay_channel->begin() + read_cursor,
+ delay_channel->begin() + read_cursor + frames_per_buffer_,
+ buffer->begin());
+ } else {
+ DCHECK_LE(buffer->begin() + delay_channel->size() - read_cursor,
+ buffer->end());
+ std::copy(delay_channel->begin() + read_cursor, delay_channel->end(),
+ buffer->begin());
+ DCHECK_LE(buffer->begin() + frames_per_buffer_, buffer->end());
+ DCHECK_LE(delay_channel->begin() + frames_per_buffer_ - remaining_size_read,
+ delay_channel->end());
+ std::copy(delay_channel->begin(),
+ delay_channel->begin() + frames_per_buffer_ - remaining_size_read,
+ buffer->begin() + remaining_size_read);
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/delay_filter.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/delay_filter.h
new file mode 100644
index 000000000..be7c49774
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/delay_filter.h
@@ -0,0 +1,85 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_DELAY_FILTER_H_
+#define RESONANCE_AUDIO_DSP_DELAY_FILTER_H_
+
+#include <algorithm>
+#include <memory>
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Single channel delay line class. Delays input buffer data by a non-negative
+// integer number of samples.
+//
+// This implementation is not thread safe. The ClearBuffer() and InsertData()
+// functions should not be called by a seperate thread during GetDelayedData().
+class DelayFilter {
+ public:
+ // Constructs a DelayFilter.
+ //
+ // @param max_delay_length Maximum number of samples the input should be
+ // delayed by.
+ // @param frames_per_buffer Number of frames in each processed buffer.
+ DelayFilter(size_t max_delay_length, size_t frames_per_buffer);
+
+ // Sets the maximum delay length. It will allocate more space in the
+ // |delay_line_| if the new |max_delay_length| is more than doubled.
+ //
+ // @param max_delay_length New maximum delay in samples.
+ void SetMaximumDelay(size_t max_delay_length);
+
+ // Returns the current length of the |delay_line_|.
+ size_t GetDelayBufferLength() const { return delay_line_->num_frames(); }
+
+ // Returns the current maximum delay applicable to input buffers.
+ size_t GetMaximumDelayLength() const { return max_delay_length_; }
+
+ // Sets all of the |delay_line_| samples to zero.
+ void ClearBuffer() { delay_line_->Clear(); }
+
+ // Copies an |AudioBuffer::Channel| of data to the delay line.
+ //
+ // @param input Input data.
+ void InsertData(const AudioBuffer::Channel& input);
+
+ // Fills an |AudioBuffer::Channel| with data delayed by a specified amount
+ // less than or equal to the delay line's set |max_delay_length_|.
+ //
+ // @param delay_samples Requested delay to the data extraced from the delay
+ // line. Must be less than or equal to |max_delay_length_|.
+ // @param buffer Pointer to the output data, i.e., delayed input data.
+ void GetDelayedData(size_t delay_samples, AudioBuffer::Channel* buffer);
+
+ private:
+ // Maximum length of the delay to be applied (in samples).
+ size_t max_delay_length_;
+
+ // Number of frames in each AudioBuffer input/output.
+ size_t frames_per_buffer_;
+
+ // The delay line holding all of the delayed samples.
+ std::unique_ptr<AudioBuffer> delay_line_;
+
+ // Position in the delay line to begin writing to.
+ size_t write_cursor_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_DELAY_FILTER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/delay_filter_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/delay_filter_test.cc
new file mode 100644
index 000000000..12a7a8f60
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/delay_filter_test.cc
@@ -0,0 +1,197 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/delay_filter.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+// Set of delay lengths to be used in the tests below.
+const size_t kInitDelayLength = 4;
+const size_t kSecondDelayLength = 5;
+const size_t kThirdDelayLength = 2;
+const size_t kFourthDelayLength = 11;
+const size_t kZeroDelayLength = 0;
+
+// Frames for buffer for the input and output data buffers below.
+const size_t kFramesPerBuffer = 6;
+
+// Frames per buffer and channel number for the input and output data.
+const size_t kFramesPerBuffer2 = 10;
+
+// Function which passes an AudioBuffer through the delay line followed by all
+// zero AudioBuffers to flush the data out. This function then tests that the
+// input data has been delayed by the correct amount.
+//
+// @param delay A pointer to a DelayFilter which will be used in the test.
+// @param delay_length An integer delay length to be used in the test.
+void IntegerDelayTestHelper(DelayFilter* delay, size_t delay_length) {
+ std::vector<float> output_collect;
+ delay->SetMaximumDelay(delay_length);
+ delay->ClearBuffer();
+
+ // Initialize mono input buffer and fill with test data.
+ const std::vector<float> kData = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f};
+ AudioBuffer input(kNumMonoChannels, kFramesPerBuffer);
+ auto* input_channel = &input[0];
+ *input_channel = kData;
+
+ // Process input.
+ delay->InsertData(*input_channel);
+ delay->GetDelayedData(delay_length, input_channel);
+ output_collect.insert(output_collect.end(), input_channel->begin(),
+ input_channel->end());
+
+ // Keep passing zeros through till we flush all of the input data out.
+ while (delay_length + kFramesPerBuffer > output_collect.size()) {
+ // Set the |input| AudioBuffer to have all zero data.
+ input.Clear();
+ // Process input.
+ delay->InsertData(*input_channel);
+ delay->GetDelayedData(delay_length, input_channel);
+ output_collect.insert(output_collect.end(), input_channel->begin(),
+ input_channel->end());
+ }
+
+ // Check that the first GetDelayedData() call yields |delay_length| zeros at
+ // the beginning of its output.
+ for (size_t i = 0; i < delay_length; ++i) {
+ EXPECT_EQ(output_collect[i], 0.0f);
+ }
+ // Check that the output is the same data as the input but delayed by
+ // delay_length samples.
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ EXPECT_EQ(output_collect[i + delay_length], kData[i]);
+ }
+}
+
+// Tests that the maximum_delay_length_ and delay_buffer_size_ are set to the
+// correct values on construction and when SetDelay() is called.
+TEST(DelayFilterTest, DelayCorrectTest) {
+ DelayFilter delay(kInitDelayLength, kFramesPerBuffer);
+
+ EXPECT_EQ(delay.GetMaximumDelayLength(), kInitDelayLength);
+ EXPECT_EQ(delay.GetDelayBufferLength(), kInitDelayLength + kFramesPerBuffer);
+
+ delay.SetMaximumDelay(kSecondDelayLength);
+
+ EXPECT_EQ(delay.GetMaximumDelayLength(), kSecondDelayLength);
+ EXPECT_EQ(delay.GetDelayBufferLength(),
+ kSecondDelayLength + kFramesPerBuffer);
+
+ // In this next case we reduce the maximum length and thus do not expect a
+ // reallocation.
+ delay.SetMaximumDelay(kThirdDelayLength);
+
+ EXPECT_EQ(delay.GetMaximumDelayLength(), kThirdDelayLength);
+ EXPECT_EQ(delay.GetDelayBufferLength(),
+ kSecondDelayLength + kFramesPerBuffer);
+
+ delay.SetMaximumDelay(kFourthDelayLength);
+
+ EXPECT_EQ(delay.GetMaximumDelayLength(), kFourthDelayLength);
+ // Now we expect the buffer to have been reallocated.
+ EXPECT_EQ(delay.GetDelayBufferLength(),
+ kFourthDelayLength + kFramesPerBuffer);
+}
+
+// Tests whether when setting a different delay on one DelayFilter,
+// the output is correct in each case.
+TEST(DelayFilterTest, DelayTest) {
+ DelayFilter delay(kInitDelayLength, kFramesPerBuffer);
+
+ IntegerDelayTestHelper(&delay, kInitDelayLength);
+
+ // Tests the case of an increasing delay.
+ IntegerDelayTestHelper(&delay, kSecondDelayLength);
+
+ // Tests the case of a decreasing delay.
+ IntegerDelayTestHelper(&delay, kThirdDelayLength);
+
+ // Tests the case of an increasing delay with allocation of more buffer space.
+ IntegerDelayTestHelper(&delay, kFourthDelayLength);
+}
+
+// Tests that differently delayed buffers can be extracted from a single delay
+// line.
+TEST(DelayFilterTest, MultipleDelaysTest) {
+ DelayFilter delay(kFramesPerBuffer2, kFramesPerBuffer2);
+ AudioBuffer input(kNumMonoChannels, kFramesPerBuffer2);
+ for (size_t i = 0; i < kFramesPerBuffer2; ++i) {
+ input[0][i] = static_cast<float>(i + 1);
+ }
+ delay.InsertData(input[0]);
+ const size_t kDelayOne = 1;
+ const size_t kDelayTwo = 2;
+ AudioBuffer buffer_1(kNumMonoChannels, kFramesPerBuffer2);
+ AudioBuffer buffer_2(kNumMonoChannels, kFramesPerBuffer2);
+
+ delay.GetDelayedData(kDelayOne, &buffer_1[0]);
+ for (size_t i = 0; i < kFramesPerBuffer2 - kDelayOne; ++i) {
+ EXPECT_NEAR(buffer_1[0][i + kDelayOne], input[0][i], kEpsilonFloat);
+ }
+
+ delay.GetDelayedData(kDelayTwo, &buffer_2[0]);
+ for (size_t i = 0; i < kFramesPerBuffer2 - kDelayTwo; ++i) {
+ EXPECT_NEAR(buffer_2[0][i + kDelayTwo], input[0][i], kEpsilonFloat);
+ }
+}
+
+// Tests whether a zero delay length is dealt with correctly, Along with a
+// negative delay value (treated as zero delay.
+TEST(DelayFilterTest, ZeroDelayTest) {
+ DelayFilter delay(kZeroDelayLength, kFramesPerBuffer);
+ IntegerDelayTestHelper(&delay, kZeroDelayLength);
+}
+
+// Tests that output from a delay line that is initally large enough vs one that
+// is resized is the same.
+TEST(DelayFilterTest, InitialSizeVsResizeTest) {
+ const size_t kSmallMaxDelay = 2;
+ const size_t kLargeMaxDelay = 5;
+ const size_t kActualDelay = 4;
+
+ DelayFilter delay_sufficient(kLargeMaxDelay, kFramesPerBuffer);
+ DelayFilter delay_insufficient(kSmallMaxDelay, kFramesPerBuffer);
+
+ AudioBuffer input_buffer(kNumMonoChannels, kFramesPerBuffer);
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ input_buffer[0][i] = static_cast<float>(i + 1);
+ }
+
+ delay_sufficient.InsertData(input_buffer[0]);
+ delay_insufficient.InsertData(input_buffer[0]);
+ delay_insufficient.SetMaximumDelay(kLargeMaxDelay);
+
+ AudioBuffer buffer_sufficient(kNumMonoChannels, kFramesPerBuffer);
+ AudioBuffer buffer_insufficient(kNumMonoChannels, kFramesPerBuffer);
+
+ delay_sufficient.GetDelayedData(kActualDelay, &buffer_sufficient[0]);
+ delay_insufficient.GetDelayedData(kActualDelay, &buffer_insufficient[0]);
+
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ EXPECT_NEAR(buffer_sufficient[0][i], buffer_insufficient[0][i],
+ kEpsilonFloat);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/distance_attenuation.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/distance_attenuation.cc
new file mode 100644
index 000000000..816053a7c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/distance_attenuation.cc
@@ -0,0 +1,121 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/distance_attenuation.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+float ComputeLogarithmicDistanceAttenuation(
+ const WorldPosition& listener_position,
+ const WorldPosition& source_position, float min_distance,
+ float max_distance) {
+ const float distance = (listener_position - source_position).norm();
+ if (distance > max_distance) {
+ return 0.0f;
+ }
+ // Logarithmic attenuation.
+ const float min_distance_allowed =
+ std::max(min_distance, kNearFieldThreshold);
+ if (distance > min_distance_allowed) {
+ const float attenuation_interval = max_distance - min_distance_allowed;
+ if (attenuation_interval > kEpsilonFloat) {
+ // Compute the distance attenuation value by the logarithmic curve
+ // "1 / (d + 1)" with an offset of |min_distance_allowed|.
+ const float relative_distance = distance - min_distance_allowed;
+ const float attenuation = 1.0f / (relative_distance + 1.0f);
+ // Shift the curve downwards by the attenuation value at |max_distance|,
+ // and scale the value by the inverse of it in order to keep the curve's
+ // peak value 1 at |min_distance_allowed|.
+ const float attenuation_max = 1.0f / (1.0f + attenuation_interval);
+ return (attenuation - attenuation_max) / (1.0f - attenuation_max);
+ }
+ }
+ return 1.0f;
+}
+
+float ComputeLinearDistanceAttenuation(const WorldPosition& listener_position,
+ const WorldPosition& source_position,
+ float min_distance, float max_distance) {
+ const float distance = (listener_position - source_position).norm();
+ if (distance > max_distance) {
+ return 0.0f;
+ }
+ // Linear attenuation.
+ const float min_distance_allowed =
+ std::max(min_distance, kNearFieldThreshold);
+ if (distance > min_distance_allowed) {
+ const float attenuation_interval = max_distance - min_distance_allowed;
+ if (attenuation_interval > kEpsilonFloat) {
+ return (max_distance - distance) / attenuation_interval;
+ }
+ }
+ return 1.0f;
+}
+
+float ComputeNearFieldEffectGain(const WorldPosition& listener_position,
+ const WorldPosition& source_position) {
+ const float distance = (listener_position - source_position).norm();
+ if (distance < kNearFieldThreshold) {
+ return (1.0f / std::max(distance, kMinNearFieldDistance)) - 1.0f;
+ }
+ return 0.0f;
+}
+
+void UpdateAttenuationParameters(float master_gain, float reflections_gain,
+ float reverb_gain,
+ const WorldPosition& listener_position,
+ SourceParameters* parameters) {
+ // Compute distance attenuation.
+ const WorldPosition& source_position = parameters->object_transform.position;
+ const auto rolloff_model = parameters->distance_rolloff_model;
+ const float min_distance = parameters->minimum_distance;
+ const float max_distance = parameters->maximum_distance;
+
+ float distance_attenuation = 0.0f;
+ switch (rolloff_model) {
+ case DistanceRolloffModel::kLogarithmic:
+ distance_attenuation = ComputeLogarithmicDistanceAttenuation(
+ listener_position, source_position, min_distance, max_distance);
+ break;
+ case DistanceRolloffModel::kLinear:
+ distance_attenuation = ComputeLinearDistanceAttenuation(
+ listener_position, source_position, min_distance, max_distance);
+ break;
+ case DistanceRolloffModel::kNone:
+ default:
+ // Distance attenuation is already set by the user.
+ distance_attenuation = parameters->distance_attenuation;
+ break;
+ }
+ // Update gain attenuations.
+ const float input_gain = master_gain * parameters->gain;
+ const float direct_attenuation = input_gain * distance_attenuation;
+ const float room_effects_attenuation = parameters->room_effects_gain;
+
+ parameters->attenuations[AttenuationType::kInput] = input_gain;
+ parameters->attenuations[AttenuationType::kDirect] = direct_attenuation;
+ parameters->attenuations[AttenuationType::kReflections] =
+ room_effects_attenuation * direct_attenuation * reflections_gain;
+ parameters->attenuations[AttenuationType::kReverb] =
+ room_effects_attenuation * input_gain * reverb_gain;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/distance_attenuation.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/distance_attenuation.h
new file mode 100644
index 000000000..2b98ac63c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/distance_attenuation.h
@@ -0,0 +1,82 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_DISTANCE_ATTENUATION_H_
+#define RESONANCE_AUDIO_DSP_DISTANCE_ATTENUATION_H_
+
+#include "base/misc_math.h"
+#include "base/source_parameters.h"
+
+namespace vraudio {
+
+// Returns the distance attenuation for |source_position| with respect to
+// |listener_position|. The amplitude will decrease by approximately 6 dB every
+// time the distance is doubled, i.e., the sound pressure "p" (amplitude)
+// approximately falls inversely proportional to the distance "1/r".
+//
+// @param listener_position World position of the listener.
+// @param source_position World position of the source.
+// @param min_distance The minimum distance at which distance attenuation is
+// applied.
+// @param max_distance The maximum distance at which the direct sound has a gain
+// of 0.0.
+// @return Attenuation (gain) value in range [0.0f, 1.0f].
+float ComputeLogarithmicDistanceAttenuation(
+ const WorldPosition& listener_position,
+ const WorldPosition& source_position, float min_distance,
+ float max_distance);
+
+// Returns the distance attenuation for |source_position| with respect to
+// |listener_position|. The amplitude will decrease linearly between
+// |min_distance| and |max_distance| from 1.0 to 0.0.
+//
+// @param listener_position World position of the listener.
+// @param source_position World position of the source.
+// @param min_distance The minimum distance at which distance attenuation is
+// applied.
+// @param max_distance The maximum distance at which the direct sound has a gain
+// of 0.0.
+// @return Attenuation (gain) value in range [0.0f, 1.0f].
+float ComputeLinearDistanceAttenuation(const WorldPosition& listener_position,
+ const WorldPosition& source_position,
+ float min_distance, float max_distance);
+
+// Calculates the gain to be applied to the near field compensating stereo mix.
+// This function will return 0.0f for all sources further away than one meter
+// and will return a value between 0.0 and 9.0 for sources as they approach
+// the listener's head location.
+//
+// @param listener_position World position of the listener.
+// @param source_position World position of the source.
+// @return Gain value in range [0.0f, 9.0f].
+float ComputeNearFieldEffectGain(const WorldPosition& listener_position,
+ const WorldPosition& source_position);
+
+// Calculates and updates gain attenuations of the given source |parameters|.
+//
+// @param master_gain Global gain adjustment in amplitude.
+// @param reflections_gain Reflections gain in amplitude.
+// @param reverb_gain Reverb gain in amplitude.
+// @param listener_position World position of the listener.
+// @param parameters Source parameters to apply the gain attenuations into.
+void UpdateAttenuationParameters(float master_gain, float reflections_gain,
+ float reverb_gain,
+ const WorldPosition& listener_position,
+ SourceParameters* parameters);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_DISTANCE_ATTENUATION_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/distance_attenuation_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/distance_attenuation_test.cc
new file mode 100644
index 000000000..6bccfcabf
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/distance_attenuation_test.cc
@@ -0,0 +1,132 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/distance_attenuation.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+namespace {
+
+// Tests the logarithmic distance attenuation method against the pre-computed
+// results.
+TEST(DistanceAttenuationTest, ComputeLogarithmicDistanceAttenuationTest) {
+ const WorldPosition kListenerPosition(0.0f, 3.0f, 0.0f);
+ const WorldPosition kSourcePosition(0.0f, 0.0f, -4.0f);
+ const float kMinDistance_low = 1.0f;
+ const float kMinDistance_high = 10.0f;
+ const float kMaxDistance_low = 3.0f;
+ const float kMaxDistance_high = 500.0f;
+ const float kExpectedAttenuation_a = 0.1983967f;
+ const float kExpectedAttenuation_b = 0.0f;
+ const float kExpectedAttenuation_c = 1.0f;
+
+ float attenuation = ComputeLogarithmicDistanceAttenuation(
+ kListenerPosition, kSourcePosition, kMinDistance_low, kMaxDistance_high);
+ EXPECT_NEAR(kExpectedAttenuation_a, attenuation, kEpsilonFloat);
+
+ // Test for the case where the source is beyond the maximum distance.
+ attenuation = ComputeLogarithmicDistanceAttenuation(
+ kListenerPosition, kSourcePosition, kMinDistance_low, kMaxDistance_low);
+ EXPECT_NEAR(kExpectedAttenuation_b, attenuation, kEpsilonFloat);
+
+ // Test for the case where the source is within the minimum distance.
+ attenuation = ComputeLogarithmicDistanceAttenuation(
+ kListenerPosition, kSourcePosition, kMinDistance_high, kMaxDistance_high);
+ EXPECT_NEAR(kExpectedAttenuation_c, attenuation, kEpsilonFloat);
+}
+
+// Tests the linear distance attenuation method against the pre-computed
+// results.
+TEST(DistanceAttenuationTest, ComputeLinearDistanceAttenuationTest) {
+ const WorldPosition kListenerPosition(0.0f, 3.0f, 0.0f);
+ const WorldPosition kSourcePosition(0.0f, 0.0f, -4.0f);
+ const float kMinDistance_low = 1.0f;
+ const float kMinDistance_high = 10.0f;
+ const float kMaxDistance_low = 3.0f;
+ const float kMaxDistance_high = 8.0f;
+ const float kExpectedAttenuation_a = 0.4285714f;
+ const float kExpectedAttenuation_b = 0.0f;
+ const float kExpectedAttenuation_c = 1.0f;
+
+ float attenuation = ComputeLinearDistanceAttenuation(
+ kListenerPosition, kSourcePosition, kMinDistance_low, kMaxDistance_high);
+ EXPECT_NEAR(kExpectedAttenuation_a, attenuation, kEpsilonFloat);
+
+ // Test for the case where the source is beyond the maximum distance.
+ attenuation = ComputeLinearDistanceAttenuation(
+ kListenerPosition, kSourcePosition, kMinDistance_low, kMaxDistance_low);
+ EXPECT_NEAR(kExpectedAttenuation_b, attenuation, kEpsilonFloat);
+
+ // Test for the case where the source is within the minimum distance.
+ attenuation = ComputeLinearDistanceAttenuation(
+ kListenerPosition, kSourcePosition, kMinDistance_high, kMaxDistance_high);
+ EXPECT_NEAR(kExpectedAttenuation_c, attenuation, kEpsilonFloat);
+}
+
+// Tests the gain attenuations update method against the pre-computed results.
+TEST(DistanceAttenuationTest, UpdateAttenuationParametersTest) {
+ const float kMasterGain = 0.5f;
+ const float kReflectionsGain = 0.5f;
+ const float kReverbGain = 2.0f;
+ const WorldPosition kListenerPosition(0.0f, 0.0f, 0.0f);
+ const WorldPosition kSourcePosition(0.0f, 0.0f, 1.5f);
+ const float kDistanceAttenuation = 0.2f;
+ const float kRoomEffectsGain = 0.25f;
+
+ // Initialize new |SourceParameters| with an explicit distance attenuation
+ // value to avoid extra complexity.
+ SourceParameters parameters;
+ parameters.distance_rolloff_model = DistanceRolloffModel::kNone;
+ parameters.distance_attenuation = kDistanceAttenuation;
+ parameters.object_transform.position = kSourcePosition;
+ parameters.room_effects_gain = kRoomEffectsGain;
+ // Update the gain attenuation parameters.
+ UpdateAttenuationParameters(kMasterGain, kReflectionsGain, kReverbGain,
+ kListenerPosition, &parameters);
+ // Check the attenuation parameters against the pre-computed values.
+ const size_t num_attenuations =
+ static_cast<size_t>(AttenuationType::kNumAttenuationTypes);
+ const float kExpectedAttenuations[num_attenuations] = {0.5f, 0.1f, 0.0125f,
+ 0.25f};
+ for (size_t i = 0; i < num_attenuations; ++i) {
+ EXPECT_NEAR(kExpectedAttenuations[i], parameters.attenuations[i],
+ kEpsilonFloat)
+ << "Attenuation " << i;
+ }
+}
+
+// Tests the near field effects gain computation method against the pre-computed
+// results.
+TEST(NearFieldEffectTest, ComputeNearFieldEffectTest) {
+ const WorldPosition kListenerPosition(0.0f, 3.0f, 0.0f);
+ const WorldPosition kSourcePosition_a(0.0f, 0.0f, -4.0f);
+ const WorldPosition kSourcePosition_b(0.0f, 2.5f, 0.0f);
+ const float kExpectedGain_a = 0.0f;
+ const float kExpectedGain_b = 1.0f;
+
+ float gain = ComputeNearFieldEffectGain(kListenerPosition, kSourcePosition_a);
+ EXPECT_NEAR(kExpectedGain_a, gain, kEpsilonFloat);
+ gain = ComputeNearFieldEffectGain(kListenerPosition, kSourcePosition_b);
+ EXPECT_NEAR(kExpectedGain_b, gain, kEpsilonFloat);
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/fft_manager.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/fft_manager.cc
new file mode 100644
index 000000000..5be470b17
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/fft_manager.cc
@@ -0,0 +1,209 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/fft_manager.h"
+
+#include <algorithm>
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/misc_math.h"
+#include "base/simd_utils.h"
+
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+namespace vraudio {
+
+namespace {
+
+// for |fft_size_|s less than 2^14, the stack is used, on the reccomendation of
+// the author of the pffft library.
+const size_t kPffftMaxStackSize = 16384;
+
+} // namespace
+
+// The pffft implementation requires a minimum fft size of 32 samples.
+const size_t FftManager::kMinFftSize = 32;
+
+FftManager::FftManager(size_t frames_per_buffer)
+ : fft_size_(std::max(NextPowTwo(frames_per_buffer) * 2, kMinFftSize)),
+ frames_per_buffer_(frames_per_buffer),
+ inverse_fft_scale_(1.0f / static_cast<float>(fft_size_)),
+ temp_zeropad_buffer_(kNumMonoChannels, fft_size_),
+ temp_freq_buffer_(kNumMonoChannels, fft_size_) {
+ DCHECK_GT(frames_per_buffer, 0U);
+ DCHECK_GE(fft_size_, kMinFftSize);
+ DCHECK(!(fft_size_ & (fft_size_ - 1))); // Ensure its a power of two.
+ // Suggested pffft initialization.
+ if (fft_size_ > kPffftMaxStackSize) {
+ // Allocate memory for work space factors etc, Size reccomended by pffft.
+ const size_t num_bytes = 2 * fft_size_ * sizeof(float);
+ pffft_workspace_ =
+ reinterpret_cast<float*>(pffft_aligned_malloc(num_bytes));
+ }
+
+ fft_ = pffft_new_setup(static_cast<int>(fft_size_), PFFFT_REAL);
+
+ temp_zeropad_buffer_.Clear();
+}
+
+FftManager::~FftManager() {
+ pffft_destroy_setup(fft_);
+ if (pffft_workspace_ != nullptr) {
+ pffft_aligned_free(pffft_workspace_);
+ }
+}
+
+void FftManager::FreqFromTimeDomain(const AudioBuffer::Channel& time_channel,
+ AudioBuffer::Channel* freq_channel) {
+
+
+ DCHECK(freq_channel);
+ DCHECK_EQ(freq_channel->size(), fft_size_);
+ DCHECK_LE(time_channel.size(), fft_size_);
+
+ // Perform forward FFT transform.
+ if (time_channel.size() == fft_size_) {
+ pffft_transform(fft_, time_channel.begin(), freq_channel->begin(),
+ pffft_workspace_, PFFFT_FORWARD);
+ } else {
+ std::copy_n(time_channel.begin(), frames_per_buffer_,
+ temp_zeropad_buffer_[0].begin());
+ pffft_transform(fft_, temp_zeropad_buffer_[0].begin(),
+ freq_channel->begin(), pffft_workspace_, PFFFT_FORWARD);
+ }
+}
+
+void FftManager::TimeFromFreqDomain(const AudioBuffer::Channel& freq_channel,
+ AudioBuffer::Channel* time_channel) {
+
+
+ DCHECK(time_channel);
+ DCHECK_EQ(freq_channel.size(), fft_size_);
+
+ // Perform reverse FFT transform.
+ const size_t time_channel_size = time_channel->size();
+ if (time_channel_size == fft_size_) {
+ pffft_transform(fft_, freq_channel.begin(), time_channel->begin(),
+ pffft_workspace_, PFFFT_BACKWARD);
+ } else {
+ DCHECK_EQ(time_channel_size, frames_per_buffer_);
+ auto& temp_channel = temp_freq_buffer_[0];
+ pffft_transform(fft_, freq_channel.begin(), temp_channel.begin(),
+ pffft_workspace_, PFFFT_BACKWARD);
+ std::copy_n(temp_channel.begin(), frames_per_buffer_,
+ time_channel->begin());
+ }
+}
+
+void FftManager::ApplyReverseFftScaling(AudioBuffer::Channel* time_channel) {
+ DCHECK(time_channel->size() == frames_per_buffer_ ||
+ time_channel->size() == fft_size_);
+ // Normalization must be performed here as we normally do this as part of the
+ // convolution.
+ ScalarMultiply(time_channel->size(), inverse_fft_scale_,
+ time_channel->begin(), time_channel->begin());
+}
+
+void FftManager::GetCanonicalFormatFreqBuffer(const AudioBuffer::Channel& input,
+ AudioBuffer::Channel* output) {
+ DCHECK_EQ(input.size(), fft_size_);
+ DCHECK_EQ(output->size(), fft_size_);
+ pffft_zreorder(fft_, input.begin(), output->begin(), PFFFT_FORWARD);
+}
+
+void FftManager::GetPffftFormatFreqBuffer(const AudioBuffer::Channel& input,
+ AudioBuffer::Channel* output) {
+ DCHECK_EQ(input.size(), fft_size_);
+ DCHECK_EQ(output->size(), fft_size_);
+ pffft_zreorder(fft_, input.begin(), output->begin(), PFFFT_BACKWARD);
+}
+
+void FftManager::MagnitudeFromCanonicalFreqBuffer(
+ const AudioBuffer::Channel& freq_channel,
+ AudioBuffer::Channel* magnitude_channel) {
+ DCHECK(magnitude_channel);
+ DCHECK_EQ(freq_channel.size(), fft_size_);
+ DCHECK_EQ(magnitude_channel->size(), frames_per_buffer_ + 1);
+
+ (*magnitude_channel)[0] = std::abs(freq_channel[0]);
+ ApproxComplexMagnitude(frames_per_buffer_ - 1, freq_channel.begin() + 2,
+ magnitude_channel->begin() + 1);
+ (*magnitude_channel)[frames_per_buffer_] = std::abs(freq_channel[1]);
+}
+
+void FftManager::CanonicalFreqBufferFromMagnitudeAndPhase(
+ const AudioBuffer::Channel& magnitude_channel,
+ const AudioBuffer::Channel& phase_channel,
+ AudioBuffer::Channel* canonical_freq_channel) {
+ DCHECK(canonical_freq_channel);
+ DCHECK_EQ(magnitude_channel.size(), frames_per_buffer_ + 1);
+ DCHECK_EQ(phase_channel.size(), frames_per_buffer_ + 1);
+ DCHECK_EQ(canonical_freq_channel->size(), fft_size_);
+
+ (*canonical_freq_channel)[0] = magnitude_channel[0];
+ (*canonical_freq_channel)[1] = -magnitude_channel[frames_per_buffer_];
+ for (size_t i = 1, j = 2; i < frames_per_buffer_; ++i, j += 2) {
+ (*canonical_freq_channel)[j] =
+ magnitude_channel[i] * std::cos(phase_channel[i]);
+ (*canonical_freq_channel)[j + 1] =
+ magnitude_channel[i] * std::sin(phase_channel[i]);
+ }
+}
+
+void FftManager::CanonicalFreqBufferFromMagnitudeAndSinCosPhase(
+ size_t phase_offset, const AudioBuffer::Channel& magnitude_channel,
+ const AudioBuffer::Channel& sin_phase_channel,
+ const AudioBuffer::Channel& cos_phase_channel,
+ AudioBuffer::Channel* canonical_freq_channel) {
+ static const size_t kSimdLength = 4;
+ DCHECK(canonical_freq_channel);
+ DCHECK_EQ(magnitude_channel.size(), frames_per_buffer_ + 1);
+ DCHECK_GE(sin_phase_channel.size() + phase_offset, frames_per_buffer_ + 1);
+ DCHECK_GE(cos_phase_channel.size() + phase_offset, frames_per_buffer_ + 1);
+ DCHECK_EQ(canonical_freq_channel->size(), fft_size_);
+
+ (*canonical_freq_channel)[0] = magnitude_channel[0];
+ (*canonical_freq_channel)[1] = -magnitude_channel[frames_per_buffer_];
+ // Continue on till we can gaurantee alignment in our audio buffer.
+ for (size_t i = 1, j = 2; i <= kSimdLength; ++i, j += 2) {
+ (*canonical_freq_channel)[j] =
+ magnitude_channel[i] * cos_phase_channel[i + phase_offset];
+ (*canonical_freq_channel)[j + 1] =
+ magnitude_channel[i] * sin_phase_channel[i + phase_offset];
+ }
+ ComplexInterleavedFormatFromMagnitudeAndSinCosPhase(
+ 2 * (frames_per_buffer_ - kSimdLength), &magnitude_channel[kSimdLength],
+ &cos_phase_channel[kSimdLength + phase_offset],
+ &sin_phase_channel[kSimdLength + phase_offset],
+ &(*canonical_freq_channel)[2 * kSimdLength]);
+}
+
+void FftManager::FreqDomainConvolution(const AudioBuffer::Channel& input_a,
+ const AudioBuffer::Channel& input_b,
+ AudioBuffer::Channel* scaled_output) {
+ DCHECK_EQ(input_a.size(), fft_size_);
+ DCHECK_EQ(input_b.size(), fft_size_);
+ DCHECK_EQ(scaled_output->size(), fft_size_);
+ pffft_zconvolve_accumulate(fft_, input_a.begin(), input_b.begin(),
+ scaled_output->begin(), inverse_fft_scale_);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/fft_manager.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/fft_manager.h
new file mode 100644
index 000000000..d7869a3d7
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/fft_manager.h
@@ -0,0 +1,182 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_FFT_MANAGER_H_
+#define RESONANCE_AUDIO_DSP_FFT_MANAGER_H_
+
+#include "pffft.h"
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// This class wraps the pffft library and enables reall FFT transformations to
+// be performed on aligned float buffers of data. The class also manages all
+// necessary data buffers and zero padding. This class is not thread safe.
+class FftManager {
+ public:
+ // Minimum required FFT size.
+ static const size_t kMinFftSize;
+
+ // Constructs a FftManager insatnce. One instance of this class can be shared.
+ // This class is not thread safe.
+ //
+ // @param frames_per_buffer System's number of frames per buffer.
+ explicit FftManager(size_t frames_per_buffer);
+
+ // Destroys a FftManager instance freeing associated aligned memory.
+ ~FftManager();
+
+ // Transforms a single channel of time domain input data into a frequency
+ // domain representation.
+ //
+ // @param time_channel Time domain input. If the length is less than
+ // |fft_size_|, the input is zeropadded. The max length is |fft_size_|.
+ // @param freq_channel Frequency domain output, |fft_size| samples long.
+ void FreqFromTimeDomain(const AudioBuffer::Channel& time_channel,
+ AudioBuffer::Channel* freq_channel);
+
+ // Transforms a single channel of frequency domain input data into a time
+ // domain representation. Note: The input must be in pffft format see:
+ // goo.gl/LYbgX7. This method can output to a buffer of either
+ // |frames_per_buffer_| or |fft_size_| in length. This feature ensures an
+ // additional copy is not needed where this method is to be used with an
+ // overlap add.
+ //
+ // @param freq_channel Frequency domain input, |fft_size| samples long.
+ // @param time_channel Time domain output, |frames_per_buffer_| samples long
+ // OR |fft_size_| samples long.
+ void TimeFromFreqDomain(const AudioBuffer::Channel& freq_channel,
+ AudioBuffer::Channel* time_channel);
+
+ // Applies a 1/|fft_size_| scaling to time domain output. NOTE this need not
+ // be applied where a convolution is taking place as the scaling will be
+ // included therein.
+ //
+ // @param time_channel Time domain data to be scaled.
+ void ApplyReverseFftScaling(AudioBuffer::Channel* time_channel);
+
+ // Transforms a pffft frequency domain format buffer into canonical format
+ // with alternating real and imaginary values with increasing frequency. The
+ // first two entries of |output| are the real part of the DC and Nyquist
+ // frequencies (imaginary part is zero). The alternating real and imaginary
+ // parts start from the third entry in |output|. For more info on the pffft
+ // format see: goo.gl/LYbgX7
+ //
+ // @param input Frequency domain input channel, |fft_size| samples long.
+ // @param output Frequency domain output channel,|fft_size| samples long.
+ void GetCanonicalFormatFreqBuffer(const AudioBuffer::Channel& input,
+ AudioBuffer::Channel* output);
+
+ // Transforms a canonical frequency domain format buffer into pffft format.
+ // For more info on the pffft format see: goo.gl/LYbgX7
+ //
+ // @param input Frequency domain input channel, |fft_size| samples long.
+ // @param output Frequency domain output channel, |fft_size| samples long.
+ void GetPffftFormatFreqBuffer(const AudioBuffer::Channel& input,
+ AudioBuffer::Channel* output);
+
+ // Genarates a buffer containing the single sided magnitude spectrum of a
+ // frequency domain buffer. The input must be in Canonical format. The output
+ // will have DC frequency as it's first entry and the Nyquist as it's last.
+ //
+ // @param freq_channel Canonical format frequency domain buffer,
+ // |fft_size_| samples long.
+ // @param magnitude_channel Magnitude of the |freq_channel|.
+ // |frames_per_buffer_| + 1 samples long.
+ void MagnitudeFromCanonicalFreqBuffer(
+ const AudioBuffer::Channel& freq_channel,
+ AudioBuffer::Channel* magnitude_channel);
+
+ // Combines single sided magnitude and phase spectra into a canonical format
+ // frequency domain buffer. The inputs must have DC frequency as their first
+ // entry and the Nyquist as their last.
+ //
+ // @param magnitude_channel Magnitude of the |frequency_buffer|.
+ // |frames_per_buffer_| + 1 samples long.
+ // @param phase_channel Phase of the |frequency_buffer|.
+ // |frames_per_buffer_| + 1 samples long.
+ // @param canonical_freq_channel Canonical format frequency domain buffer,
+ // |fft_size_| samples long.
+ void CanonicalFreqBufferFromMagnitudeAndPhase(
+ const AudioBuffer::Channel& magnitude_channel,
+ const AudioBuffer::Channel& phase_channel,
+ AudioBuffer::Channel* canonical_freq_channel);
+
+ // Combines single sided magnitude spectrum and the cosine and sine of a phase
+ // spectrum into a canonical format frequency domain buffer. The inputs must
+ // have DC frequency as their first entry and the Nyquist as their last.
+ // The phase spectra channels can be offset by |phase_offset|. This feature
+ // is specifically for use as an optimization in the |SpectralReverb|.
+ //
+ // @param phase_offset An offset into the channels of the phase buffer.
+ // @param magnitude_channel Magnitude of the |frequency_buffer|.
+ // |frames_per_buffer_| + 1 samples long.
+ // @param sin_phase_channel Sine of the phase of the |frequency_buffer|.
+ // |frames_per_buffer_| + 1 samples long.
+ // @param cos_phase_channel Cosine of the phase of the |frequency_buffer|.
+ // |frames_per_buffer_| + 1 samples long.
+ // @param canonical_freq_channel Canonical format frequency domain buffer,
+ // |fft_size_| samples long.
+ void CanonicalFreqBufferFromMagnitudeAndSinCosPhase(
+ size_t phase_offset, const AudioBuffer::Channel& magnitude_channel,
+ const AudioBuffer::Channel& sin_phase_channel,
+ const AudioBuffer::Channel& cos_phase_channel,
+ AudioBuffer::Channel* canonical_freq_channel);
+
+ // Performs a pointwise complex multiplication of two frequency domain buffers
+ // and applies tha inverse scaling factor of 1/|fft_size_|. This operation is
+ // equivalent to a time domain circular convolution.
+ //
+ // @param input_a Frequency domain input channel, |fft_size| samples long.
+ // @param input_b Frequency domain input channel, |fft_size| samples long.
+ // @param scaled_output Frequency domain output channel, |fft_size| samples
+ // long.
+ void FreqDomainConvolution(const AudioBuffer::Channel& input_a,
+ const AudioBuffer::Channel& input_b,
+ AudioBuffer::Channel* scaled_output);
+
+ // Returns the number of points in the FFT.
+ size_t GetFftSize() const { return fft_size_; }
+
+ private:
+ // FFT size in samples.
+ const size_t fft_size_;
+
+ // Number of frames in each buffer of input data.
+ const size_t frames_per_buffer_;
+
+ // Inverse scale to be applied to buffers transformed from frequency to time
+ // domain.
+ const float inverse_fft_scale_;
+
+ // Temporary time domain buffer to store zeropadded input.
+ AudioBuffer temp_zeropad_buffer_;
+
+ // Temporary freq domain buffer to store.
+ AudioBuffer temp_freq_buffer_;
+
+ // pffft states.
+ PFFFT_Setup* fft_;
+
+ // Workspace for pffft. This pointer should be set to null for |fft_size_|
+ // less than 2^14. In which case the stack is used. This is the recommendation
+ // by the author of the pffft library.
+ float* pffft_workspace_ = nullptr;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_FFT_MANAGER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/fft_manager_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/fft_manager_test.cc
new file mode 100644
index 000000000..e96ed231c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/fft_manager_test.cc
@@ -0,0 +1,260 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/fft_manager.h"
+
+#include <cstdlib>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+namespace {
+
+const float kInverseFftEpsilon = 2e-5f;
+
+// This tests that the |FreqFromTimeDomain| and |TimeFromFreqDomain|
+// functions are the inverse of one another for a number of fft sizes and signal
+// types.
+TEST(FftManagerTest, FftIfftTest) {
+ // Generate a test signal.
+ const size_t kNumSignalTypes = 3;
+ const size_t kNumBufferLengths = 10;
+ const size_t kBufferLengths[kNumBufferLengths] = {31, 32, 63, 64, 127,
+ 128, 255, 256, 511, 512};
+
+ for (size_t length_idx = 0; length_idx < kNumBufferLengths; length_idx++) {
+ for (size_t type = 0; type < kNumSignalTypes; ++type) {
+ AudioBuffer time_signal(kNumMonoChannels, kBufferLengths[length_idx]);
+ for (size_t i = 0; i < kBufferLengths[length_idx]; ++i) {
+ switch (type) {
+ case 0:
+ time_signal[0][i] = static_cast<float>(i) /
+ static_cast<float>(kBufferLengths[length_idx]);
+ break;
+ case 1:
+ time_signal[0][i] = std::cos(static_cast<float>(i));
+ break;
+ case 2:
+ time_signal[0][i] = static_cast<float>(i % 2) * -0.5f;
+ break;
+ }
+ }
+ AudioBuffer freq_signal(kNumMonoChannels,
+ NextPowTwo(kBufferLengths[length_idx]) * 2);
+ AudioBuffer output(kNumMonoChannels, kBufferLengths[length_idx]);
+ output.Clear();
+
+ FftManager fft_manager(kBufferLengths[length_idx]);
+
+ fft_manager.FreqFromTimeDomain(time_signal[0], &freq_signal[0]);
+ fft_manager.TimeFromFreqDomain(freq_signal[0], &output[0]);
+ fft_manager.ApplyReverseFftScaling(&output[0]);
+
+ for (size_t i = 0; i < kBufferLengths[length_idx]; ++i) {
+ EXPECT_NEAR(output[0][i], time_signal[0][i], kEpsilonFloat);
+ }
+ }
+ }
+}
+
+// Tests that the result from an inverse FFT is the same whether it is written
+// into a buffer of |frames_per_buffer_| or |fft_size_| in length.
+TEST(FftManagerTest, ReverseFftOutputSizeTest) {
+ const size_t kFramesPerBuffer = 32;
+ AudioBuffer freq_buffer(kNumMonoChannels, 2 * kFramesPerBuffer);
+ freq_buffer.Clear();
+ AudioBuffer time_buffer_short(kNumMonoChannels, kFramesPerBuffer);
+ time_buffer_short.Clear();
+ AudioBuffer time_buffer_long(kNumMonoChannels, 2 * kFramesPerBuffer);
+ time_buffer_long.Clear();
+ AudioBuffer input_buffer(kNumMonoChannels, kFramesPerBuffer);
+
+ std::srand(0);
+ for (auto& sample : input_buffer[0]) {
+ sample = static_cast<float>(std::rand()) / static_cast<float>(RAND_MAX);
+ }
+
+ FftManager fft_manager(kFramesPerBuffer);
+ fft_manager.FreqFromTimeDomain(input_buffer[0], &freq_buffer[0]);
+ fft_manager.TimeFromFreqDomain(freq_buffer[0], &time_buffer_short[0]);
+ fft_manager.ApplyReverseFftScaling(&time_buffer_short[0]);
+ fft_manager.TimeFromFreqDomain(freq_buffer[0], &time_buffer_long[0]);
+ fft_manager.ApplyReverseFftScaling(&time_buffer_long[0]);
+
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ EXPECT_NEAR(time_buffer_short[0][i], time_buffer_long[0][i], kEpsilonFloat);
+ EXPECT_NEAR(time_buffer_long[0][i + kFramesPerBuffer], 0.0f,
+ kInverseFftEpsilon);
+ }
+}
+
+// Tests that a frequency domain buffer can be transformed into a canonical
+// format and back.
+TEST(FftManagerTest, PffftFormatToCanonicalFormatTest) {
+ const size_t kFramesPerBuffer = 32;
+ AudioBuffer time_buffer(kNumMonoChannels, kFramesPerBuffer);
+ time_buffer.Clear();
+ time_buffer[0][0] = 1.0f;
+ time_buffer[0][1] = 1.0f;
+ AudioBuffer freq_buffer(kNumMonoChannels, 2 * kFramesPerBuffer);
+ freq_buffer.Clear();
+ AudioBuffer reordered_buffer(kNumMonoChannels, 2 * kFramesPerBuffer);
+ reordered_buffer.Clear();
+ AudioBuffer final_buffer(kNumMonoChannels, 2 * kFramesPerBuffer);
+ reordered_buffer.Clear();
+
+ FftManager fft_manager(kFramesPerBuffer);
+ fft_manager.FreqFromTimeDomain(time_buffer[0], &freq_buffer[0]);
+
+ fft_manager.GetCanonicalFormatFreqBuffer(freq_buffer[0],
+ &reordered_buffer[0]);
+ fft_manager.GetPffftFormatFreqBuffer(reordered_buffer[0], &final_buffer[0]);
+
+ for (size_t i = 0; i < kFramesPerBuffer * 2; ++i) {
+ EXPECT_NEAR(final_buffer[0][i], freq_buffer[0][i], kEpsilonFloat);
+ }
+}
+
+// Tests that for a scaled kronecker delta, the magnitude response will be flat
+// and equal to the absolute magnitude of the kronecker.
+TEST(FftManagerTest, MagnitudeTest) {
+ const size_t kFramesPerBuffer = 32;
+ const size_t kMagnitudeLength = kFramesPerBuffer + 1;
+ FftManager fft_manager(kFramesPerBuffer);
+ AudioBuffer time_buffer(kNumMonoChannels, kFramesPerBuffer);
+ time_buffer.Clear();
+ AudioBuffer freq_buffer(kNumMonoChannels, 2 * kFramesPerBuffer);
+ AudioBuffer reordered_buffer(kNumMonoChannels, 2 * kFramesPerBuffer);
+ AudioBuffer magnitude_buffer(kNumMonoChannels, kMagnitudeLength);
+ const std::vector<float> magnitudes = {1.0f, 2.0f, 3.0f, -1.0f, -2.0f, -3.0f};
+
+ for (auto& magnitude : magnitudes) {
+ time_buffer[0][0] = magnitude;
+ fft_manager.FreqFromTimeDomain(time_buffer[0], &freq_buffer[0]);
+ fft_manager.GetCanonicalFormatFreqBuffer(freq_buffer[0],
+ &reordered_buffer[0]);
+ fft_manager.MagnitudeFromCanonicalFreqBuffer(reordered_buffer[0],
+ &magnitude_buffer[0]);
+ for (size_t sample = 0; sample < kMagnitudeLength; ++sample) {
+ // Check its correct to within 0.5%.
+ const float kErrEpsilon = 5e-3f;
+ const float expected = std::abs(magnitude);
+ EXPECT_NEAR(magnitude_buffer[0][sample], expected,
+ kErrEpsilon * expected);
+ }
+ }
+}
+
+// Tests that conversion from Canonical frequency domain data to phase and
+// magnitude spectra and back results in an output equal to the input.
+TEST(FftManagerTest, FreqFromMagnitudePhase) {
+ const size_t kFramesPerBuffer = 16;
+ const size_t kMagnitudePhaseLength = kFramesPerBuffer + 1;
+ FftManager fft_manager(kFramesPerBuffer);
+ AudioBuffer time_buffer(kNumMonoChannels, kFramesPerBuffer);
+ time_buffer.Clear();
+ time_buffer[0][0] = 0.5f;
+ time_buffer[0][1] = 1.0f;
+ AudioBuffer freq_buffer(kNumMonoChannels, 2 * kFramesPerBuffer);
+ AudioBuffer reordered_buffer(kNumMonoChannels, 2 * kFramesPerBuffer);
+ AudioBuffer phase_buffer(kNumMonoChannels, kMagnitudePhaseLength);
+ AudioBuffer magnitude_buffer(kNumMonoChannels, kMagnitudePhaseLength);
+ fft_manager.FreqFromTimeDomain(time_buffer[0], &freq_buffer[0]);
+ fft_manager.GetCanonicalFormatFreqBuffer(freq_buffer[0],
+ &reordered_buffer[0]);
+ fft_manager.MagnitudeFromCanonicalFreqBuffer(reordered_buffer[0],
+ &magnitude_buffer[0]);
+
+ // Calculate the phase.
+ phase_buffer[0][0] = 0.0f;
+ for (size_t i = 1, j = 2; i < kFramesPerBuffer; ++i, j += 2) {
+ phase_buffer[0][i] = std::atan2(reordered_buffer[0][j + 1] /*imag*/,
+ reordered_buffer[0][j] /*real*/);
+ }
+ phase_buffer[0][kFramesPerBuffer] = kPi;
+
+ fft_manager.CanonicalFreqBufferFromMagnitudeAndPhase(
+ magnitude_buffer[0], phase_buffer[0], &freq_buffer[0]);
+
+ for (size_t sample = 0; sample < kFramesPerBuffer * 2; ++sample) {
+ // Check its correct to within 0.5%.
+ const float kErrEpsilon = 5e-3f;
+ EXPECT_NEAR(freq_buffer[0][sample], reordered_buffer[0][sample],
+ kErrEpsilon * std::abs(reordered_buffer[0][sample]));
+ }
+}
+
+// Tests that conversion from phase and magnitude spectra and back results in
+// an output equal to that from sine and cosine phase, using SIMD on arm.
+TEST(FftManagerTest, FMagnitudePhaseAndSineCosinePhase) {
+ const size_t kFramesPerBuffer = 16;
+ const size_t kMagnitudePhaseLength = kFramesPerBuffer + 1;
+ FftManager fft_manager(kFramesPerBuffer);
+ AudioBuffer freq_buffer_one(kNumMonoChannels, 2 * kFramesPerBuffer);
+ AudioBuffer freq_buffer_two(kNumMonoChannels, 2 * kFramesPerBuffer);
+ AudioBuffer phase_buffer(kNumMonoChannels, kMagnitudePhaseLength);
+ AudioBuffer sin_phase_buffer(kNumMonoChannels, kMagnitudePhaseLength);
+ AudioBuffer cos_phase_buffer(kNumMonoChannels, kMagnitudePhaseLength);
+ AudioBuffer magnitude_buffer(kNumMonoChannels, kMagnitudePhaseLength);
+
+ std::fill(magnitude_buffer[0].begin(), magnitude_buffer[0].end(), 2.0f);
+ phase_buffer[0] = std::vector<float>(
+ {0.4720f, 1.6100f, -1.9831f, 0.7569f, 0.2799f, -1.1481f, -0.3807f,
+ 0.3008f, 3.1416f, 2.4314f, -1.1851f, 2.6645f, 0.6369f, -0.0554f, 0.6275f,
+ -0.1799f, 1.2345f});
+ for (size_t i = 0; i < phase_buffer.num_frames(); ++i) {
+ sin_phase_buffer[0][i] = std::sin(phase_buffer[0][i]);
+ cos_phase_buffer[0][i] = std::cos(phase_buffer[0][i]);
+ }
+
+ fft_manager.CanonicalFreqBufferFromMagnitudeAndPhase(
+ magnitude_buffer[0], phase_buffer[0], &freq_buffer_one[0]);
+ fft_manager.CanonicalFreqBufferFromMagnitudeAndSinCosPhase(
+ 0, /* phase_offset */
+ magnitude_buffer[0], sin_phase_buffer[0], cos_phase_buffer[0],
+ &freq_buffer_two[0]);
+
+ for (size_t i = 0; i < kFramesPerBuffer * 2; ++i) {
+ EXPECT_NEAR(freq_buffer_one[0][i], freq_buffer_two[0][i], kEpsilonFloat);
+ }
+}
+
+// Tests that the correct scaling factor is applied consistently across a time
+// domain buffer.
+TEST(FftManagerTest, ReverseScalingTest) {
+ const size_t kFramesPerBuffer = 128;
+ const float kExpectedScale = 1.0f / static_cast<float>(2 * kFramesPerBuffer);
+
+ AudioBuffer buffer(kNumMonoChannels, kFramesPerBuffer);
+ for (auto& sample : buffer[0]) {
+ sample = 1.0f;
+ }
+ FftManager fft_manager(kFramesPerBuffer);
+
+ fft_manager.ApplyReverseFftScaling(&buffer[0]);
+ for (auto& sample : buffer[0]) {
+ EXPECT_NEAR(sample, kExpectedScale, kEpsilonFloat);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/filter_coefficient_generators.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/filter_coefficient_generators.cc
new file mode 100644
index 000000000..11d19694d
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/filter_coefficient_generators.cc
@@ -0,0 +1,165 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/filter_coefficient_generators.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+// Hard coded 4th order fit calculated from:
+
+const int kPolynomialOrder = 4;
+const float kPolynomialCoefficients[] = {
+ 2.52730826376129e-06f, 0.00018737263228963f, 0.00618822707412605f,
+ 0.113947188715447f, 0.999048314740445f};
+
+// Threshold frequency for human hearing 20Hz.
+const float kTwentyHz = 20.0f;
+
+// ln(2)/2 for use in bandpass filter.
+const float kLn2over2 = 0.346573590279973f;
+
+} // namespace
+
+BiquadCoefficients ComputeBandPassBiquadCoefficients(int sample_rate,
+ float center_frequency,
+ int bandwidth) {
+ DCHECK_GT(sample_rate, 0);
+ DCHECK_GE(center_frequency, 0.0f);
+ DCHECK_GT(bandwidth, 0);
+ // Check if an invalid |center_frequency|, greater than or equal to the
+ // Nyquist rate is passed as input.
+ CHECK_LT(center_frequency, 0.5f * static_cast<float>(sample_rate));
+
+ // Frequency of interest (in radians).
+ const float w0 = kTwoPi * center_frequency / static_cast<float>(sample_rate);
+ // Intermediate storage.
+ const float cos_w0 = std::cos(w0);
+ const float sin_w0 = std::sin(w0);
+ const float alpha =
+ sin_w0 * sinhf(kLn2over2 * static_cast<float>(bandwidth) * w0 / sin_w0);
+
+ // The BiquadFilterState constructor is passed (a0, a1, a2, b0, b1, b2).
+ return BiquadCoefficients(1.0f + alpha, -2.0f * cos_w0, 1.0f - alpha, alpha,
+ 0.0f, -alpha);
+}
+
+void ComputeDualBandBiquadCoefficients(
+ int sample_rate, float crossover_frequency,
+ BiquadCoefficients* low_pass_coefficients,
+ BiquadCoefficients* high_pass_coefficients) {
+ DCHECK_GT(sample_rate, 0);
+ DCHECK_GE(crossover_frequency, 0.0f);
+ DCHECK_LE(crossover_frequency, static_cast<float>(sample_rate) / 2.0f);
+ DCHECK(low_pass_coefficients);
+ DCHECK(high_pass_coefficients);
+
+ const float k = std::tan(static_cast<float>(M_PI) * crossover_frequency /
+ static_cast<float>(sample_rate));
+ const float k_squared = k * k;
+ const float denominator = k_squared + 2.0f * k + 1.0f;
+
+ // |denominator| must not be near 0. Since |k| is always guaranteed to be in
+ // the range 0 < k < pi/2, the |denominator| should always be >=1. This is a
+ // sanity check only.
+ DCHECK_GT(denominator, kEpsilonDouble);
+
+ // Computes numerator coefficients of the low-pass |low_pass_coefficients|
+ // bi-quad.
+ low_pass_coefficients->a[0] = 1.0f;
+ low_pass_coefficients->a[1] = 2.0f * (k_squared - 1.0f) / denominator;
+ low_pass_coefficients->a[2] = (k_squared - 2.0f * k + 1.0f) / denominator;
+
+ // Numerator coefficients of the high-pass |high_pass_coefficients| bi-quad
+ // are the same.
+ BiquadCoefficients high_pass;
+ std::copy(low_pass_coefficients->a.begin(), low_pass_coefficients->a.end(),
+ high_pass_coefficients->a.begin());
+
+ // Computes denominator coefficients of the low-pass |low_pass_coefficients|
+ // bi-quad.
+ low_pass_coefficients->b[0] = k_squared / denominator;
+ low_pass_coefficients->b[1] = 2.0f * low_pass_coefficients->b[0];
+ low_pass_coefficients->b[2] = low_pass_coefficients->b[0];
+
+ // Computes denominator coefficients of the high-pass |high_pass_coefficients|
+ // bi-quad.
+ high_pass_coefficients->b[0] = 1.0f / denominator;
+ high_pass_coefficients->b[1] = -2.0f * high_pass_coefficients->b[0];
+ high_pass_coefficients->b[2] = high_pass_coefficients->b[0];
+}
+
+BiquadCoefficients ComputeLowPassBiquadCoefficients(
+ int sample_rate, float specification_frequency, float attenuation) {
+ DCHECK_GT(sample_rate, 0);
+ DCHECK_GE(specification_frequency, 0.0f);
+ DCHECK_LE(specification_frequency, static_cast<float>(sample_rate) / 2.0f);
+ DCHECK_LT(attenuation, 0.0f);
+
+ // Frequency of interest (in radians).
+ const float w0 =
+ kTwoPi * specification_frequency / static_cast<float>(sample_rate);
+
+ // Q is the Q-factor. For more information see "Digital Signal Processing" -
+ // J. G. Prolakis, D. G. Manolakis - Published by Pearson.
+ float Q = 0.0f;
+
+ // Variable to handle the growth in power as one extra mult per iteration
+ // across the polynomial coefficients.
+ float attenuation_to_a_power = 1.0f;
+
+ // Add in each term in reverse order.
+ for (int order = kPolynomialOrder; order >= 0; --order) {
+ Q += kPolynomialCoefficients[order] * attenuation_to_a_power;
+ attenuation_to_a_power *= attenuation;
+ }
+
+ // Intermediate storage of commonly used values.
+ const float alpha = std::sin(w0) / (2.0f * Q);
+ const float cos_w0 = std::cos(w0);
+
+ // Filter coefficients.
+ float a0 = 1.0f + alpha;
+ float a1 = -2.0f * cos_w0;
+ float a2 = 1.0f - alpha;
+ // Coefficients b0 and b2 will have the same value in this case.
+ float b0_b2 = (1.0f - cos_w0) / 2.0f;
+ float b1 = 1.0f - cos_w0;
+
+ return BiquadCoefficients(a0, a1, a2, b0_b2, b1, b0_b2);
+}
+
+float ComputeLowPassMonoPoleCoefficient(float cuttoff_frequency,
+ int sample_rate) {
+ float coefficient = 0.0f;
+ // Check that the cuttoff_frequency provided is not too low (below human
+ // threshold, also danger of filter instability).
+ if (cuttoff_frequency > kTwentyHz) {
+ const float inverse_time_constant = kTwoPi * cuttoff_frequency;
+ const float sample_rate_float = static_cast<float>(sample_rate);
+ coefficient =
+ sample_rate_float / (inverse_time_constant + sample_rate_float);
+ }
+ return coefficient;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/filter_coefficient_generators.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/filter_coefficient_generators.h
new file mode 100644
index 000000000..4c7659a7a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/filter_coefficient_generators.h
@@ -0,0 +1,177 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_FILTER_COEFFICIENT_GENERATORS_H_
+#define RESONANCE_AUDIO_DSP_FILTER_COEFFICIENT_GENERATORS_H_
+
+#include "dsp/biquad_filter.h"
+
+// Functions for the generation of filter coefficients for various common tasks.
+// Currently supports the following filter types:
+// Low pass first-order filter.
+// Low pass biquad filter.
+// Band pass biquad filter.
+// Dual band, matched phase biquad shelf filters.
+namespace vraudio {
+
+// Computes corresponding biquad coefficients for band pass filter with respect
+// to the centre frequency and the bandwidth between -3 dB frequencies.
+//
+// b0 + b1*z^-1 + b2*z^-2
+// H(z) = ------------------------
+// a0 + a1*z^-1 + a2*z^-2
+//
+// where:
+// w0 = 2*pi*center_frequency/sample_rate
+//
+// alpha = sin(w0)*sinh( ln(2)/2 * bandwidth * w0/sin(w0) )
+//
+// b0 = alpha
+// b1 = 0
+// b2 = -alpha
+// a0 = 1 + alpha
+// a1 = -2*cos(w0)
+// a2 = 1 - alpha
+//
+// @param sample_rate Sampling rate in Hz.
+// @param centre_frequency Centre frequency of passband.
+// @param bandwidth Bandwidth in octaves between -3 dB frequencies.
+// @return Output structure of band-pass BiquadCoefficients.
+BiquadCoefficients ComputeBandPassBiquadCoefficients(int sample_rate,
+ float centre_frequency,
+ int bandwidth);
+
+// Computes two sets of transfer function coefficients to be used with a
+// pair of generic bi-quad filters. The coefficients are used to implement a
+// phase-matched low-pass |low_pass_state| and high-pass |high_pass_state|
+// filter pair with a cross-over frequency defined as |crossover_frequency|.
+//
+// Implementation of the matched bi-quad filter pair as described in:
+// http://www.ai.sri.com/ajh/ambisonics/BLaH3.pdf
+//
+// b0 + b1*z^-1 + b2*z^-2
+// H(z) = ------------------------
+// a0 + a1*z^-1 + a2*z^-2
+//
+// where:
+//
+// a0 = 1
+//
+// 2(k^2 − 1)
+// a1 = --------------
+// k^2 + 2k + 1
+//
+// k^2 - 2k + 1
+// a2 = --------------
+// k^2 + 2k + 1
+//
+// low-pass: high-pass:
+//
+// k^2 1
+// b0 = ------------- b0 = --------------
+// k^2 + 2k + 1 k^2 + 2k + 1
+//
+// b1 = 2b0 b1 = -2b0
+//
+// b2 = b0 b2 = b0
+//
+// and
+//
+// pi * crossover_frequency
+// k = tan --------------------------
+// sample_frequency
+//
+// @param sample_rate Sampling rate in [Hz]
+// @param crossover_frequency Cross-over frequency in [Hz]
+// @param low_pass_coefficients Output structure of low-pass bi-quad
+// coefficients
+// @param high_pass_coefficients Output structure of high-pass bi-quad
+// coefficients.
+void ComputeDualBandBiquadCoefficients(
+ int sample_rate, float crossover_frequency,
+ BiquadCoefficients* low_pass_coefficients,
+ BiquadCoefficients* high_pass_coefficients);
+
+// Computes biquad coefficients for low pass filter with respect to the
+// specification frequency and the attenuation value at that frequency.
+//
+// b0 + b1*z^-1 + b2*z^-2
+// H(z) = ------------------------
+// a0 + a1*z^-1 + a2*z^-2
+//
+// where:
+// Q = 2.5273e-06*attenuation^4 + 0.00018737*attenuation^3 +
+// 0.0061882*attenuation^2 + 0.11395*attenuation + 0.99905
+//
+// w0 = 2*pi*specification_frequency/sample_rate;
+//
+// alpha = sin(w0)/(2*Q);
+//
+// a0 = 1 + alpha;
+// a1 = -2*cos(w0);
+// a2 = 1 - alpha;
+// b0 = (1 - cos(w0))/2;
+// b1 = 1 - cos(w0);
+// b2 = (1 - cos(w0)/2;
+//
+// These coefficients were generated after a bilinerar transform of an
+// analogue filter H(s) = 1 / (s^2 + s/Q + 1), from:
+// www.analog.com/library/analogdialogue/archives/43-09/edch%208%20filter.pdf.
+//
+// Please note Q has been calculated by fitting a 4th order polynomial to the
+// average of Q vs attenuation curves at different f0's from 10kHz to 19kHz.
+// Please note that at differing frequencies these graphs had identical shape
+// and differed only by an overall offset of at most 0.1 (attenuation) dB from
+// the mean. This script can be found at
+
+//
+// @param sample_rate Sampling rate in Hz.
+// @param specification_frequency Frequency at which attenuation applies in Hz.
+// @param attenuation Attenuation at specification_frequency in negative dB.
+// @return low_pass Output structure of low-pass BiquadCoefficients.
+BiquadCoefficients ComputeLowPassBiquadCoefficients(
+ int sample_rate, float specification_frequency, float attenuation);
+
+// Generates a coefficient for the |MonoPoleFilterClass| based on a 3dB
+// bandwidth.
+//
+// The Laplace transfer function of a first order low pass system is:
+//
+// 1
+// ------------ where tau is the RC time constant of the system.
+// 1 + tau * s
+//
+// For a discrete moving average filter with input x[n] and output y[n],
+// the difference equation is:
+// y[n] = a * y[n - 1] + (1 - a) * x[n]
+// tau
+// a = ---------- where T is the sample period.
+// tau + T
+// since the -3dB bandwith of a first order system can be
+// 1 related to its time constant by
+// f3 = --------------
+// 2 * pi * tau we can obtain 'a' from cuttoff_frequency and
+// sample rate.
+//
+// @param cuttoff_frequency The -3dB frequency in Hz of the low pass lobe.
+// @param sample_rate System sampling rate.
+// @return A |MonoPoleFilterClass| coefficient for the specified bandwidth.
+float ComputeLowPassMonoPoleCoefficient(float cuttoff_frequency,
+ int sample_rate);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_FILTER_COEFFICIENT_GENERATORS_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/filter_coefficient_generators_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/filter_coefficient_generators_test.cc
new file mode 100644
index 000000000..163b6a6d2
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/filter_coefficient_generators_test.cc
@@ -0,0 +1,146 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/filter_coefficient_generators.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+
+namespace vraudio {
+
+namespace {
+
+// Allowable error between filter coefficients.
+const float kErrorf = 1e-5f;
+
+// Input into the ComputeLowPassBiquadCoefficients() function.
+const int kSampleRate = 44100;
+const float kSpecificationFrequency = 13000.0f;
+const float kAttenuation = -15.0f;
+
+const float kMonoPoleCoefficient = 0.94863985f;
+
+// These filter coefficients were calculated in MATLAB with a specified Q value
+// of 0.17775. The attenuation at the specification frequency was then
+// calculated from the MATLAB freqz() calls.
+const BiquadCoefficients kIdealCoefficients(3.70224817628938f,
+ 0.555382147099001f,
+ -1.70224817628938f,
+ 0.63884553677475f, 1.2776910735495f,
+ 0.63884553677475f);
+
+// These filter coefficients were calculated in MATLAB for a centre_frequency of
+// 8kHz and a bandwidth of 1 octave.
+const BiquadCoefficients kIdealBandpassCoefficients(1.37364799922092f, -1.0f,
+ 0.626352000779082f,
+ 0.373647999220918f, 0.0f,
+ -0.373647999220918f);
+
+const float kBandPassCenterFrequency = 8000.0f;
+const int kBandPassOctaveBandwidth = 1;
+
+// Input into the ComputeDualBandBiquadCoefficients() function.
+const int kSampleRate48 = 48000;
+const float kCrossoverFrequency = 380.0f;
+
+// Pre-computed coefficients for the phase-matched filter pair (dual-band) with
+// the cross-over frequency set at 380Hz and the sample rate 48kHz.
+const float kLowPassA[] = {1.0f, -1.9029109f, 0.90526748f};
+const float kLowPassB[] = {0.00058914319f, 0.0011782864f, 0.00058914319f};
+const float kHighPassA[] = {1.0f, -1.9029109f, 0.90526748f};
+const float kHighPassB[] = {0.95204461f, -1.9040892f, 0.95204461f};
+
+TEST(FilterCoefficientGeneratorsTest, ComputeMonoPoleLowpassCoefficientTest) {
+ float coefficient =
+ ComputeLowPassMonoPoleCoefficient(kCrossoverFrequency, kSampleRate);
+ EXPECT_NEAR(kMonoPoleCoefficient, coefficient, kErrorf);
+
+ const float twenty_hertz = 20.0f;
+ coefficient = ComputeLowPassMonoPoleCoefficient(twenty_hertz, kSampleRate);
+ EXPECT_EQ(0.0f, coefficient);
+ // New test for the case where a frequency below 20Hz is passed.
+}
+
+// Tests that the BiquadCoefficients generated by
+// ComputeBandPassBiquadCoefficients() match those known to be correct as
+// directly calculated from MATLAB.
+TEST(FilterCoefficientGeneratorsTest, ComputeBandPassBiquadCoefficientsTest) {
+ // Perform computation.
+ BiquadCoefficients biquad_filter_coefficients =
+ ComputeBandPassBiquadCoefficients(kSampleRate48, kBandPassCenterFrequency,
+ kBandPassOctaveBandwidth);
+
+ // Make sure they are all equal.
+ for (size_t i = 0; i < biquad_filter_coefficients.a.size(); ++i) {
+ EXPECT_NEAR(kIdealBandpassCoefficients.a[i],
+ biquad_filter_coefficients.a[i], kErrorf);
+ EXPECT_NEAR(kIdealBandpassCoefficients.b[i],
+ biquad_filter_coefficients.b[i], kErrorf);
+ }
+}
+
+// Tests that the BiquadCoefficients generated by
+// ComputeLowPassBiquadCoefficients() match those known to be correct as
+// directly calculated from MATLAB. This also tests the validity of the inherent
+// 4th order best fit between attenuation at a specified frequency and Q factor.
+TEST(FilterCoefficientGeneratorsTest, ComputeLowPassBiquadCoefficientsTest) {
+ // Perform computation.
+ BiquadCoefficients biquad_filter_coefficients =
+ ComputeLowPassBiquadCoefficients(kSampleRate, kSpecificationFrequency,
+ kAttenuation);
+
+ // Make sure they are all equal.
+ for (size_t i = 0; i < biquad_filter_coefficients.a.size(); ++i) {
+ EXPECT_NEAR(kIdealCoefficients.a[i], biquad_filter_coefficients.a[i],
+ kErrorf);
+ EXPECT_NEAR(kIdealCoefficients.b[i], biquad_filter_coefficients.b[i],
+ kErrorf);
+ }
+}
+
+// Tests that the BiquadCoefficients generated by
+// ComputeDualBandBiquadCoefficients() match those known to be correct as
+// directly calculated from MATLAB. These coefficients are as described in:
+// http://www.ai.sri.com/ajh/ambisonics/BLaH3.pdf.
+TEST(DualBandBiquadCoefficientsTest, ComputeDualBandBiquadCoefficientsTest) {
+ // Generate default bi-quad coefficients and constructs low- and high-pass
+ // filter coefficients.
+ const BiquadCoefficients kDefaultCoefficients;
+ BiquadCoefficients low_pass_coefficients(kDefaultCoefficients);
+ BiquadCoefficients high_pass_coefficients(kDefaultCoefficients);
+
+ // Perform computation.
+ ComputeDualBandBiquadCoefficients(kSampleRate48, kCrossoverFrequency,
+ &low_pass_coefficients,
+ &high_pass_coefficients);
+
+ // Check if arrays with a and b coefficients for both filters are of the
+ // same size.
+ ASSERT_EQ(low_pass_coefficients.a.size(), low_pass_coefficients.b.size());
+ ASSERT_EQ(high_pass_coefficients.a.size(), low_pass_coefficients.a.size());
+ ASSERT_EQ(high_pass_coefficients.b.size(), low_pass_coefficients.b.size());
+
+ // Make sure they are all equal.
+ for (size_t i = 0; i < low_pass_coefficients.a.size(); ++i) {
+ EXPECT_NEAR(low_pass_coefficients.a[i], kLowPassA[i], kErrorf);
+ EXPECT_NEAR(low_pass_coefficients.b[i], kLowPassB[i], kErrorf);
+ EXPECT_NEAR(high_pass_coefficients.a[i], kHighPassA[i], kErrorf);
+ EXPECT_NEAR(high_pass_coefficients.b[i], kHighPassB[i], kErrorf);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/fir_filter.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/fir_filter.cc
new file mode 100644
index 000000000..e77ab71ce
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/fir_filter.cc
@@ -0,0 +1,148 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "dsp/fir_filter.h"
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/simd_macros.h"
+
+namespace vraudio {
+
+FirFilter::FirFilter(const AudioBuffer::Channel& filter_coefficients,
+ size_t frames_per_buffer) {
+ DCHECK_GE(filter_coefficients.size(), 1U);
+ // Create the kernel buffer in repeated entry representation from standard
+ // form FIR representation.
+ const size_t coefficients_length = filter_coefficients.size();
+ filter_length_ = coefficients_length +
+ ((coefficients_length % SIMD_LENGTH == 0)
+ ? 0
+ : SIMD_LENGTH - (coefficients_length % SIMD_LENGTH));
+ num_filter_chunks_ = filter_length_ / SIMD_LENGTH;
+ DCHECK_EQ(filter_length_ % SIMD_LENGTH, 0);
+ coefficients_ = AudioBuffer(kNumMonoChannels, filter_length_ * SIMD_LENGTH);
+ AudioBuffer::Channel* coefficients = &(coefficients_[0]);
+ // Store the coefficients so that each individual coefficient is repeated
+ // SIMD_LENGTH times.
+ for (size_t coeff = 0; coeff < coefficients_length; ++coeff) {
+ auto coefficient_iter = coefficients->begin() + (coeff * SIMD_LENGTH);
+ std::fill(coefficient_iter, coefficient_iter + SIMD_LENGTH,
+ filter_coefficients[coeff]);
+ }
+ std::fill(coefficients->begin() + (coefficients_length * SIMD_LENGTH),
+ coefficients->end(), 0.0f);
+ // Allocate an aligned buffer with |filter_length_| extra samples to
+ // store previous input samples.
+ state_ = AudioBuffer(kNumMonoChannels, frames_per_buffer + filter_length_);
+ state_.Clear();
+}
+
+void FirFilter::Process(const AudioBuffer::Channel& input,
+ AudioBuffer::Channel* output) {
+ DCHECK(output);
+ DCHECK_EQ(input.size(), state_.num_frames() - filter_length_);
+ // In this case we know that SIMD_LENGTH == 1, therefore we don't need to take
+ // account of it.
+ const size_t input_length = input.size();
+ std::copy_n(state_[0].end() - filter_length_, filter_length_,
+ state_[0].begin());
+ std::copy_n(input.begin(), input_length, state_[0].begin() + filter_length_);
+#if defined(SIMD_DISABLED)
+ AudioBuffer::Channel* coefficients = &(coefficients_[0]);
+ AudioBuffer::Channel* data = &(state_[0]);
+ for (size_t frame = 0; frame < input_length; ++frame) {
+ for (size_t coeff = 0; coeff < filter_length_; ++coeff) {
+ (*output)[frame] +=
+ (*coefficients)[coeff] * (*data)[filter_length_ - coeff];
+ }
+ }
+#else
+ const SimdVector* cptr_input =
+ reinterpret_cast<const SimdVector*>(&(state_[0][0]));
+ const SimdVector* cptr_filter =
+ reinterpret_cast<const SimdVector*>(&(coefficients_[0][0]));
+ SimdVector* ptr_output = reinterpret_cast<SimdVector*>(&((*output)[0]));
+
+ const size_t num_input_chunks = input_length / SIMD_LENGTH;
+ DCHECK_EQ(input_length % SIMD_LENGTH, 0);
+
+ // A pair of |SimdVector|s one for holding the input for each operation, the
+ // other to store data that needed to be copied as it straddled a four float
+ // boundary.
+ SimdVector input_use;
+ SimdVector input_split;
+ for (size_t input_position = num_filter_chunks_;
+ input_position < num_input_chunks + num_filter_chunks_;
+ ++input_position) {
+ // Replace these with the indexed pointers to save on copies.
+ SimdVector* output_now = &ptr_output[input_position - num_filter_chunks_];
+ DCHECK_GE(input_position, num_filter_chunks_);
+
+ for (size_t chunk = 0; chunk < num_filter_chunks_; ++chunk) {
+ const size_t filter_index_offset = chunk * SIMD_LENGTH;
+ const SimdVector* input_a = &cptr_input[input_position - (chunk + 1)];
+ const SimdVector* input_b = &cptr_input[input_position - chunk];
+#if defined(SIMD_SSE)
+ // Select four floats from input_a and input_b, based on the mask. Here we
+ // take the latter two entries from input_b followed by the first two
+ // entries of input_a.
+ input_split = _mm_shuffle_ps(*input_a, *input_b, _MM_SHUFFLE(1, 0, 3, 2));
+
+ // All from input_b.
+ *output_now = SIMD_MULTIPLY_ADD(
+ *input_b, cptr_filter[filter_index_offset], *output_now);
+ // One from input_a and the rest (three) from input_b.
+ input_use =
+ _mm_shuffle_ps(input_split, *input_b, _MM_SHUFFLE(2, 1, 2, 1));
+ *output_now = SIMD_MULTIPLY_ADD(
+ input_use, cptr_filter[1 + filter_index_offset], *output_now);
+ // Two from input_a and two from input_b.
+ *output_now = SIMD_MULTIPLY_ADD(
+ input_split, cptr_filter[2 + filter_index_offset], *output_now);
+ // Three from input_a and one from input_b.
+ input_use =
+ _mm_shuffle_ps(*input_a, input_split, _MM_SHUFFLE(2, 1, 2, 1));
+ *output_now = SIMD_MULTIPLY_ADD(
+ input_use, cptr_filter[3 + filter_index_offset], *output_now);
+#elif defined(SIMD_NEON)
+ // All from input_b.
+ *output_now = SIMD_MULTIPLY_ADD(
+ *input_b, cptr_filter[filter_index_offset], *output_now);
+ // One from input_a and the rest (three) from input_b.
+ input_use = vextq_f32(*input_a, *input_b, 3);
+ *output_now = SIMD_MULTIPLY_ADD(
+ input_use, cptr_filter[1 + filter_index_offset], *output_now);
+ // Two from input_a and two from input_b.
+ input_use = vextq_f32(*input_a, *input_b, 2);
+ *output_now = SIMD_MULTIPLY_ADD(
+ input_use, cptr_filter[2 + filter_index_offset], *output_now);
+ // Three from input_a and one from input_b.
+ input_use = vextq_f32(*input_a, *input_b, 1);
+ *output_now = SIMD_MULTIPLY_ADD(
+ input_use, cptr_filter[3 + filter_index_offset], *output_now);
+#endif // SIMD_SSE/SIMD_NEON
+ }
+ }
+#endif // SIMD_DISABLED
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/fir_filter.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/fir_filter.h
new file mode 100644
index 000000000..46c1edde5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/fir_filter.h
@@ -0,0 +1,62 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_FIR_FILTER_H_
+#define RESONANCE_AUDIO_DSP_FIR_FILTER_H_
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+class FirFilter {
+ public:
+ // Constructs a |FirFilter| from a mono |AudioBuffer| of filter coefficients.
+ //
+ // @param filter_coefficients FIR filter coefficients.
+ // @param frames_per_buffer Number of frames of data in each audio buffer.
+ FirFilter(const AudioBuffer::Channel& filter_coefficients,
+ size_t frames_per_buffer);
+
+ // Filters an array of input with a finite impulse response (FIR) filter in
+ // the time domain. All pointers and lengths passed must be SIMD compatible.
+ //
+ // @param input Pointer to an aray of input.
+ // @param output Pointer to a cleared block of memory of the input length.
+ void Process(const AudioBuffer::Channel& input, AudioBuffer::Channel* output);
+
+ // Returns the length of the filter kernel after zeropadding.
+ //
+ // @return Length of the FIR filter in frames.
+ size_t filter_length() const { return filter_length_; }
+
+ private:
+ // Length of the filter kernel in frames after zeropadding.
+ size_t filter_length_;
+
+ // Number of filter chunks equivalent to the filter length after zeropadding
+ // divided by the SIMD_LENGTH.
+ size_t num_filter_chunks_;
+
+ // Coefficients of the filter stored in a repeated format.
+ AudioBuffer coefficients_;
+
+ // Aligned buffer of previous and current input.
+ AudioBuffer state_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_FIR_FILTER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/fir_filter_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/fir_filter_test.cc
new file mode 100644
index 000000000..e64a44f15
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/fir_filter_test.cc
@@ -0,0 +1,104 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/fir_filter.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/simd_macros.h"
+
+namespace vraudio {
+
+namespace {
+
+const size_t kFirFramesPerBuffer = 32;
+const size_t kFirKernelLength = 8;
+
+// Tests that the filter is resized to a length compatable with the current
+// SIMD_LENGTH whether it is one greater than or one less than a multiple of
+// SIMD_LENGTH to begin with.
+TEST(FirFilterTest, GetFilterLengthTest) {
+ const size_t twice_simd_length = (SIMD_LENGTH * 2);
+ // Divisible by the length of a SIMD vector.
+ AudioBuffer kernel_buffer1(kNumMonoChannels, twice_simd_length);
+ FirFilter filter1(kernel_buffer1[0], twice_simd_length);
+ EXPECT_EQ(twice_simd_length, filter1.filter_length());
+
+ // Shorter filter.
+ AudioBuffer kernel_buffer2(kNumMonoChannels, twice_simd_length - 1);
+ FirFilter filter2(kernel_buffer2[0], twice_simd_length - 1);
+ EXPECT_EQ(twice_simd_length, filter2.filter_length());
+
+ // Longer filter.
+ AudioBuffer kernel_buffer3(kNumMonoChannels, twice_simd_length + 1);
+ FirFilter filter3(kernel_buffer3[0], twice_simd_length + 1);
+ EXPECT_EQ(twice_simd_length + SIMD_LENGTH, filter3.filter_length());
+}
+
+// Tests that the output from a convolution between a double kronecker input and
+// a alternating 1, 0 buffer of input yields the correct output buffer.
+TEST(FirFilterTest, ProcessWithDoubleKronecker) {
+ AudioBuffer input_buffer(kNumMonoChannels, kFirFramesPerBuffer);
+ input_buffer.Clear();
+ AudioBuffer output_buffer(1, kFirFramesPerBuffer);
+ output_buffer.Clear();
+ // First create a vector containing the FIR filter coefficients in standard
+ // format.
+ AudioBuffer kernel_buffer(kNumMonoChannels, kFirKernelLength);
+ kernel_buffer.Clear();
+ kernel_buffer[0][0] = 1.0f;
+ kernel_buffer[0][kFirKernelLength / 2] = 1.0f;
+ // Now we can create the kernel buffer in its repeated entry representation
+ // from this standard form FIR representation.
+ FirFilter filter(kernel_buffer[0], kFirFramesPerBuffer);
+ // Next populate the input buffer.
+ for (size_t i = 0; i < kFirFramesPerBuffer; i += 2) {
+ input_buffer[0][i] = 1.0f;
+ }
+
+ filter.Process(input_buffer[0], &(output_buffer[0]));
+
+ for (size_t i = 0; i < kFirFramesPerBuffer; ++i) {
+ if (i % 2 != 0) {
+ EXPECT_NEAR(0.0f, output_buffer[0][i], kEpsilonFloat);
+ } else if (i <= 3) {
+ EXPECT_NEAR(1.0f, output_buffer[0][i], kEpsilonFloat);
+ } else {
+ EXPECT_NEAR(2.0f, output_buffer[0][i], kEpsilonFloat);
+ }
+ }
+
+ // Run again with a cleared buffer to flush out the filter state.
+ input_buffer.Clear();
+ output_buffer.Clear();
+
+ filter.Process(input_buffer[0], &(output_buffer[0]));
+
+ for (size_t i = 0; i < kFirFramesPerBuffer; ++i) {
+ if (i % 2 != 0) {
+ EXPECT_NEAR(0.0f, output_buffer[0][i], kEpsilonFloat);
+ } else if (i <= 3) {
+ EXPECT_NEAR(1.0f, output_buffer[0][i], kEpsilonFloat);
+ } else {
+ EXPECT_NEAR(0.0f, output_buffer[0][i], kEpsilonFloat);
+ }
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/gain.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain.cc
new file mode 100644
index 000000000..54e3fc216
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain.cc
@@ -0,0 +1,102 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/gain.h"
+
+#include "base/constants_and_types.h"
+#include "base/simd_macros.h"
+#include "base/simd_utils.h"
+
+namespace vraudio {
+
+float LinearGainRamp(size_t ramp_length, float start_gain, float end_gain,
+ const AudioBuffer::Channel& input_samples,
+ AudioBuffer::Channel* output_samples,
+ bool accumulate_output) {
+ DCHECK(output_samples);
+ DCHECK_EQ(input_samples.size(), output_samples->size());
+ DCHECK_GT(ramp_length, 0U);
+
+ const size_t process_length = std::min(ramp_length, input_samples.size());
+ const float gain_increment_per_sample =
+ (end_gain - start_gain) / static_cast<float>(ramp_length);
+
+ float current_gain = start_gain;
+ if (accumulate_output) {
+ for (size_t frame = 0; frame < process_length; ++frame) {
+ (*output_samples)[frame] += current_gain * input_samples[frame];
+ current_gain += gain_increment_per_sample;
+ }
+ } else {
+ for (size_t frame = 0; frame < process_length; ++frame) {
+ (*output_samples)[frame] = current_gain * input_samples[frame];
+ current_gain += gain_increment_per_sample;
+ }
+ }
+
+ return current_gain;
+}
+
+void ConstantGain(size_t offset_index, float gain,
+ const AudioBuffer::Channel& input_samples,
+ AudioBuffer::Channel* output_samples,
+ bool accumulate_output) {
+ DCHECK(output_samples);
+ const size_t input_size = input_samples.size();
+ DCHECK_EQ(input_size, output_samples->size());
+ DCHECK_LT(offset_index, input_size);
+
+ // Apply gain to samples at the beginning, prior to SIMD_LENGTH alignment.
+ const size_t unaligned_samples = SIMD_LENGTH - (offset_index % SIMD_LENGTH);
+ const size_t offset_index_simd =
+ std::min(input_size, offset_index + unaligned_samples);
+ if (accumulate_output) {
+ for (size_t i = offset_index; i < offset_index_simd; ++i) {
+ (*output_samples)[i] += input_samples[i] * gain;
+ }
+ } else {
+ for (size_t i = offset_index; i < offset_index_simd; ++i) {
+ (*output_samples)[i] = input_samples[i] * gain;
+ }
+ }
+
+ if (offset_index_simd == input_size) {
+ // Return if there are no remaining operations to carry out.
+ return;
+ }
+
+ const size_t aligned_length = input_size - offset_index_simd;
+ const float* aligned_input = &(input_samples[offset_index_simd]);
+ float* aligned_output = &(*output_samples)[offset_index_simd];
+
+ // Apply gain via SIMD operations.
+ if (accumulate_output) {
+ ScalarMultiplyAndAccumulate(aligned_length, gain, aligned_input,
+ aligned_output);
+ } else {
+ ScalarMultiply(aligned_length, gain, aligned_input, aligned_output);
+ }
+}
+
+bool IsGainNearZero(float gain) {
+ return std::abs(gain) < kNegative60dbInAmplitude;
+}
+
+bool IsGainNearUnity(float gain) {
+ return std::abs(1.0f - gain) < kNegative60dbInAmplitude;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/gain.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain.h
new file mode 100644
index 000000000..500af8c22
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain.h
@@ -0,0 +1,67 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_GAIN_H_
+#define RESONANCE_AUDIO_DSP_GAIN_H_
+
+#include <cstddef>
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Implements a linearly-interpolated application of gain to a buffer channel.
+//
+// @param ramp_length Length of interpolation ramp in samples. Must be > 0.
+// @param start_gain Starting gain value for ramp.
+// @param end_gain Finishing gain value for ramp.
+// @param input_samples Channel buffer to which interpolated gain is applied.
+// @param output_samples Channel buffer to contain scaled output.
+// @param accumulate_output True if the processed input should be mixed into the
+// output. Otherwise, the output will be replaced by the processed input.
+// @return Next gain value to be applied to the buffer channel.
+float LinearGainRamp(size_t ramp_length, float start_gain, float end_gain,
+ const AudioBuffer::Channel& input_samples,
+ AudioBuffer::Channel* output_samples,
+ bool accumulate_output);
+
+// Applies a gain value to a vector of buffer samples starting at some offset.
+//
+// @param offset_index Starting index for gain application in buffer.
+// @param gain Gain value applied to samples.
+// @param input_samples Channel buffer to which gain is applied.
+// @param output_samples Channel buffer to contain scaled output.
+// @param accumulate_output True if the processed input should be mixed into the
+// output. Otherwise, the output will be replaced by the processed input.
+void ConstantGain(size_t offset_index, float gain,
+ const AudioBuffer::Channel& input_samples,
+ AudioBuffer::Channel* output_samples, bool accumulate_output);
+
+// Checks if the gain factor is close enough to zero (less than -60 decibels).
+//
+// @param gain Gain value to be tested.
+// @return true if the current gain factors are near zero, false otherwise.
+bool IsGainNearZero(float gain);
+
+// Checks if the gain state is close enough to Unity (less than -60 decibels
+// below or above).
+//
+// @param gain Gain value to be tested.
+// @return true if the current gain factors are near unity, false otherwise.
+bool IsGainNearUnity(float gain);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_GAIN_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_mixer.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_mixer.cc
new file mode 100644
index 000000000..06417be11
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_mixer.cc
@@ -0,0 +1,115 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/gain_mixer.h"
+
+#include <cmath>
+
+#include "base/logging.h"
+
+namespace vraudio {
+
+GainMixer::GainMixer(size_t num_channels, size_t frames_per_buffer)
+ : num_channels_(num_channels),
+ output_(num_channels_, frames_per_buffer),
+ is_empty_(false) {
+ DCHECK_NE(num_channels_, 0U);
+ Reset();
+}
+
+void GainMixer::AddInput(const AudioBuffer& input,
+ const std::vector<float>& gains) {
+ DCHECK_EQ(gains.size(), num_channels_);
+ DCHECK_EQ(input.num_channels(), num_channels_);
+ DCHECK_EQ(input.num_frames(), output_.num_frames());
+
+ auto* gain_processors = GetOrCreateProcessors(input.source_id());
+ // Accumulate the input buffers into the output buffer.
+ for (size_t i = 0; i < num_channels_; ++i) {
+ if (input[i].IsEnabled()) {
+ (*gain_processors)[i].ApplyGain(gains[i], input[i], &output_[i],
+ true /* accumulate_output */);
+ } else {
+ // Make sure the gain processor is initialized.
+ (*gain_processors)[i].Reset(gains[i]);
+ }
+ }
+ is_empty_ = false;
+}
+
+void GainMixer::AddInputChannel(const AudioBuffer::Channel& input,
+ SourceId source_id,
+ const std::vector<float>& gains) {
+ DCHECK_EQ(gains.size(), num_channels_);
+ DCHECK_EQ(input.size(), output_.num_frames());
+
+ auto* gain_processors = GetOrCreateProcessors(source_id);
+ // Accumulate the input buffers into the output buffer.
+ for (size_t i = 0; i < num_channels_; ++i) {
+ if (input.IsEnabled()) {
+ (*gain_processors)[i].ApplyGain(gains[i], input, &output_[i],
+ true /* accumulate_output */);
+ } else {
+ // Make sure the gain processor is initialized.
+ (*gain_processors)[i].Reset(gains[i]);
+ }
+ }
+ is_empty_ = false;
+}
+
+const AudioBuffer* GainMixer::GetOutput() const {
+ if (is_empty_) {
+ return nullptr;
+ }
+ return &output_;
+}
+
+void GainMixer::Reset() {
+ if (!is_empty_) {
+ // Delete the processors for sources which no longer exist.
+ for (auto it = source_gain_processors_.begin();
+ it != source_gain_processors_.end();
+ /* no increment */) {
+ if (it->second.processors_active) {
+ it->second.processors_active = false;
+ ++it;
+ } else {
+ source_gain_processors_.erase(it++);
+ }
+ }
+ // Reset the output buffer.
+ output_.Clear();
+ }
+ is_empty_ = true;
+}
+
+GainMixer::GainProcessors::GainProcessors(size_t num_channels)
+ : processors_active(true), processors(num_channels) {}
+
+std::vector<GainProcessor>* GainMixer::GetOrCreateProcessors(
+ SourceId source_id) {
+ // Attempt to find a |ScaleAndAccumulateProcessor| for the given |source_id|,
+ // if none can be found add one. In either case mark that the processor has
+ // been used so that it is not later deleted.
+ if (source_gain_processors_.find(source_id) ==
+ source_gain_processors_.end()) {
+ source_gain_processors_.insert({source_id, GainProcessors(num_channels_)});
+ }
+ source_gain_processors_.at(source_id).processors_active = true;
+ return &(source_gain_processors_.at(source_id).processors);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_mixer.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_mixer.h
new file mode 100644
index 000000000..18c785c26
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_mixer.h
@@ -0,0 +1,96 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_GAIN_MIXER_H_
+#define RESONANCE_AUDIO_DSP_GAIN_MIXER_H_
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "dsp/gain_processor.h"
+
+namespace vraudio {
+
+class GainMixer {
+ public:
+ GainMixer(size_t num_channels, size_t frames_per_buffer);
+
+ // Adds a separately scaled version of each channel of the input buffer to the
+ // output buffer's corresponding channels.
+ //
+ // @param input Input buffer to be added.
+ // @param gains Gains to be applied to the buffers channels. Must be equal in
+ // length to the number of channels in |input|.
+ void AddInput(const AudioBuffer& input, const std::vector<float>& gains);
+
+ // Adds a single input channel to each of the output buffer's channels, with
+ // a separate gain applied to the input channel per output channel.
+ //
+ // @param input Input channel to be added.
+ // @param source_id Identifier corresponding to the input.
+ // @param gains Gains to be applied to the buffers channels. Must be equal in
+ // length to the number of channels in the output buffer.
+ void AddInputChannel(const AudioBuffer::Channel& input, SourceId source_id,
+ const std::vector<float>& gains);
+
+ // Returns a pointer to the accumulator.
+ //
+ // @return Pointer to the processed (mixed) output buffer, or nullptr if no
+ // input has been added to the accumulator.
+ const AudioBuffer* GetOutput() const;
+
+ // Resets the state of the accumulator.
+ void Reset();
+
+ private:
+ // Comprises one |GainProcessor| per channel of a source and a boolean to
+ // denote whether that source is active.
+ struct GainProcessors {
+ explicit GainProcessors(size_t num_channels);
+
+ // Bool to signify if a given source is still passing data to a processor.
+ bool processors_active;
+
+ // Scale and accumulation processors, one per channel for each source.
+ std::vector<GainProcessor> processors;
+ };
+
+ // Returns the |GainProcessor|s associated with a |source_id| (or creates
+ // one if needed) and sets the corresponding |processors_active| flag to true.
+ //
+ // @param source_id Identifier for a given input.
+ // @return The corresponding |ScalingAccumulators|.
+ std::vector<GainProcessor>* GetOrCreateProcessors(SourceId source_id);
+
+ // Number of channels.
+ const size_t num_channels_;
+
+ // Output buffer (accumulator).
+ AudioBuffer output_;
+
+ // Denotes whether the accumulator has processed any inputs or not.
+ bool is_empty_;
+
+ // Scale and accumulation processors, one per channel for each source.
+ std::unordered_map<SourceId, GainProcessors> source_gain_processors_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_GAIN_MIXER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_mixer_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_mixer_test.cc
new file mode 100644
index 000000000..70a45a692
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_mixer_test.cc
@@ -0,0 +1,188 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/gain_mixer.h"
+
+#include <iterator>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "utils/planar_interleaved_conversion.h"
+
+namespace vraudio {
+
+namespace {
+
+const int kId1 = 0;
+const int kId2 = 1;
+
+// Number of frames per output buffer.
+const size_t kFramesPerBuffer = 3;
+
+// Sample mono input data, gains, and the corresponding output data.
+const float kGain1 = 0.5f;
+const float kGain2 = 2.0f;
+const float kInput1[kFramesPerBuffer] = {0.2f, 0.4f, 0.6f};
+const float kInput2[kFramesPerBuffer] = {0.5f, 1.0f, 1.5f};
+const float kOutput[kFramesPerBuffer] = {1.1f, 2.2f, 3.3f};
+
+// Tests that the gain mixer returns null output if no input has been added.
+TEST(MixerTest, EmptyInputTest) {
+ GainMixer gain_mixer(kNumMonoChannels, kFramesPerBuffer);
+
+ const AudioBuffer* output = gain_mixer.GetOutput();
+ EXPECT_TRUE(output == nullptr);
+}
+
+// Mono processing test with two inputs. Tests the mixed output buffer against
+// the manually computed output data.
+TEST(GainMixerTest, ProcessTest) {
+ GainMixer gain_mixer(kNumMonoChannels, kFramesPerBuffer);
+
+ // Initialize input buffers.
+ AudioBuffer input1(kNumMonoChannels, kFramesPerBuffer);
+ input1.set_source_id(kId1);
+ FillAudioBuffer(std::begin(kInput1), kFramesPerBuffer, kNumMonoChannels,
+ &input1);
+ AudioBuffer input2(kNumMonoChannels, kFramesPerBuffer);
+ input2.set_source_id(kId2);
+ FillAudioBuffer(std::begin(kInput2), kFramesPerBuffer, kNumMonoChannels,
+ &input2);
+ // Initialize gain vectors.
+ const std::vector<float> kGainVector1(1, kGain1);
+ const std::vector<float> kGainVector2(1, kGain2);
+
+ // Add the input buffers to the |GainMixer| (process multiple times so the
+ // gains have reached steady state.
+ const AudioBuffer* output = nullptr;
+ for (size_t iterations = 0; iterations < kUnitRampLength; ++iterations) {
+ gain_mixer.Reset();
+ gain_mixer.AddInput(input1, kGainVector1);
+ gain_mixer.AddInput(input2, kGainVector2);
+ // Get the processed output (should contain the pre-computed output data).
+ output = gain_mixer.GetOutput();
+ }
+
+ EXPECT_FALSE(output == nullptr);
+ EXPECT_EQ(output->num_channels(), kNumMonoChannels);
+ EXPECT_EQ(output->num_frames(), kFramesPerBuffer);
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ EXPECT_NEAR((*output)[0][i], kOutput[i], kEpsilonFloat);
+ }
+}
+
+// Reset test for the |GainMixer| with single input at a time. First, adds an
+// input buffer, resets the |GainMixer|. Then, adds another input buffer and
+// tests the final output buffer against the data of the last added input buffer
+// to verify if the system has been successfully reset.
+TEST(GainMixerTest, ResetTest) {
+ GainMixer gain_mixer(kNumMonoChannels, kFramesPerBuffer);
+
+ // Initialize input buffers.
+ AudioBuffer input1(kNumMonoChannels, kFramesPerBuffer);
+ input1.set_source_id(kId1);
+ FillAudioBuffer(std::begin(kInput1), kFramesPerBuffer, kNumMonoChannels,
+ &input1);
+ AudioBuffer input2(kNumMonoChannels, kFramesPerBuffer);
+ input2.set_source_id(kId2);
+ FillAudioBuffer(std::begin(kInput2), kFramesPerBuffer, kNumMonoChannels,
+ &input2);
+ // Initialize gain vectors.
+ const std::vector<float> kGainVector1(1, kGain1);
+ const std::vector<float> kGainVector2(1, kGain2);
+
+ // Add the first input buffer to the |GainMixer|.
+ gain_mixer.AddInput(input1, kGainVector1);
+ // Reset the accumulator (and release the buffer).
+ gain_mixer.Reset();
+ // Add the second input buffers to the |GainMixer|.
+ gain_mixer.AddInput(input2, kGainVector2);
+
+ // Get the output (should only contain the second input).
+ const AudioBuffer* output = gain_mixer.GetOutput();
+
+ EXPECT_FALSE(output == nullptr);
+ EXPECT_EQ(output->num_channels(), kNumMonoChannels);
+ EXPECT_EQ(output->num_frames(), kFramesPerBuffer);
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ EXPECT_NEAR((*output)[0][i], kInput2[i] * kGain2, kEpsilonFloat);
+ }
+}
+
+// Tests that gains are correctly applied for multiple channels.
+TEST(GainMixerTest, MultiChannelTest) {
+ const std::vector<float> kGains = {0.1f, -0.2f};
+ const std::vector<float> kInputChannel = {10.0f, 10.0f, 10.0f};
+ GainMixer gain_mixer(kGains.size(), kFramesPerBuffer);
+
+ // Initialize input buffer.
+ AudioBuffer input(kGains.size(), kInputChannel.size());
+ for (size_t i = 0; i < kGains.size(); ++i) {
+ input[i] = kInputChannel;
+ }
+
+ const AudioBuffer* output = nullptr;
+ for (size_t iterations = 0; iterations < kUnitRampLength; ++iterations) {
+ gain_mixer.Reset();
+ gain_mixer.AddInput(input, kGains);
+ output = gain_mixer.GetOutput();
+ }
+
+ EXPECT_FALSE(output == nullptr);
+ EXPECT_EQ(output->num_channels(), kGains.size());
+ EXPECT_EQ(output->num_frames(), kFramesPerBuffer);
+ for (size_t channel = 0; channel < kGains.size(); ++channel) {
+ for (size_t i = 0; i < kInputChannel.size(); ++i) {
+ EXPECT_NEAR((*output)[channel][i], kInputChannel[i] * kGains[channel],
+ kEpsilonFloat);
+ }
+ }
+}
+
+// Tests that gains are correctly applied for a mono input buffer across a
+// multichannel output buffer.
+TEST(GainMixerTest, MonoChannelInputTest) {
+ const std::vector<float> kGains = {2.0f, -3.0f};
+ const std::vector<float> kInputChannel = {10.0f, 10.0f, 10.0f};
+ GainMixer gain_mixer(kGains.size(), kInputChannel.size());
+
+ // Create a mono input buffer.
+ AudioBuffer input(kNumMonoChannels, kInputChannel.size());
+ AudioBuffer::Channel* input_channel = &input[0];
+ *input_channel = kInputChannel;
+
+ const AudioBuffer* output = nullptr;
+ for (size_t iterations = 0; iterations < kUnitRampLength; ++iterations) {
+ gain_mixer.Reset();
+ gain_mixer.AddInputChannel(*input_channel, kId1, kGains);
+ output = gain_mixer.GetOutput();
+ }
+
+ EXPECT_FALSE(output == nullptr);
+ EXPECT_EQ(output->num_channels(), kGains.size());
+ EXPECT_EQ(output->num_frames(), kFramesPerBuffer);
+ for (size_t channel = 0; channel < kGains.size(); ++channel) {
+ for (size_t i = 0; i < kInputChannel.size(); ++i) {
+ EXPECT_NEAR((*output)[channel][i], kInputChannel[i] * kGains[channel],
+ kEpsilonFloat);
+ }
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_processor.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_processor.cc
new file mode 100644
index 000000000..f5eaea22d
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_processor.cc
@@ -0,0 +1,94 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/gain_processor.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/constants_and_types.h"
+
+#include "dsp/gain.h"
+
+namespace vraudio {
+
+// Delegate default constructor.
+GainProcessor::GainProcessor() : current_gain_(0.0f), is_initialized_(false) {}
+
+GainProcessor::GainProcessor(float initial_gain)
+ : current_gain_(initial_gain), is_initialized_(true) {}
+
+void GainProcessor::ApplyGain(float target_gain,
+ const AudioBuffer::Channel& input,
+ AudioBuffer::Channel* output,
+ bool accumulate_output) {
+
+ DCHECK(output);
+
+ if (!is_initialized_) {
+ Reset(target_gain);
+ }
+
+ // Check buffer length.
+ const size_t input_length = input.size();
+ DCHECK_GT(input_length, 0U);
+ DCHECK_EQ(input_length, output->size());
+
+ // Index for where to stop interpolating.
+ size_t ramp_length =
+ static_cast<size_t>(std::abs(target_gain - current_gain_) *
+ static_cast<float>(kUnitRampLength));
+
+ // Check if there is a new gain value.
+ if (ramp_length > 0) {
+ // Apply gain ramp to buffer.
+ current_gain_ = LinearGainRamp(ramp_length, current_gain_, target_gain,
+ input, output, accumulate_output);
+ } else {
+ // No ramping needed.
+ current_gain_ = target_gain;
+ }
+
+ // Apply constant gain to the rest of the buffer.
+ if (ramp_length < input_length) {
+ if (IsGainNearZero(current_gain_)) {
+ // Skip processing if the gain is zero.
+ if (!accumulate_output) {
+ // Directly fill the remaining output with zeros.
+ std::fill(output->begin() + ramp_length, output->end(), 0.0f);
+ }
+ return;
+ } else if (IsGainNearUnity(current_gain_) && !accumulate_output) {
+ // Skip processing if the gain is unity.
+ if (&input != output) {
+ // Directly copy the remaining input samples into output.
+ std::copy(input.begin() + ramp_length, input.end(),
+ output->begin() + ramp_length);
+ }
+ return;
+ }
+ ConstantGain(ramp_length, current_gain_, input, output, accumulate_output);
+ }
+}
+
+float GainProcessor::GetGain() const { return current_gain_; }
+
+void GainProcessor::Reset(float gain) {
+ current_gain_ = gain;
+ is_initialized_ = true;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_processor.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_processor.h
new file mode 100644
index 000000000..4baa276ea
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_processor.h
@@ -0,0 +1,74 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_GAIN_PROCESSOR_H_
+#define RESONANCE_AUDIO_DSP_GAIN_PROCESSOR_H_
+
+#include <cmath>
+#include <cstddef>
+#include <vector>
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Processor class which applies a gain to a vector of samples from an audio
+// buffer. A short linear ramp is applied to the gain to reduce audible
+// artifacts in the output.
+class GainProcessor {
+ public:
+ // Default constructor keeps the gain state uninitialized. The first call to
+ // |ApplyGain| sets the internal gain state.
+ GainProcessor();
+
+ // Constructs |GainProcessor| with some initial gain value.
+ //
+ // @param initial_gain Gain value used as starting point for first processing
+ // period's gain ramping.
+ explicit GainProcessor(float initial_gain);
+
+ // Applies gain supplied to the input samples.
+ //
+ // @param target_gain Target gain value.
+ // @param input Samples to which gain will be applied.
+ // @param output Samples to which gain has been applied.
+ // @param accumulate_output True if the processed input should be mixed into
+ // the output. Otherwise, the output will be replaced by the processed
+ // input.
+ void ApplyGain(float target_gain, const AudioBuffer::Channel& input,
+ AudioBuffer::Channel* output, bool accumulate_output);
+
+ // Returns the |current_gain_| value.
+ //
+ // @return Current gain applied by the |GainProcessor|.
+ float GetGain() const;
+
+ // Resets the gain processor to a new gain factor.
+ //
+ // @param gain Gain value.
+ void Reset(float gain);
+
+ private:
+ // Latest gain value to be applied to buffer values.
+ float current_gain_;
+
+ // Flag to indiciate if an initial gain has been assigned.
+ bool is_initialized_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_GAIN_PROCESSOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_processor_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_processor_test.cc
new file mode 100644
index 000000000..e1077655a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_processor_test.cc
@@ -0,0 +1,181 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/gain_processor.h"
+
+#include <algorithm>
+#include <cmath>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+
+// A set of simple tests to confirm expected behavior when a linear gain ramp is
+// applied to a vector of samples.
+namespace vraudio {
+
+namespace {
+
+// Initial gain value.
+const float kInitialGain = 0.1f;
+
+// Target gain value.
+const float kTargetGain = 0.6f;
+
+// Test input data length.
+const size_t kInputLength = 10;
+
+// Test value of each sample of the output buffer prior to processing.
+const float kInitialOutputValue = 2.0f;
+
+class GainProcessorTest : public ::testing::TestWithParam<bool> {};
+
+// This test checks that gain value is applied to the input buffer in a linear
+// ramp when the buffer length is less than the ramp length.
+TEST_P(GainProcessorTest, ApplyGainOverBufferLengthTest) {
+ // Expected output data for ramped gain application.
+ const float kExpectedOutput[kInputLength] = {
+ 0.1f, 0.1004883f, 0.1009766f, 0.1014648f, 0.1019531f,
+ 0.1024414f, 0.1029297f, 0.1034180f, 0.1039063f, 0.1043945f};
+ const bool accumulate_output = GetParam();
+ // Initialize input buffer.
+ AudioBuffer input(kNumMonoChannels, kInputLength);
+ std::fill(input[0].begin(), input[0].end(), 1.0f);
+ // Initialize output buffer.
+ AudioBuffer output(kNumMonoChannels, kInputLength);
+ std::fill(output[0].begin(), output[0].end(), kInitialOutputValue);
+
+ // Initialize gain processor with a gain value.
+ GainProcessor gain_processor(kInitialGain);
+ // Process buffer samples with the test gain value.
+ gain_processor.ApplyGain(kTargetGain, input[0], &output[0],
+ accumulate_output);
+
+ // Check that gain values have been applied correctly to the buffer.
+ for (size_t i = 0; i < kInputLength; ++i) {
+ const float expected_value = accumulate_output
+ ? kInitialOutputValue + kExpectedOutput[i]
+ : kExpectedOutput[i];
+ EXPECT_NEAR(expected_value, output[0][i], kEpsilonFloat);
+ }
+}
+
+// This test checks that gain value is applied to the buffer samples in a linear
+// ramp. The test also confirms that the gain ramping halts at the correct index
+// for buffer lengths greater than the ramp length.
+TEST_P(GainProcessorTest, ApplyGainLongerThanRampTest) {
+ const bool accumulate_output = GetParam();
+ // Initialize input buffer.
+ AudioBuffer input(kNumMonoChannels, kUnitRampLength);
+ std::fill(input[0].begin(), input[0].end(), 1.0f);
+ // Initialize output buffer.
+ AudioBuffer output(kNumMonoChannels, kUnitRampLength);
+ std::fill(output[0].begin(), output[0].end(), kInitialOutputValue);
+
+ // Initialize gain processor with a gain value.
+ GainProcessor gain_processor(kInitialGain);
+ // Process an input buffer with the test gain value.
+ gain_processor.ApplyGain(kTargetGain, input[0], &output[0],
+ accumulate_output);
+
+ // Generate expected gain ramp. The output should consist of the linearly
+ // interpolated gain values over the ramp length, and the final (constant)
+ // gain for the rest of the buffer.
+ std::vector<float> expected_output(kUnitRampLength);
+ const size_t ramp_length =
+ static_cast<size_t>(std::abs(kTargetGain - kInitialGain) *
+ static_cast<float>(kUnitRampLength));
+ const float increment =
+ (kTargetGain - kInitialGain) / static_cast<float>(ramp_length);
+ float expected_value =
+ accumulate_output ? kInitialOutputValue + kInitialGain : kInitialGain;
+ for (size_t i = 0; i < kUnitRampLength; ++i) {
+ expected_output[i] = expected_value;
+ if (i < ramp_length) {
+ expected_value += increment;
+ }
+ }
+ // Check that gain values have been applied correctly to the buffer.
+ for (size_t i = 0; i < kUnitRampLength; ++i) {
+ // Check that ramp was applied.
+ EXPECT_NEAR(expected_output[i], output[0][i], kEpsilonFloat);
+ }
+}
+
+// This test checks that gain values are reset to initial gain after a call to
+// the |Reset()| method.
+TEST_P(GainProcessorTest, ResetGainProcessorTest) {
+ const bool accumulate_output = GetParam();
+ // Initialize input buffer.
+ AudioBuffer input(kNumMonoChannels, kUnitRampLength);
+ std::fill(input[0].begin(), input[0].end(), 1.0f);
+ // Initialize output buffer.
+ AudioBuffer output(kNumMonoChannels, kUnitRampLength);
+ std::fill(output[0].begin(), output[0].end(), 0.0f);
+
+ // Initialize gain processor with a gain of 0.0f.
+ GainProcessor gain_processor(0.0f);
+ // Reset gain to |kInitialGain|.
+ gain_processor.Reset(kInitialGain);
+ // Apply newly-reset gains (no ramp expected).
+ gain_processor.ApplyGain(kInitialGain, input[0], &output[0],
+ accumulate_output);
+
+ // Check that uniform gain has been applied correctly to the buffer.
+ for (size_t i = 0; i < kInputLength; ++i) {
+ EXPECT_NEAR(kInitialGain, output[0][i], kEpsilonFloat);
+ }
+}
+
+// Checks that the initial gain is assigned during the first call of |ApplyGain|
+// in case the |GainProcessor| instance is constructed via the default
+// constructor.
+TEST_P(GainProcessorTest, DefaultConstructorTest) {
+ const bool accumulate_output = GetParam();
+ // Initialize input buffer.
+ AudioBuffer input(kNumMonoChannels, kUnitRampLength);
+ std::fill(input[0].begin(), input[0].end(), kInitialGain);
+ // Initialize output buffer.
+ AudioBuffer output(kNumMonoChannels, kUnitRampLength);
+ std::fill(output[0].begin(), output[0].end(), 0.0f);
+
+ // Declare gain processor without specifiying a gain value.
+ GainProcessor gain_processor;
+ // Apply some new gain value.
+ gain_processor.ApplyGain(kTargetGain, input[0], &output[0],
+ accumulate_output);
+
+ // Check that uniform gain has been applied correctly to the buffer.
+ for (size_t i = 0; i < kInputLength; ++i) {
+ EXPECT_NEAR(kInitialGain * kTargetGain, output[0][i], kEpsilonFloat);
+ }
+}
+
+// Tests the |GetGain| method.
+TEST(GainProcessorTest, GetGainTest) {
+ // Test |GainProcessor| with arbitrary gain.
+ const float kGainValue = -1.5f;
+ GainProcessor gain_processor(kGainValue);
+ EXPECT_EQ(gain_processor.GetGain(), kGainValue);
+}
+
+INSTANTIATE_TEST_CASE_P(AccumulateOutput, GainProcessorTest,
+ ::testing::Values(false, true));
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_test.cc
new file mode 100644
index 000000000..01e4471f4
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/gain_test.cc
@@ -0,0 +1,132 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/gain.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "dsp/utils.h"
+
+namespace vraudio {
+
+namespace {
+
+// Tolerated error margin.
+const float kEpsilon = 1e-4f;
+
+class GainTest : public ::testing::TestWithParam<bool> {};
+
+// This test checks that the gain applied using ConstantGain is correct.
+TEST_P(GainTest, ConstantGainTest) {
+ const size_t kMaxInputLength = 32;
+ const float kGain = 1;
+ const bool accumulate_output = GetParam();
+
+ for (size_t length = 1; length < kMaxInputLength; ++length) {
+ AudioBuffer input_samples(kNumMonoChannels, length);
+ GenerateUniformNoise(
+ /*min=*/-1.0f, /*min=*/1.0f, static_cast<unsigned>(length),
+ &input_samples[0]);
+ for (size_t offset_index = 0; offset_index < length; ++offset_index) {
+ // Initialize input buffer.
+ AudioBuffer input(kNumMonoChannels, length);
+ input[0] = input_samples[0];
+ // Initialize output buffer with the same values.
+ AudioBuffer output(kNumMonoChannels, length);
+ output[0] = input_samples[0];
+
+ // Apply constant gain.
+ ConstantGain(offset_index, kGain, input[0], &output[0],
+ accumulate_output);
+
+ // Compute expected values.
+ AudioBuffer expected_samples_buffer(kNumMonoChannels, length);
+ expected_samples_buffer[0] = input_samples[0];
+ for (size_t i = offset_index; i < length; ++i) {
+ const float processed_input = kGain * input[0][i];
+ if (accumulate_output) {
+ expected_samples_buffer[0][i] += processed_input;
+ } else {
+ expected_samples_buffer[0][i] = processed_input;
+ }
+ }
+ // Check that the output buffer has the expected values per each sample.
+ for (size_t i = 0; i < length; ++i) {
+ EXPECT_NEAR(expected_samples_buffer[0][i], output[0][i], kEpsilon)
+ << " at index=" << i << " with input_length=" << length
+ << " and offset_index=" << offset_index;
+ }
+ }
+ }
+}
+
+// Test that checks that the gain ramp applied is correct.
+TEST_P(GainTest, LinearGainRampTest) {
+ const float kInitialOutputValue = 2.0f;
+ const float kStartGain = 0.0f;
+ const float kEndGain = 1.0f;
+ const size_t kNumSamples = 10;
+ const float kPerSampleIncrease =
+ (kEndGain - kStartGain) / static_cast<float>(kNumSamples);
+ const bool accumulate_output = GetParam();
+
+ // Create an input buffer with unity samples.
+ AudioBuffer input(kNumMonoChannels, kNumSamples);
+ std::fill(input[0].begin(), input[0].end(), 1.0f);
+ // Create an output buffer with all samples the same.
+ AudioBuffer output(kNumMonoChannels, kNumSamples);
+ std::fill(output[0].begin(), output[0].end(), kInitialOutputValue);
+
+ // Apply linear gain ramp.
+ LinearGainRamp(kNumSamples, kStartGain, kEndGain, input[0], &(output[0]),
+ accumulate_output);
+
+ // Check that the output buffer has the expected values per each sample.
+ float expected_value = accumulate_output ? kInitialOutputValue : 0.0f;
+ for (size_t i = 0; i < kNumSamples; ++i) {
+ EXPECT_NEAR(expected_value, output[0][i], kEpsilon);
+ expected_value += kPerSampleIncrease;
+ }
+}
+
+TEST(GainTest, GainUtilsTest) {
+ const float kGainUnity = 1.0f;
+ const float kGainZero = 0.0f;
+ const float kGainOther = -1.7f;
+
+ // Test the cases where each should return true.
+ EXPECT_TRUE(IsGainNearZero(kGainZero));
+ EXPECT_TRUE(IsGainNearUnity(kGainUnity));
+
+ // Test the cases where gain value is non zero, positive and negative.
+ EXPECT_FALSE(IsGainNearZero(kGainOther));
+ EXPECT_FALSE(IsGainNearZero(kGainUnity));
+
+ // Test the case where gain value is not unity, with alternate value and zero.
+ EXPECT_FALSE(IsGainNearUnity(kGainOther));
+ EXPECT_FALSE(IsGainNearUnity(kGainZero));
+}
+
+INSTANTIATE_TEST_CASE_P(AccumulateOutput, GainTest,
+ ::testing::Values(false, true));
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/mixer.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/mixer.cc
new file mode 100644
index 000000000..1b7460428
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/mixer.cc
@@ -0,0 +1,56 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/mixer.h"
+
+#include "base/logging.h"
+
+namespace vraudio {
+
+Mixer::Mixer(size_t target_num_channels, size_t frames_per_buffer)
+ : output_(target_num_channels, frames_per_buffer), is_empty_(false) {
+ Reset();
+}
+
+void Mixer::AddInput(const AudioBuffer& input) {
+ DCHECK_EQ(input.num_frames(), output_.num_frames());
+
+ // Accumulate the input buffers into the output buffer.
+ const size_t num_channels =
+ std::min(input.num_channels(), output_.num_channels());
+ for (size_t n = 0; n < num_channels; ++n) {
+ if (input[n].IsEnabled()) {
+ output_[n] += input[n];
+ }
+ }
+ is_empty_ = false;
+}
+
+const AudioBuffer* Mixer::GetOutput() const {
+ if (is_empty_) {
+ return nullptr;
+ }
+ return &output_;
+}
+
+void Mixer::Reset() {
+ if (!is_empty_) {
+ output_.Clear();
+ }
+ is_empty_ = true;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/mixer.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/mixer.h
new file mode 100644
index 000000000..b89144799
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/mixer.h
@@ -0,0 +1,62 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_MIXER_H_
+#define RESONANCE_AUDIO_DSP_MIXER_H_
+
+#include <memory>
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Accepts multiple input buffers and outputs a downmix to a single output
+// buffer. All input buffers must have the same number of frames per buffer, the
+// output will have the target number of channels regardless of the input number
+// of channels.
+class Mixer {
+ public:
+ // Constructor.
+ //
+ // @param target_num_channels Target number of channels in accumulator buffer.
+ // @param frames_per_buffer Number of frames in accumulator buffer.
+ Mixer(size_t target_num_channels, size_t frames_per_buffer);
+
+ // Adds an input buffer to the mixer, updates the output buffer accordingly.
+ //
+ // @param input Input buffer to be added.
+ void AddInput(const AudioBuffer& input);
+
+ // Returns a pointer to the accumulator.
+ //
+ // @return Pointer to the processed (mixed) output buffer, or nullptr if no
+ // input has been added to the accumulator.
+ const AudioBuffer* GetOutput() const;
+
+ // Resets the state of the accumulator.
+ void Reset();
+
+ private:
+ // Output buffer (accumulator).
+ AudioBuffer output_;
+
+ // Denotes whether the accumulator has processed any inputs or not.
+ bool is_empty_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_MIXER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/mixer_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/mixer_test.cc
new file mode 100644
index 000000000..0cfdf8675
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/mixer_test.cc
@@ -0,0 +1,163 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/mixer.h"
+
+#include <iterator>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "utils/planar_interleaved_conversion.h"
+
+namespace vraudio {
+
+namespace {
+
+// Number of frames per output buffer.
+const size_t kFramesPerBuffer = 3;
+
+// Sample two-channel interleaved input data.
+const float kStereoInput1[kNumStereoChannels * kFramesPerBuffer] = {
+ 0.5f, 0.0f, -0.25f, 0.0f, 0.25f, 0.0f};
+const float kStereoInput2[kNumStereoChannels * kFramesPerBuffer] = {
+ 0.5f, 1.0f, 0.5f, -1.0f, 0.5f, 1.0f};
+
+// Tests that the mixer returns null output if no input has been added.
+TEST(MixerTest, EmptyInputTest) {
+ Mixer mixer(kNumStereoChannels, kFramesPerBuffer);
+
+ const AudioBuffer* output = mixer.GetOutput();
+ EXPECT_TRUE(output == nullptr);
+}
+
+// This is a simple two-channel process test with two inputs. Tests the
+// mixed output buffer against the manually computed output data.
+TEST(MixerTest, ProcessUniformInputChannelsTest) {
+ Mixer mixer(kNumStereoChannels, kFramesPerBuffer);
+
+ // Initialize the input buffers.
+ AudioBuffer input1(kNumStereoChannels, kFramesPerBuffer);
+ FillAudioBuffer(std::begin(kStereoInput1), kFramesPerBuffer,
+ kNumStereoChannels, &input1);
+ AudioBuffer input2(kNumStereoChannels, kFramesPerBuffer);
+ FillAudioBuffer(std::begin(kStereoInput2), kFramesPerBuffer,
+ kNumStereoChannels, &input2);
+
+ // Add the input buffers to the mixer.
+ mixer.AddInput(input1);
+ mixer.AddInput(input2);
+ // Get the processed output.
+ const AudioBuffer* output = mixer.GetOutput();
+
+ // Test that the output channels was accumulated correctly.
+ EXPECT_FALSE(output == nullptr);
+ EXPECT_EQ(output->num_channels(), kNumStereoChannels);
+ EXPECT_EQ(output->num_frames(), kFramesPerBuffer);
+ for (size_t n = 0; n < kNumStereoChannels; ++n) {
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ EXPECT_NEAR((*output)[n][i], input1[n][i] + input2[n][i], kEpsilonFloat);
+ }
+ }
+}
+
+// This is a non-uniform process test with two inputs with arbitrary number of
+// channels. Tests the mixed output buffer with different target number of
+// channels against the manually computed output data.
+TEST(MixerTest, ProcessVaryingInputChannelsTest) {
+ // Initialize the input buffers.
+ AudioBuffer mono_input(kNumMonoChannels, kFramesPerBuffer);
+ FillAudioBuffer(std::begin(kStereoInput1), kFramesPerBuffer, kNumMonoChannels,
+ &mono_input);
+ AudioBuffer stereo_input(kNumStereoChannels, kFramesPerBuffer);
+ FillAudioBuffer(std::begin(kStereoInput2), kFramesPerBuffer,
+ kNumStereoChannels, &stereo_input);
+
+ // Initialize a mono mixer.
+ Mixer mono_mixer(kNumMonoChannels, kFramesPerBuffer);
+ // Add the input buffers to the mixer.
+ mono_mixer.AddInput(mono_input);
+ mono_mixer.AddInput(stereo_input);
+ // Get the processed output.
+ const AudioBuffer* mono_output = mono_mixer.GetOutput();
+ // Test that the mono output channel was accumulated correctly.
+ EXPECT_FALSE(mono_output == nullptr);
+ EXPECT_EQ(mono_output->num_channels(), kNumMonoChannels);
+ EXPECT_EQ(mono_output->num_frames(), kFramesPerBuffer);
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ EXPECT_NEAR((*mono_output)[0][i], mono_input[0][i] + stereo_input[0][i],
+ kEpsilonFloat);
+ }
+
+ // Initialize a stereo mixer.
+ Mixer stereo_mixer(kNumStereoChannels, kFramesPerBuffer);
+ // Add the input buffers to the mixer.
+ stereo_mixer.AddInput(mono_input);
+ stereo_mixer.AddInput(stereo_input);
+ // Get the processed output.
+ const AudioBuffer* stereo_output = stereo_mixer.GetOutput();
+ // Test that the stereo output channels were accumulated correctly.
+ EXPECT_FALSE(stereo_output == nullptr);
+ EXPECT_EQ(stereo_output->num_channels(), kNumStereoChannels);
+ EXPECT_EQ(stereo_output->num_frames(), kFramesPerBuffer);
+ for (size_t n = 0; n < kNumStereoChannels; ++n) {
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ // The second channel should only contain the samples from |stereo_input|.
+ EXPECT_NEAR(
+ (*stereo_output)[n][i],
+ (n == 0) ? mono_input[n][i] + stereo_input[n][i] : stereo_input[n][i],
+ kEpsilonFloat);
+ }
+ }
+}
+
+// This is a two-channel reset test for the mixer with single input at a time.
+// First, adds an input buffer, resets the mixer. Then, adds another input
+// buffer and tests the final output buffer against the data of the last added
+// input buffer to verify if the system has been successfully reset.
+TEST(MixerTest, ResetTest) {
+ Mixer mixer(kNumStereoChannels, kFramesPerBuffer);
+
+ // Initialize the input buffers.
+ AudioBuffer input1(kNumStereoChannels, kFramesPerBuffer);
+ FillAudioBuffer(std::begin(kStereoInput1), kFramesPerBuffer,
+ kNumStereoChannels, &input1);
+ AudioBuffer input2(kNumStereoChannels, kFramesPerBuffer);
+ FillAudioBuffer(std::begin(kStereoInput2), kFramesPerBuffer,
+ kNumStereoChannels, &input2);
+
+ // Add the first input buffer to the mixer.
+ mixer.AddInput(input1);
+ // Reset the accumulator.
+ mixer.Reset();
+ // Add the second input buffers to the mixer.
+ mixer.AddInput(input2);
+ // Get the processed output.
+ const AudioBuffer* output = mixer.GetOutput();
+ // Test that the output channels contains only the samples from |input2|.
+ EXPECT_FALSE(output == nullptr);
+ EXPECT_EQ(output->num_channels(), kNumStereoChannels);
+ EXPECT_EQ(output->num_frames(), kFramesPerBuffer);
+ for (size_t n = 0; n < kNumStereoChannels; ++n) {
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ EXPECT_NEAR((*output)[n][i], input2[n][i], kEpsilonFloat);
+ }
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/mono_pole_filter.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/mono_pole_filter.cc
new file mode 100644
index 000000000..18c4625d5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/mono_pole_filter.cc
@@ -0,0 +1,59 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/mono_pole_filter.h"
+
+#include <algorithm>
+
+#include "base/constants_and_types.h"
+
+
+namespace vraudio {
+
+MonoPoleFilter::MonoPoleFilter(float coefficient) : previous_output_(0.0f) {
+ SetCoefficient(coefficient);
+}
+
+void MonoPoleFilter::SetCoefficient(float coefficient) {
+ coefficient_ = std::max(std::min(coefficient, 1.0f), 0.0f);
+}
+
+bool MonoPoleFilter::Filter(const AudioBuffer::Channel& input,
+ AudioBuffer::Channel* output) {
+
+ DCHECK(output);
+ const size_t num_frames = input.size();
+ DCHECK_EQ(num_frames, output->size());
+
+ // Do not perform processing if the coefficient is zero to avoid wasteful
+ // "all pass" cases.
+ if (coefficient_ < kEpsilonFloat) {
+ previous_output_ = input[num_frames - 1];
+ return false;
+ }
+
+ // The difference equation implemented here is as follows:
+ // y[n] = a * (y[n-1] - x[n]) + x[n]
+ // where y[n] is the output and x[n] is the input vector.
+ for (size_t frame = 0; frame < num_frames; ++frame) {
+ (*output)[frame] =
+ coefficient_ * (previous_output_ - input[frame]) + input[frame];
+ previous_output_ = (*output)[frame];
+ }
+ return true;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/mono_pole_filter.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/mono_pole_filter.h
new file mode 100644
index 000000000..4ec3f75dd
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/mono_pole_filter.h
@@ -0,0 +1,57 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_MONO_POLE_FILTER_H_
+#define RESONANCE_AUDIO_DSP_MONO_POLE_FILTER_H_
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Class representing a mono pole filter. This class also performs filtering.
+class MonoPoleFilter {
+ public:
+ // Constructs a |MonoPoleFilter| given a single coefficient.
+ //
+ // @param coefficient A single coefficient between 0.0f and 1.0f.
+ explicit MonoPoleFilter(float coefficient);
+
+ // Filter method for use with AudioBuffer::Channel.
+ //
+ // @param input |AudioBuffer::Channel| of input to be processed.
+ // @param output Pointer to output |AudioBuffer::Channel|.
+ // @return Returns false if the filter has an allpass configuration. This
+ // helps to avoid copies whenever the output is expected to be identical
+ // to the input.
+ bool Filter(const AudioBuffer::Channel& input, AudioBuffer::Channel* output);
+
+ // Sets the filter's coefficent.
+ //
+ // @param coefficient A mono pole filter coefficient.
+ void SetCoefficient(float coefficient);
+
+ private:
+ // The previous frame computed by the filter.
+ float previous_output_;
+
+ // Represents and maintains the state of the filter in terms of its
+ // transfer function coefficient.
+ float coefficient_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_MONO_POLE_FILTER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/mono_pole_filter_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/mono_pole_filter_test.cc
new file mode 100644
index 000000000..c93b4fab7
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/mono_pole_filter_test.cc
@@ -0,0 +1,59 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/mono_pole_filter.h"
+
+#include <cmath>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+namespace {
+
+const float kCoefficient = 0.5f;
+const size_t kFramesPerBuffer = 10;
+
+// Tests that the filter correctly implements the difference equation.
+TEST(MonoPoleFilterTest, ImpulseResponseTest) {
+ MonoPoleFilter filter(kCoefficient);
+ AudioBuffer buffer(1U, kFramesPerBuffer);
+ buffer.Clear();
+ buffer[0][0] = 1.0f;
+ EXPECT_TRUE(filter.Filter(buffer[0], &buffer[0]));
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ // The impulse response of this filter (for coefficient a), should be:
+ // d[n] = (1 - a) * a^n
+ const float expected =
+ (1.0f - kCoefficient) * IntegerPow(kCoefficient, static_cast<int>(i));
+ EXPECT_NEAR(buffer[0][i], expected, kEpsilonFloat);
+ }
+}
+
+// Tests that no processing is performed when the filter is allpass.
+TEST(MonoPoleFilterTest, AllPassNoProcessingTest) {
+ MonoPoleFilter filter(0.0f);
+ AudioBuffer buffer(1U, kFramesPerBuffer);
+ buffer.Clear();
+ buffer[0][0] = 1.0f;
+ EXPECT_FALSE(filter.Filter(buffer[0], &buffer[0]));
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/multi_channel_iir.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/multi_channel_iir.cc
new file mode 100644
index 000000000..dc3bbcb8f
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/multi_channel_iir.cc
@@ -0,0 +1,152 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/multi_channel_iir.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/simd_macros.h"
+
+
+namespace vraudio {
+
+std::unique_ptr<MultiChannelIir> MultiChannelIir::Create(
+ size_t num_channels, size_t frames_per_buffer,
+ const std::vector<std::vector<float>>& numerators,
+ const std::vector<std::vector<float>>& denominators) {
+ CHECK_EQ(denominators.size(), numerators.size());
+ CHECK_EQ(denominators.size(), num_channels);
+ CHECK_EQ(num_channels % SIMD_LENGTH, 0U);
+ CHECK_GT(num_channels, 0U);
+ for (size_t channel = 0; channel < num_channels; ++channel) {
+ CHECK_GT(denominators[channel].size(), 0U);
+ CHECK_GT(std::abs(denominators[channel][0]),
+ std::numeric_limits<float>::epsilon());
+ CHECK_EQ(denominators[channel].size(), numerators[channel].size());
+ }
+
+ std::unique_ptr<MultiChannelIir> multi_channel_iir(new MultiChannelIir(
+ num_channels, frames_per_buffer, numerators[0].size()));
+ CHECK(multi_channel_iir);
+
+ for (size_t channel = 0; channel < num_channels; ++channel) {
+ size_t interleaved_index = channel;
+ for (size_t i = 0; i < numerators[channel].size(); ++i) {
+ // Make the denominator polynomials monic, (divide all coefficients by the
+ // the first denominator coefficients). Furthermore negate all
+ // coefficients beyond the first (see equation in Process method).
+ // Copy the coefficients into the |numerator_| and |denominator_| buffers.
+ multi_channel_iir->numerator_[0][interleaved_index] =
+ numerators[channel][i] / denominators[channel][0];
+ multi_channel_iir->denominator_[0][interleaved_index] =
+ denominators[channel][i] /
+ ((i > 0) ? -denominators[channel][0] : denominators[channel][0]);
+ interleaved_index += num_channels;
+ }
+ }
+
+ multi_channel_iir->delay_line_.Clear();
+ return multi_channel_iir;
+}
+
+void MultiChannelIir::Process(AudioBuffer::Channel* interleaved_buffer) {
+
+ DCHECK(interleaved_buffer);
+ DCHECK_EQ(interleaved_buffer->size(), num_channels_ * frames_per_buffer_);
+ const SimdVector* simd_numerator =
+ reinterpret_cast<SimdVector*>(&(numerator_[0][0]));
+ const SimdVector* simd_denominator =
+ reinterpret_cast<SimdVector*>(&(denominator_[0][0]));
+ SimdVector* simd_delay_line =
+ reinterpret_cast<SimdVector*>(&(delay_line_[0][0]));
+ SimdVector* simd_buffer =
+ reinterpret_cast<SimdVector*>(&((*interleaved_buffer)[0]));
+
+ const size_t num_channel_chunks = num_channels_ / SIMD_LENGTH;
+ const size_t num_buffer_chunks = interleaved_buffer->size() / SIMD_LENGTH;
+ const size_t delay_length_in_chunks = num_channel_chunks * num_coefficients_;
+
+ for (size_t current_frame = 0, individual_frame = 0;
+ current_frame < num_buffer_chunks;
+ current_frame += num_channel_chunks, individual_frame += num_channels_) {
+ DCHECK_LT(individual_frame, interleaved_buffer->size());
+ // Copy the current sample into the delay line at the very start.
+ // {x[n], w[n-1], w[n-2], . . .} where each x, w represents all channels.
+ std::copy_n(interleaved_buffer->begin() + individual_frame, num_channels_,
+ delay_line_[0].begin() + (delay_line_front_ * SIMD_LENGTH));
+ // Using A Direct Form II implementation difference equation:
+ // Source: Digital Signal Processing Principles Algorithms & Applications
+ // Fourth Edition. John G. Prolakis and Dimitris G. Manolakis - Chap 9
+ // w[n] = x[n] - (a1/a0)*w[n-1] - (a2/a0)*w[n-2] - . . .
+ // y(n) = (b0/a0)*w[n] + (b1/a0)*w[n-1] + (b2/a0)*w[n-2] + . . .
+ // where x[n] is input, w[n] is storage and y[n] is output.
+ // The division by a0 has been performed in the constructor along with the
+ // negation of the denominator coefficients beyond the first. Note also
+ // that each term here refers to a set of channels.
+ for (size_t channel_chunk = 0; channel_chunk < num_channel_chunks;
+ ++channel_chunk) {
+ // First zero out the relevant section of the buffer before accumulation.
+ // Zero constant used for loading zeros into a neon simd array, as the
+ // |vld1q_dup_f32| neon intrinsic requires an lvalue parameter.
+ const float kZerof = 0.0f;
+ simd_buffer[current_frame + channel_chunk] = SIMD_LOAD_ONE_FLOAT(kZerof);
+ for (size_t coeff_offset = num_channel_chunks;
+ coeff_offset < delay_length_in_chunks;
+ coeff_offset += num_channel_chunks) {
+ // Denominator part.
+ const size_t multiplication_index = channel_chunk + coeff_offset;
+ const size_t delay_multiplication_index =
+ (multiplication_index + delay_line_front_) % delay_length_in_chunks;
+ const size_t delay_write_index = channel_chunk + delay_line_front_;
+ simd_delay_line[delay_write_index] =
+ SIMD_MULTIPLY_ADD(simd_denominator[multiplication_index],
+ simd_delay_line[delay_multiplication_index],
+ simd_delay_line[delay_write_index]);
+ }
+ for (size_t coeff_offset = 0; coeff_offset < delay_length_in_chunks;
+ coeff_offset += num_channel_chunks) {
+ // Numerator part.
+ const size_t multiplication_index = channel_chunk + coeff_offset;
+ const size_t write_index = current_frame + channel_chunk;
+ const size_t delay_multiplication_index =
+ (multiplication_index + delay_line_front_) % delay_length_in_chunks;
+ simd_buffer[write_index] =
+ SIMD_MULTIPLY_ADD(simd_numerator[multiplication_index],
+ simd_delay_line[delay_multiplication_index],
+ simd_buffer[write_index]);
+ }
+ }
+ // Update the index to the wrapped around 'front' of the delay line.
+ delay_line_front_ = ((static_cast<int>(delay_line_front_) -
+ num_channel_chunks + delay_length_in_chunks) %
+ delay_length_in_chunks);
+ }
+}
+
+MultiChannelIir::MultiChannelIir(size_t num_channels, size_t frames_per_buffer,
+ size_t num_coefficients)
+ : num_channels_(num_channels),
+ frames_per_buffer_(frames_per_buffer),
+ num_coefficients_(num_coefficients),
+ delay_line_front_(0),
+ numerator_(kNumMonoChannels, num_coefficients_ * num_channels_),
+ denominator_(kNumMonoChannels, num_coefficients_ * num_channels_),
+ delay_line_(kNumMonoChannels, num_coefficients_ * num_channels_) {}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/multi_channel_iir.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/multi_channel_iir.h
new file mode 100644
index 000000000..fd7efe72e
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/multi_channel_iir.h
@@ -0,0 +1,88 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_MULTI_CHANNEL_IIR_H_
+#define RESONANCE_AUDIO_DSP_MULTI_CHANNEL_IIR_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Class that performs IIR filtering on interleaved data. This class can be used
+// to implement any order of IIR filter on multichannel data where the number of
+// channels is a multiple of the SIMD vector length.
+class MultiChannelIir {
+ public:
+ // Returns a |MultiChannelIir| given valid parameters.
+ //
+ // @param num_channels Number of channels in each input buffer. The number of
+ // channels must be divisible by |SIMD_LENGTH|.
+ // @param frames_per_buffer Number of frames in each input buffer.
+ // @param numerators Numerator coefficients, one set per channel.
+ // @param denominators Denominator coefficients, should be equal in length to
+ // the |numerator| vector, one set per channel.
+ // @return A |MultiChannelIir| instance.
+ static std::unique_ptr<MultiChannelIir> Create(
+ size_t num_channels, size_t frames_per_buffer,
+ const std::vector<std::vector<float>>& numerators,
+ const std::vector<std::vector<float>>& denominators);
+
+ // Processes an interleaved buffer of input data with the given IIR filter.
+ //
+ // @param interleaved_buffer A single channel of data containing input in
+ // interleaved format, this will contain output data in interleaved format
+ // on return.
+ void Process(AudioBuffer::Channel* interleaved_buffer);
+
+ private:
+ // Constructs a |MultiChannelIir|.
+ //
+ // @param num_channels Number of channels in each input buffer. The number of
+ // channels must be divisible by |SIMD_LENGTH|.
+ // @param frames_per_buffer Number of frames in each input buffer.
+ // @param num_coefficients Number of coefficients in the numerator, which
+ // equals the number in the denominator.
+ MultiChannelIir(size_t num_channels, size_t frames_per_buffer,
+ size_t num_coefficients);
+
+ // Number of channels in each input buffer.
+ const size_t num_channels_;
+
+ // Number of frames in each input buffer.
+ const size_t frames_per_buffer_;
+
+ // Number of coefficients in the numerator and denominator polynomials.
+ const size_t num_coefficients_;
+
+ // Current front of the delay line which is circularly indexed.
+ size_t delay_line_front_;
+
+ // Stores numerator coefficients in repeated fashion.
+ AudioBuffer numerator_;
+
+ // Stores denominator coefficients in repeated fashion.
+ AudioBuffer denominator_;
+
+ // Holds previous data computed from the numerator section.
+ AudioBuffer delay_line_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_MULTI_CHANNEL_IIR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/multi_channel_iir_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/multi_channel_iir_test.cc
new file mode 100644
index 000000000..3c291196e
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/multi_channel_iir_test.cc
@@ -0,0 +1,117 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/multi_channel_iir.h"
+
+#include <memory>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+namespace {
+
+const size_t kFramesPerBuffer = 4;
+
+// Fills a channel buffer with each of the values passed |num_channels| times.
+void FillChannels(const std::vector<float>& values, size_t num_channels,
+ AudioBuffer::Channel* buffer) {
+ DCHECK_EQ(values.size() * num_channels, buffer->size());
+ size_t offset = 0;
+ for (auto value : values) {
+ std::fill_n(buffer->begin() + offset, num_channels, value);
+ offset += num_channels;
+ }
+}
+
+// Compares the contents of the |input| to the corresponding section of the
+// |expected| vector.
+void CompareMultiChannelOutput(const std::vector<std::vector<float>>& expected,
+ size_t offset, size_t num_channels,
+ const AudioBuffer::Channel& input) {
+ DCHECK_GE(expected[0].size(), kFramesPerBuffer + offset);
+ for (size_t channel = 0; channel < num_channels; ++channel) {
+ for (size_t frame = 0; frame < kFramesPerBuffer; ++frame) {
+ EXPECT_NEAR(expected[channel][frame + offset],
+ input[frame * num_channels + channel], kEpsilonFloat);
+ }
+ }
+}
+
+// Tests that the |MultiChannelIir| can filter a single channel of input
+// simultaneously with four different biquad filters.
+TEST(MultiChannelIirTest, MultipleFilterSetProcessTest) {
+ const std::vector<std::vector<float>> numerators({{1.0f, 3.0f, 5.0f},
+ {2.0f, 2.0f, 4.0f},
+ {1.0f, 2.0f, 2.0f},
+ {2.0f, 4.0f, 2.0f}});
+ const std::vector<std::vector<float>> denominators({{1.0f, 1.0f, 0.0f},
+ {2.0f, 2.0f, 0.0f},
+ {1.0f, 1.0f, 0.0f},
+ {2.0f, 2.0f, 0.0f}});
+
+ const std::vector<float> initial_input({1.0f, 4.0f, 6.0f, 0.0f});
+ const std::vector<float> zero_input({0.0f, 0.0f, 0.0f, 0.0f});
+ // These values have been determined through the MATLAB commands:
+ // filter([1 3 5], [1 1 0], [1 4 6 0, 0 0 0 0, 0 0 0 0])
+ // filter([2 2 4], [2 2 0], [1 4 6 0, 0 0 0 0, 0 0 0 0])
+ // filter([1 2 2], [1 1 0], [1 4 6 0, 0 0 0 0, 0 0 0 0])
+ // filter([2 4 2], [2 2 0], [1 4 6 0, 0 0 0 0, 0 0 0 0])
+ const std::vector<std::vector<float>> kExpectedOutputs = {
+ {1.0f, 6.0f, 17.0f, 21.0f, 9.0f, -9.0f, 9.0f, -9.0f, 9.0f, -9.0f, 9.0f,
+ -9.0f},
+ {1.0f, 4.0f, 8.0f, 6.0f, 6.0f, -6.0f, 6.0f, -6.0f, 6.0f, -6.0f, 6.0f,
+ -6.0f},
+ {1.0f, 5.0f, 11.0f, 9.0f, 3.0f, -3.0f, 3.0f, -3.0f, 3.0f, -3.0f, 3.0f,
+ -3.0f},
+ {1.0f, 5.0f, 10.0f, 6.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f}};
+
+ const size_t num_channels = numerators.size();
+
+ AudioBuffer interleaved_buffer(kNumMonoChannels,
+ num_channels * kFramesPerBuffer);
+ std::unique_ptr<MultiChannelIir> filter = MultiChannelIir::Create(
+ num_channels, kFramesPerBuffer, numerators, denominators);
+
+ // Filter the initial buffer [1 4 6 0], and compare the output to the first
+ // kFramesPerBuffer entries in |kExpectedOutputs|.
+ FillChannels(initial_input, num_channels, &interleaved_buffer[0]);
+ filter->Process(&(interleaved_buffer[0]));
+ CompareMultiChannelOutput(kExpectedOutputs, /*offset*/ 0, num_channels,
+ interleaved_buffer[0]);
+
+ // Filter zeros [0 0 0 0], and compare the output to the next kFramesPerBuffer
+ // entries in |kExpectedOutputs|.
+ FillChannels(zero_input, num_channels, &interleaved_buffer[0]);
+ filter->Process(&(interleaved_buffer[0]));
+ CompareMultiChannelOutput(kExpectedOutputs, kFramesPerBuffer, num_channels,
+ interleaved_buffer[0]);
+
+ // Filter zeros [0 0 0 0], and compare the output to the next kFramesPerBuffer
+ // entries in |kExpectedOutputs|.
+ FillChannels(zero_input, num_channels, &interleaved_buffer[0]);
+ filter->Process(&(interleaved_buffer[0]));
+ CompareMultiChannelOutput(kExpectedOutputs, 2 * kFramesPerBuffer,
+ num_channels, interleaved_buffer[0]);
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/near_field_processor.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/near_field_processor.cc
new file mode 100644
index 000000000..90afcc472
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/near_field_processor.cc
@@ -0,0 +1,97 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/near_field_processor.h"
+
+#include "base/constants_and_types.h"
+#include "dsp/filter_coefficient_generators.h"
+#include "dsp/gain.h"
+
+namespace vraudio {
+
+namespace {
+
+// Cross-over frequency of the band-splitting filter.
+const float kCrossOverFrequencyHz = 1000.0f;
+
+// +6dB bass boost factor converted to linear scale.
+const float kBassBoost = 2.0f;
+
+// Average group delay of the HRTF filters in seconds. Please see
+// [internal ref]
+const float kMeanHrtfGroupDelaySeconds = 0.00066667f;
+
+// Average group delay of the shelf-filter in samples.
+const size_t kMeanShelfFilterGroupDelaySamples = 1;
+
+} // namespace
+
+NearFieldProcessor::NearFieldProcessor(int sample_rate,
+ size_t frames_per_buffer)
+ : frames_per_buffer_(frames_per_buffer),
+ delay_compensation_(static_cast<size_t>(kMeanHrtfGroupDelaySeconds *
+ static_cast<float>(sample_rate)) -
+ kMeanShelfFilterGroupDelaySamples),
+ lo_pass_filter_(BiquadCoefficients(), frames_per_buffer_),
+ hi_pass_filter_(BiquadCoefficients(), frames_per_buffer_),
+ low_passed_buffer_(kNumMonoChannels, frames_per_buffer_),
+ delay_filter_(delay_compensation_, frames_per_buffer_) {
+ DCHECK_GT(sample_rate, 0);
+ DCHECK_GT(frames_per_buffer, 0);
+ DCHECK_LT(kCrossOverFrequencyHz, 0.5f * static_cast<float>(sample_rate));
+
+ // Generate biquad coefficients and construct low- and high-pass filter
+ // states.
+ BiquadCoefficients lo_pass_coefficients;
+ BiquadCoefficients hi_pass_coefficients;
+ ComputeDualBandBiquadCoefficients(sample_rate, kCrossOverFrequencyHz,
+ &lo_pass_coefficients,
+ &hi_pass_coefficients);
+
+ // Create two biquad filters initialized with the above filter coefficients.
+ lo_pass_filter_.SetCoefficients(lo_pass_coefficients);
+ hi_pass_filter_.SetCoefficients(hi_pass_coefficients);
+}
+
+void NearFieldProcessor::Process(const AudioBuffer::Channel& input,
+ AudioBuffer::Channel* output,
+ bool enable_hrtf) {
+
+ DCHECK(output);
+ DCHECK_EQ(input.size(), frames_per_buffer_);
+ DCHECK_EQ(output->size(), frames_per_buffer_);
+
+ // Low-pass filter the input and put it in the temporary low-passed buffer.
+ auto* low_passed_channel = &low_passed_buffer_[0];
+ lo_pass_filter_.Filter(input, low_passed_channel);
+
+ // High-pass filter the input and put it in the output channel (unmodified).
+ hi_pass_filter_.Filter(input, output);
+ // Iterate through all the samples in the |low_passed_buffer_| and apply
+ // the bass boost. Then, combine with the high-passed part in order to form
+ // the shelf-filtered output. Note: phase flip of the low-passed signal is
+ // required to form the correct filtered output.
+ ConstantGain(/*offset_index=*/0, -kBassBoost, *low_passed_channel, output,
+ /*accumulate_output=*/true);
+
+ if (enable_hrtf) {
+ // Delay the output to compensate for the average HRTF group delay.
+ delay_filter_.InsertData(*output);
+ delay_filter_.GetDelayedData(delay_compensation_, output);
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/near_field_processor.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/near_field_processor.h
new file mode 100644
index 000000000..86a3093d5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/near_field_processor.h
@@ -0,0 +1,75 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_NEAR_FIELD_PROCESSOR_H_
+#define RESONANCE_AUDIO_DSP_NEAR_FIELD_PROCESSOR_H_
+
+#include "base/audio_buffer.h"
+#include "dsp/biquad_filter.h"
+#include "dsp/delay_filter.h"
+
+namespace vraudio {
+
+// Class which applies an approximate near field effect to a source mono input.
+// The effect consists of a +6dB bass boost (shelf-filter) as well as phase
+// correction (HRTF group delay compensation) when the signal is to be combined
+// with the binaural output.
+//
+// For more information: [internal ref]
+class NearFieldProcessor {
+ public:
+ // Constructor of the |NearFieldProcessor|, uses the following parameters:
+ //
+ // @param sample_rate Sampling rate in [Hz].
+ // @param frames_per_buffer Number of frames per buffer in the input/output
+ // signal.
+ NearFieldProcessor(int sample_rate, size_t frames_per_buffer);
+
+ // Returns currently used delay compensation in samples.
+ size_t GetDelayCompensation() const { return delay_compensation_; }
+
+ // Applies approximate near field effect to the source mono input signal.
+ //
+ // @param input Mono input channel.
+ // @param output Pointer to mono output channel.
+ // @param enable_hrtf Whether to enable delay compensation for HRTF filtering.
+ void Process(const AudioBuffer::Channel& input, AudioBuffer::Channel* output,
+ bool enable_hrtf);
+
+ private:
+ // Number of frames per buffer.
+ const size_t frames_per_buffer_;
+
+ // Delay compensation computed as average group delay of the HRTF filter
+ // minus average group delay of the shelf-filter. Should be disabled when
+ // using with stereo-panned sound sources.
+ const size_t delay_compensation_;
+
+ // Biquad filters that apply frequency splitting of the input mono signal.
+ BiquadFilter lo_pass_filter_;
+ BiquadFilter hi_pass_filter_;
+
+ // Buffer for the low-passed signal. We do not modify the high-passed signal
+ // so we can write it directly to the output channel.
+ AudioBuffer low_passed_buffer_;
+
+ // Delay filter used to delay the incoming input mono buffer.
+ DelayFilter delay_filter_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_NEAR_FIELD_PROCESSOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/occlusion_calculator.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/occlusion_calculator.cc
new file mode 100644
index 000000000..9c8d92c1b
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/occlusion_calculator.cc
@@ -0,0 +1,52 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/occlusion_calculator.h"
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+float CalculateDirectivity(float alpha, float order,
+ const SphericalAngle& spherical_angle) {
+ // Clamp alpha weighting.
+ const float alpha_clamped = std::min(std::max(alpha, 0.0f), 1.0f);
+
+ // Check for zero-valued alpha (omnidirectional).
+ if (alpha_clamped < std::numeric_limits<float>::epsilon()) {
+ return 1.0f;
+ } else {
+ const float gain = (1.0f - alpha_clamped) +
+ alpha_clamped * (std::cos(spherical_angle.azimuth()) *
+ std::cos(spherical_angle.elevation()));
+
+ return std::pow(std::abs(gain), std::max(order, 1.0f));
+ }
+}
+
+float CalculateOcclusionFilterCoefficient(float directivity,
+ float occlusion_intensity) {
+ DCHECK_GE(occlusion_intensity, 0.0f);
+
+ const float occlusion_factor =
+ 1.0f / IntegerPow(occlusion_intensity + 1.0f, 4);
+ return std::max(0.0f, 1.0f - directivity * occlusion_factor);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/occlusion_calculator.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/occlusion_calculator.h
new file mode 100644
index 000000000..924da9068
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/occlusion_calculator.h
@@ -0,0 +1,54 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_OCCLUSION_CALCULATOR_H_
+#define RESONANCE_AUDIO_DSP_OCCLUSION_CALCULATOR_H_
+
+#include "base/spherical_angle.h"
+
+namespace vraudio {
+
+// Calculates directivity gain value for supplied source and listener
+// parameters.
+//
+// @param alpha Balance between dipole pattern and omnidirectional pattern for
+// source emission. By varying this value, differing directivity patterns
+// can be formed. Value in range [0, 1]. 2D visualization for several
+// values: http://goo.gl/GhKvoc.
+// @param order Order of directivity function. Higher values will result in
+// increased directivity. Value in range [1, +inf]. 2D visualization for
+// several orders:
+// http://goo.gl/sNrm1a.
+// @param spherical_angle Spherical angle of the listener relative to the
+// audio source which is being shaped.
+// @return Gain value in range [0, 1].
+float CalculateDirectivity(float alpha, float order,
+ const SphericalAngle& spherical_angle);
+
+// This function calculates a |MonoPoleFilter| coefficient based upon the
+// directivity and occlusion values. The coefficient calculation was designed
+// via empirical methods.
+//
+// @param directivity Gain value calculated based upon the directivity.
+// @param occlusion_intensity Gain value calculated based upon the degree of
+// occlusion.
+// @return Filter coefficient for a mono pole low pass filter.
+float CalculateOcclusionFilterCoefficient(float directivity,
+ float occlusion_intensity);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_OCCLUSION_CALCULATOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/occlusion_calculator_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/occlusion_calculator_test.cc
new file mode 100644
index 000000000..4de2d07ad
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/occlusion_calculator_test.cc
@@ -0,0 +1,139 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/occlusion_calculator.h"
+
+#include <algorithm>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+namespace {
+
+// Spherical coordinates of listener relative to source.
+const float kListenerAheadRads[2] = {0.0f, 0.0f};
+const float kListenerBesideRads[2] = {0.0f, static_cast<float>(M_PI_2)};
+const float kListenerAboveAndAheadRads[2] = {static_cast<float>(M_PI_4), 0.0f};
+
+// Test case data structure.
+struct DirectivityTestParams {
+ // Directivity function's alpha weighting.
+ const float alpha;
+ // Directivity function's order.
+ const float order;
+ // Spherical angle of the listener relative to the source.
+ const float* relative_spherical_angle;
+ // Expected value produced by the directivity function for the given input
+ // parameters.
+ const float expected_directivity;
+};
+
+// This test runs through a series of parametrized test cases and checks that
+// the directivity value calculated according to those parameters is correct.
+class DirectivityCalculatorParametrizedTest
+ : public testing::Test,
+ public testing::WithParamInterface<DirectivityTestParams> {};
+
+// Parametrized test which verifies that the directivity value calculated from
+// the test parameters matches the expected value supplied.
+TEST_P(DirectivityCalculatorParametrizedTest, CalculateDirectivityTest) {
+ // Test parameters.
+ const DirectivityTestParams test_params = GetParam();
+
+ // Construct test angle.
+ const float elevation_rad = test_params.relative_spherical_angle[0];
+ const float azimuth_rad = test_params.relative_spherical_angle[1];
+ SphericalAngle test_angle(elevation_rad, azimuth_rad);
+
+ // Calculate directivity.
+ const float directivity =
+ CalculateDirectivity(test_params.alpha, test_params.order, test_angle);
+
+ // Check calculated directivity.
+ EXPECT_NEAR(test_params.expected_directivity, directivity, kEpsilonFloat);
+}
+
+// Test parameters, according to struct |DirectivityParams|.
+DirectivityTestParams test_cases[] = {
+ // Omnidirectional.
+ {0.0f, 1.0f, &(kListenerAheadRads[0]), 1.0f},
+ {0.0f, 1.0f, &(kListenerBesideRads[0]), 1.0f},
+ {0.0f, 1.0f, &(kListenerAboveAndAheadRads[0]), 1.0f},
+ {0.0f, 2.0f, &(kListenerAboveAndAheadRads[0]), 1.0f},
+ {0.0f, 0.5f, &(kListenerAboveAndAheadRads[0]), 1.0f},
+ // Hypocardioid.
+ {0.25f, 1.0f, &(kListenerAheadRads[0]), 1.0f},
+ {0.25f, 1.0f, &(kListenerBesideRads[0]), 0.75f},
+ {0.25f, 1.0f, &(kListenerAboveAndAheadRads[0]), 0.926777f},
+ {0.25f, 2.0f, &(kListenerAboveAndAheadRads[0]), 0.858915f},
+ {0.25f, 0.5f, &(kListenerAboveAndAheadRads[0]), 0.926777f},
+ // Cardioid.
+ {0.5f, 1.0f, &(kListenerAheadRads[0]), 1.0f},
+ {0.5f, 1.0f, &(kListenerBesideRads[0]), 0.5f},
+ {0.5f, 1.0f, &(kListenerAboveAndAheadRads[0]), 0.853553f},
+ {0.5f, 2.0f, &(kListenerAboveAndAheadRads[0]), 0.728553f},
+ // Hypercardioid.
+ {0.75f, 1.0f, &(kListenerAheadRads[0]), 1.0f},
+ {0.75f, 1.0f, &(kListenerBesideRads[0]), 0.25f},
+ {0.75f, 1.0f, &(kListenerAboveAndAheadRads[0]), 0.780330f},
+ {0.75f, 2.0f, &(kListenerAboveAndAheadRads[0]), 0.608915f},
+ {0.75f, 0.5f, &(kListenerAboveAndAheadRads[0]), 0.780330f},
+ // Dipole.
+ {1.0f, 1.0f, &(kListenerAheadRads[0]), 1.0f},
+ {1.0f, 1.0f, &(kListenerBesideRads[0]), 0.0f},
+ {1.0f, 1.0f, &(kListenerAboveAndAheadRads[0]), 0.707107f},
+ {1.0f, 2.0f, &(kListenerAboveAndAheadRads[0]), 0.5f},
+ {1.0f, 0.5f, &(kListenerAboveAndAheadRads[0]), 0.707107f}};
+
+INSTANTIATE_TEST_CASE_P(Instance, DirectivityCalculatorParametrizedTest,
+ testing::ValuesIn(test_cases));
+
+TEST(DirectivityTest, CalculateOcclusionFilterCoefficientTest) {
+ // When there is no occlusion (occlusion == 0) expect the filter coefficient
+ // to be 1 - directivity.
+ const std::vector<float> directivities = {0.0f, 0.3f, 0.5f, 0.7f, 0.9f, 1.5f};
+ for (size_t i = 0; i < directivities.size() - 1; ++i) {
+ const float coefficient =
+ CalculateOcclusionFilterCoefficient(directivities[i], 0.0f);
+ EXPECT_EQ(1.0f - directivities[i], coefficient);
+ }
+ // Ensure the minimum value returned is 0.
+ const float coefficient =
+ CalculateOcclusionFilterCoefficient(directivities.back(), 0.0f);
+ EXPECT_EQ(0.0f, coefficient);
+}
+
+TEST(OcclusionTest, CalculateOcclusionFilterCoefficientTest) {
+ // When there is no effect on directivity, expect the filter coefficient
+ // to be 1 / (x + 1)^4 where x is the occlusion intensity.
+ const std::vector<float> occlusions = {0.01f, 0.1f, 1.0f, 10.0f, 100.0f};
+ std::vector<float> expected_coefficients = occlusions;
+ // Calculate 1 / (x + 1)^4 where x is the occlusion intensity.
+ std::for_each(expected_coefficients.begin(), expected_coefficients.end(),
+ [](float& n) { n = 1.0f - 1.0f / IntegerPow(n + 1.0f, 4); });
+ for (size_t i = 0; i < expected_coefficients.size(); ++i) {
+ const float coefficient =
+ CalculateOcclusionFilterCoefficient(1.0f, occlusions[i]);
+ EXPECT_EQ(expected_coefficients[i], coefficient);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/partitioned_fft_filter.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/partitioned_fft_filter.cc
new file mode 100644
index 000000000..4a2f071f8
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/partitioned_fft_filter.cc
@@ -0,0 +1,267 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "dsp/partitioned_fft_filter.h"
+
+#include <algorithm>
+
+#include "pffft.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/misc_math.h"
+#include "base/simd_utils.h"
+
+#include "dsp/utils.h"
+
+namespace vraudio {
+
+PartitionedFftFilter::PartitionedFftFilter(size_t filter_size,
+ size_t frames_per_buffer,
+ FftManager* fft_manager)
+ : PartitionedFftFilter(filter_size, frames_per_buffer, filter_size,
+ fft_manager) {}
+
+PartitionedFftFilter::PartitionedFftFilter(size_t filter_size,
+ size_t frames_per_buffer,
+ size_t max_filter_size,
+ FftManager* fft_manager)
+ : fft_manager_(fft_manager),
+ fft_size_(fft_manager_->GetFftSize()),
+ chunk_size_(fft_size_ / 2),
+ frames_per_buffer_(frames_per_buffer),
+ max_filter_size_(
+ CeilToMultipleOfFramesPerBuffer(max_filter_size, frames_per_buffer_)),
+ max_num_partitions_(max_filter_size_ / frames_per_buffer_),
+ filter_size_(
+ CeilToMultipleOfFramesPerBuffer(filter_size, frames_per_buffer_)),
+ num_partitions_(filter_size_ / frames_per_buffer_),
+ kernel_freq_domain_buffer_(max_num_partitions_, fft_size_),
+ buffer_selector_(0),
+ curr_front_buffer_(0),
+ freq_domain_buffer_(max_num_partitions_, fft_size_),
+ filtered_time_domain_buffers_(kNumStereoChannels, fft_size_),
+ freq_domain_accumulator_(kNumMonoChannels, fft_size_),
+ temp_zeropad_buffer_(kNumMonoChannels, chunk_size_),
+ temp_kernel_chunk_buffer_(kNumMonoChannels, frames_per_buffer_) {
+ // Ensure that |frames_per_buffer_| is less than or equal to the final
+ // partition |chunk_size_|.
+ CHECK(fft_manager_);
+ CHECK_LE(frames_per_buffer_, chunk_size_);
+ CHECK_GE(filter_size_, filter_size);
+ CHECK_GE(max_filter_size_, max_filter_size);
+ // Ensure that |filter_size_| does not exceed |max_filter_size_|.
+ CHECK_LE(filter_size, max_filter_size_);
+ // Make sure all partitions have the same size.
+ CHECK_EQ(num_partitions_ * frames_per_buffer_, filter_size_);
+ CHECK_EQ(max_num_partitions_ * frames_per_buffer_, max_filter_size_);
+
+ Clear();
+}
+
+void PartitionedFftFilter::Clear() {
+ // Reset valid part of the filter |FreqDomainBuffer|s to zero.
+ for (size_t i = 0; i < num_partitions_; ++i) {
+ kernel_freq_domain_buffer_[i].Clear();
+ freq_domain_buffer_[i].Clear();
+ }
+ // Reset filter state to zero.
+ filtered_time_domain_buffers_.Clear();
+}
+
+void PartitionedFftFilter::ResetFreqDomainBuffers(size_t new_filter_size) {
+
+ // Update the filter size.
+ DCHECK_GT(new_filter_size, 0U);
+ filter_size_ =
+ CeilToMultipleOfFramesPerBuffer(new_filter_size, frames_per_buffer_);
+ DCHECK_LE(filter_size_, max_filter_size_);
+
+ const size_t old_num_partitions = num_partitions_;
+ num_partitions_ = filter_size_ / frames_per_buffer_;
+ const size_t min_num_partitions =
+ std::min(old_num_partitions, num_partitions_);
+
+ if (curr_front_buffer_ > 0) {
+
+ FreqDomainBuffer temp_freq_domain_buffer(min_num_partitions, fft_size_);
+ // Copy in |min_num_partitions| to |temp_freq_domain_buffer|, starting with
+ // the partition at |curr_front_buffer_| to be moved back to the beginning
+ // of |freq_domain_buffer| .
+ for (size_t i = 0; i < min_num_partitions; ++i) {
+ temp_freq_domain_buffer[i] =
+ freq_domain_buffer_[(curr_front_buffer_ + i) % old_num_partitions];
+ }
+ // Replace the partitions.
+ for (size_t i = 0; i < min_num_partitions; ++i) {
+ freq_domain_buffer_[i] = temp_freq_domain_buffer[i];
+ }
+ curr_front_buffer_ = 0;
+ }
+ // Clear out the remaining partitions in case the filter size grew.
+ for (size_t i = old_num_partitions; i < num_partitions_; ++i) {
+ freq_domain_buffer_[i].Clear();
+ }
+}
+
+void PartitionedFftFilter::ReplacePartition(
+ size_t partition_index, const AudioBuffer::Channel& kernel_chunk) {
+ DCHECK_GE(partition_index, 0U);
+ DCHECK_LT(partition_index, num_partitions_);
+ DCHECK_EQ(kernel_chunk.size(), frames_per_buffer_);
+
+ fft_manager_->FreqFromTimeDomain(
+ kernel_chunk, &kernel_freq_domain_buffer_[partition_index]);
+}
+
+void PartitionedFftFilter::SetFilterLength(size_t new_filter_size) {
+ DCHECK_GT(new_filter_size, 0U);
+ new_filter_size =
+ CeilToMultipleOfFramesPerBuffer(new_filter_size, frames_per_buffer_);
+ DCHECK_LE(new_filter_size, max_filter_size_);
+
+ const size_t new_num_partitions = new_filter_size / frames_per_buffer_;
+ DCHECK_LE(new_num_partitions, max_num_partitions_);
+
+ // Clear out the remaining partitions in case the filter size grew.
+ for (size_t i = num_partitions_; i < new_num_partitions; ++i) {
+ kernel_freq_domain_buffer_[i].Clear();
+ }
+ // Call |ResetFreqDomainBuffers| to make sure that the input buffers are also
+ // correctly resized.
+ ResetFreqDomainBuffers(new_filter_size);
+}
+
+void PartitionedFftFilter::SetTimeDomainKernel(
+ const AudioBuffer::Channel& kernel) {
+
+
+ // Precomputes a set of floor(|filter_size_|/(|fft_size|/2)) frequency domain
+ // kernels, one for each partition of the |kernel|. This allows to reduce
+ // computational complexity if a fixed set of filter kernels is needed. The
+ // size of the |kernel| can be arbitrarily long but must not be less than
+ // |fft_size_|/2. Filter lengths which are multiples of |fft_size_|/2 are most
+ // efficient.
+
+ // Calculate the new number of partitions as filter length may have changed.
+ // This number is set to 1 if the filter length is smaller than
+ // |frames_per_buffer_|.
+ const size_t new_num_partitions =
+ CeilToMultipleOfFramesPerBuffer(kernel.size(), frames_per_buffer_) /
+ frames_per_buffer_;
+
+ auto& padded_channel = temp_kernel_chunk_buffer_[0];
+ // Break up time domain filter into chunks and FFT each of these separately.
+ for (size_t partition = 0; partition < new_num_partitions; ++partition) {
+ DCHECK_LE(partition * frames_per_buffer_, kernel.size());
+ const float* chunk_begin_itr =
+ kernel.begin() + partition * frames_per_buffer_;
+ const size_t num_frames_to_copy =
+ std::min<size_t>(frames_per_buffer_, kernel.end() - chunk_begin_itr);
+
+ std::copy_n(chunk_begin_itr, num_frames_to_copy, padded_channel.begin());
+ // This fill only occurs on the very last partition.
+ std::fill(padded_channel.begin() + num_frames_to_copy, padded_channel.end(),
+ 0.0f);
+ fft_manager_->FreqFromTimeDomain(padded_channel,
+ &kernel_freq_domain_buffer_[partition]);
+ }
+
+ if (new_num_partitions != num_partitions_) {
+ const size_t new_filter_size = new_num_partitions * frames_per_buffer_;
+ ResetFreqDomainBuffers(new_filter_size);
+ }
+}
+
+void PartitionedFftFilter::SetFreqDomainKernel(const FreqDomainBuffer& kernel) {
+ DCHECK_LE(kernel.num_channels(), max_num_partitions_);
+ DCHECK_EQ(kernel.num_frames(), fft_size_);
+
+ const size_t new_num_partitions = kernel.num_channels();
+ for (size_t i = 0; i < new_num_partitions; ++i) {
+ kernel_freq_domain_buffer_[i] = kernel[i];
+ }
+ if (new_num_partitions != num_partitions_) {
+ const size_t new_filter_size = new_num_partitions * frames_per_buffer_;
+ ResetFreqDomainBuffers(new_filter_size);
+ }
+}
+
+void PartitionedFftFilter::Filter(const FreqDomainBuffer::Channel& input) {
+
+
+ DCHECK_EQ(input.size(), fft_size_);
+ std::copy_n(input.begin(), fft_size_,
+ freq_domain_buffer_[curr_front_buffer_].begin());
+ buffer_selector_ = !buffer_selector_;
+ freq_domain_accumulator_.Clear();
+ auto* accumulator_channel = &freq_domain_accumulator_[0];
+
+ for (size_t i = 0; i < num_partitions_; ++i) {
+ // Complex vector product in frequency domain with filter kernel.
+ const size_t modulo_index = (curr_front_buffer_ + i) % num_partitions_;
+
+ // Perform inverse scaling along with accumulation of last fft buffer.
+ fft_manager_->FreqDomainConvolution(freq_domain_buffer_[modulo_index],
+ kernel_freq_domain_buffer_[i],
+ accumulator_channel);
+ }
+ // Our modulo based index.
+ curr_front_buffer_ =
+ (curr_front_buffer_ + num_partitions_ - 1) % num_partitions_;
+ // Perform inverse FFT transform of |freq_domain_buffer_| and store the
+ // result back in |filtered_time_domain_buffers_|.
+ fft_manager_->TimeFromFreqDomain(
+ *accumulator_channel, &filtered_time_domain_buffers_[buffer_selector_]);
+}
+
+void PartitionedFftFilter::GetFilteredSignal(AudioBuffer::Channel* output) {
+
+ DCHECK(output);
+ DCHECK_EQ(output->size(), frames_per_buffer_);
+
+ const size_t curr_buffer = buffer_selector_;
+ const size_t prev_buffer = !buffer_selector_;
+
+ // Overlap add.
+ if (frames_per_buffer_ == chunk_size_) {
+ AddPointwise(chunk_size_, &(filtered_time_domain_buffers_[curr_buffer][0]),
+ &filtered_time_domain_buffers_[prev_buffer][chunk_size_],
+ &((*output)[0]));
+ } else {
+ // If we have a non power of two |frames_per_buffer| we will have to perform
+ // the overlap add and then a copy, as buffer lengths are not a multiple of
+ // |chunk_size_|. NOTE: Indexing into |filtered_time_domain_buffers_| for a
+ // non power of two |frames_per_buffer_| means that the |input_b| parameter
+ // of |AddPointwiseOutOfPlace| may not be aligned in memory and thus we will
+ // not be able to use SIMD for the overlap add operation.
+ const auto& first_channel = filtered_time_domain_buffers_[curr_buffer];
+ const auto& second_channel = filtered_time_domain_buffers_[prev_buffer];
+ auto& output_channel = temp_zeropad_buffer_[0];
+ for (size_t i = 0; i < frames_per_buffer_; ++i) {
+ output_channel[i] =
+ first_channel[i] + second_channel[i + frames_per_buffer_];
+ }
+ std::copy_n(temp_zeropad_buffer_[0].begin(), frames_per_buffer_,
+ output->begin());
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/partitioned_fft_filter.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/partitioned_fft_filter.h
new file mode 100644
index 000000000..f8399922a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/partitioned_fft_filter.h
@@ -0,0 +1,167 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_PARTITIONED_FFT_FILTER_H_
+#define RESONANCE_AUDIO_DSP_PARTITIONED_FFT_FILTER_H_
+
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "base/misc_math.h"
+#include "dsp/fft_manager.h"
+
+struct PFFFT_Setup;
+
+namespace vraudio {
+
+// Class performing a FFT-based overlap and add FIR convolution.
+// Given an FFT size N and a filter size M > N/2; the filter is broken in to
+// floor(M/(N/2)) partitions in the time domain. This results in a set of
+// frequency domain "filters" of length (N/2)+1.
+class PartitionedFftFilter {
+ public:
+ // Typedef declares the data type for storing frequency domain buffers. Each
+ // channel stores the kernel for a partition.
+ typedef AudioBuffer FreqDomainBuffer;
+
+ // Constructor preallocates memory based on the |filter_size|. This can be
+ // used for simplicity if the filter size will be constant after creation.
+ //
+ // @param filter_size Length of the time domain filter in samples. This will
+ // be increased such that it becomes a multiple of |chunk_size_|.
+ // @param frames_per_buffer Number of points in each time domain input buffer.
+ // @param fft_manager Pointer to a manager to perform FFT transformations.
+ PartitionedFftFilter(size_t filter_size, size_t frames_per_buffer,
+ FftManager* fft_manager);
+
+ // Constructor preallocates memory based on the |max_filter_size|. The
+ // |fft_size_| will be twice |frames_per_buffer| if this is a power of two and
+ // twice the next larger power of two if it is not.
+ //
+ // @param filter_size Length of the time domain filter in samples. This will
+ // be increased such that it becomes a multiple of |chunk_size_|.
+ // @param frames_per_buffer Number of points in each time domain input buffer.
+ // @param max_filter_size Maximum length that |filter_size| can get.
+ // @param fft_manager Pointer to a manager for all fft related functionality.
+ PartitionedFftFilter(size_t filter_size, size_t frames_per_buffer,
+ size_t max_filter_size, FftManager* fft_manager);
+
+ // Initializes the FIR filter from a time domain kernel.
+ //
+ // @parem kernel Time domain filter to be used for processing.
+ void SetTimeDomainKernel(const AudioBuffer::Channel& kernel);
+
+ // Initializes the FIR filter from a precomputed frequency domain kernel.
+ //
+ // @param kernel Frequency domain filter to be used for processing.
+ void SetFreqDomainKernel(const FreqDomainBuffer& kernel);
+
+ // Replaces a partition indicated by the |partition_index| with
+ // |kernel_chunk|'s frequency domain equivalent.
+ //
+ // @param partition_index Location (partition) of the time domain filter we
+ // wish to replace.
+ // @param kernel_chunk |fft_size_|/2 length chunk of a filter used to
+ // replace the |partition_index|th partition.
+ void ReplacePartition(size_t partition_index,
+ const AudioBuffer::Channel& kernel_chunk);
+
+ // Alters the filter length by adding or removing partitions in the frequency
+ // domain. If |new_filter_size| is not a multiple of |chunk_size_| (i.e.
+ // frames per buffer), then the time domain filter kernel will be zeropadded
+ // to a multiple of |chunk_size_|.
+ //
+ // @param new_filter_size New length of the time domain filter kernel.
+ void SetFilterLength(size_t new_filter_size);
+
+ // Processes a block of frequency domain samples. The size of the input
+ // block must be |fft_size_|.
+ //
+ // @param Frequency domain input buffer.
+ void Filter(const FreqDomainBuffer::Channel& input);
+
+ // Returns block of filtered signal output of size |fft_size_|/2.
+ //
+ // @param output Time domain block filtered with the given kernel.
+ void GetFilteredSignal(AudioBuffer::Channel* output);
+
+ // Resets the filter state.
+ void Clear();
+
+ private:
+ friend class PartitionedFftFilterFrequencyBufferTest;
+
+ // Adjusts the |num_partitions_| and the size of |freq_domain_buffers_| for
+ // use with a new time domain filter kernel greater in length than the
+ // previous kernel.
+ //
+ // @param new_kernel_size Length of the time domain filter kernel.
+ void ResetFreqDomainBuffers(size_t new_kernel_size);
+
+ // Manager for all FFT related functionality (not owned).
+ FftManager* const fft_manager_;
+
+ // Number of points in the |fft_manager_|s FFT.
+ const size_t fft_size_;
+
+ // Size of each partition of the filter in time domain.
+ const size_t chunk_size_;
+
+ // Number of frames in each buffer of input data.
+ const size_t frames_per_buffer_;
+
+ // Maximum filter size in samples.
+ const size_t max_filter_size_;
+
+ // Maximum partition count.
+ const size_t max_num_partitions_;
+
+ // Filter size in samples.
+ size_t filter_size_;
+
+ // Partition Count.
+ size_t num_partitions_;
+
+ // Kernel buffer in frequency domain.
+ FreqDomainBuffer kernel_freq_domain_buffer_;
+
+ // Buffer selector to switch between two filtered signal buffers.
+ size_t buffer_selector_;
+
+ // The freq_domain_buffer we will write new incoming audio into.
+ size_t curr_front_buffer_;
+
+ // Frequency domain buffer used to perform filtering.
+ FreqDomainBuffer freq_domain_buffer_;
+
+ // Two buffers that are consecutively filled with filtered signal output.
+ AudioBuffer filtered_time_domain_buffers_;
+
+ // Accumulator for the outputs from each convolution partition
+ FreqDomainBuffer freq_domain_accumulator_;
+
+ // Temporary time domain buffer to store output when zero padding has been
+ // applied due to non power of two input buffer lengths.
+ AudioBuffer temp_zeropad_buffer_;
+
+ // Temporary time domain buffer to hold time domain kernel chunks during
+ // conversion of a kernel from time to frequency domain.
+ AudioBuffer temp_kernel_chunk_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_PARTITIONED_FFT_FILTER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/partitioned_fft_filter_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/partitioned_fft_filter_test.cc
new file mode 100644
index 000000000..230ec2544
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/partitioned_fft_filter_test.cc
@@ -0,0 +1,761 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "dsp/partitioned_fft_filter.h"
+
+#include <cmath>
+#include <iostream>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "dsp/fft_manager.h"
+
+namespace vraudio {
+
+namespace {
+
+// Permitted error in the output relative to the expected output. This value is
+// 1e-3 as expected output is rounded for readability.
+const float kFftEpsilon = 1e-3f;
+
+// Length of input buffer for use in LongerShorterKernelTest.
+const size_t kLength = 32;
+
+// Helper function for use in LongerShorterKernelTest. Passes a dirac of length
+// kLength through a filter followed by |zeros_iteration| zero vectors of the
+// same length.
+void ProcessFilterWithImpulseSignal(PartitionedFftFilter* filter,
+ FftManager* fft_manager,
+ size_t zeros_iteration,
+ std::vector<float>* output_signal) {
+ AudioBuffer signal_buffer(kNumMonoChannels, kLength);
+ signal_buffer.Clear();
+ signal_buffer[0][0] = 1.0f;
+ AudioBuffer output_buffer(kNumMonoChannels, kLength);
+
+ // Begin filtering with the original (small) kernel.
+ PartitionedFftFilter::FreqDomainBuffer freq_domain_buffer(kNumMonoChannels,
+ kLength * 2);
+ fft_manager->FreqFromTimeDomain(signal_buffer[0], &freq_domain_buffer[0]);
+ filter->Filter(freq_domain_buffer[0]);
+ filter->GetFilteredSignal(&output_buffer[0]);
+ output_signal->insert(output_signal->end(), output_buffer[0].begin(),
+ output_buffer[0].end());
+ // Filter zeros to flush out internal buffers.
+ std::fill(signal_buffer[0].begin(), signal_buffer[0].end(), 0.0f);
+ for (size_t i = 0; i < zeros_iteration; ++i) {
+ fft_manager->FreqFromTimeDomain(signal_buffer[0], &freq_domain_buffer[0]);
+ filter->Filter(freq_domain_buffer[0]);
+ filter->GetFilteredSignal(&output_buffer[0]);
+ output_signal->insert(output_signal->end(), output_buffer[0].begin(),
+ output_buffer[0].end());
+ }
+}
+
+// Tests that convolution will work correctly when the input buffer length is
+// not a power of two.
+TEST(PartitionedFftFilterTest, NonPow2Test) {
+ // Non power of two input buffer length.
+ const size_t kMinNonPowTwo = 13;
+ const size_t kMaxNonPowTwo = 41;
+ for (size_t buffer_length = kMinNonPowTwo; buffer_length <= kMaxNonPowTwo;
+ buffer_length += 2) {
+ for (size_t filter_length = kLength; filter_length <= 3 * kLength;
+ filter_length += kLength) {
+ // Use a filter length that is a power of two.
+ AudioBuffer kernel_buffer(kNumMonoChannels, filter_length);
+ // Place to collect all of the output.
+ std::vector<float> output_signal;
+ // First set the kernel to a linear ramp.
+ for (size_t i = 0; i < filter_length; ++i) {
+ kernel_buffer[0][i] = static_cast<float>(i) / 4.0f;
+ }
+ FftManager fft_manager(buffer_length);
+ PartitionedFftFilter filter(filter_length, buffer_length, &fft_manager);
+ filter.SetTimeDomainKernel(kernel_buffer[0]);
+
+ // Kronecker delta signal.
+ AudioBuffer signal_buffer(kNumMonoChannels, buffer_length);
+ signal_buffer.Clear();
+ signal_buffer[0][0] = 1.0f;
+ AudioBuffer output_buffer(kNumMonoChannels, buffer_length);
+ // Create a freq domain buffer which should be fft_size
+ // (i.e. NextPowTwo(buffer_length) * 2).
+ PartitionedFftFilter::FreqDomainBuffer freq_domain_buffer(
+ kNumMonoChannels, NextPowTwo(buffer_length) * 2);
+ freq_domain_buffer.Clear();
+ fft_manager.FreqFromTimeDomain(signal_buffer[0], &freq_domain_buffer[0]);
+ // Perform convolution.
+ filter.Filter(freq_domain_buffer[0]);
+ filter.GetFilteredSignal(&output_buffer[0]);
+ output_signal.insert(output_signal.end(), output_buffer[0].begin(),
+ output_buffer[0].end());
+ while (output_signal.size() < kernel_buffer.num_frames()) {
+ // Flush with zeros.
+ freq_domain_buffer.Clear();
+ output_buffer.Clear();
+ filter.Filter(freq_domain_buffer[0]);
+ filter.GetFilteredSignal(&output_buffer[0]);
+ output_signal.insert(output_signal.end(), output_buffer[0].begin(),
+ output_buffer[0].end());
+ }
+ // Ensure the output is identical to the input buffer. (large epsilon
+ // needed due to large values in filter).
+ for (size_t i = 0; i < kernel_buffer.num_frames(); ++i) {
+ EXPECT_NEAR(kernel_buffer[0][i], output_signal[i],
+ kEpsilonFloat * 4.0f);
+ }
+ }
+ }
+}
+
+// Tests that the outputs from the convolution are correct based on a
+// precomputed vector from MATLAB.
+TEST(PartitionedFftFilterTest, CorrectNonPowTwoOutputTest) {
+ const size_t kBufferSize = 15;
+
+ // Create an arbirary vector of size 16 for filter and 15 for input signal.
+ const std::vector<float> kernel = {1.0f, 3.0f, 0.0f, 2.0f, 5.0f, 1.0f,
+ 3.0f, 2.0f, 0.0f, 4.0f, 1.0f, 3.0f,
+ 0.0f, 2.0f, 1.0f, 2.0f};
+ const std::vector<float> signal = {2.0f, 3.0f, 3.0f, 4.0f, 0.0f,
+ 0.0f, 2.0f, 1.0f, 2.0f, 1.0f,
+ 3.0f, 2.0f, 4.0f, 0.0f, 2.0f};
+ // Ideal output vector verified with MATLAB.
+ const std::vector<float> ideal_output = {
+ 2.0f, 9.0f, 12.0f, 17.0f, 28.0f, 23.0f, 34.0f, 43.0f, 24.0f,
+ 37.0f, 40.0f, 43.0f, 57.0f, 49.0f, 50.0f, 57.0f, 65.0f, 57.0f,
+ 60.0f, 61.0f, 47.0f, 74.0f, 59.0f, 55.0f, 48.0f, 62.0f, 51.0f,
+ 69.0f, 51.0f, 54.0f, 55.0f, 56.0f, 45.0f, 43.0f, 33.0f, 24.0f,
+ 40.0f, 16.0f, 31.0f, 11.0f, 22.0f, 8.0f, 12.0f, 2.0f, 4.0f};
+
+ AudioBuffer kernel_buffer(kNumMonoChannels, kernel.size());
+ kernel_buffer[0] = kernel;
+
+ AudioBuffer signal_buffer(kNumMonoChannels, signal.size());
+ signal_buffer[0] = signal;
+
+ AudioBuffer output_buffer(kNumMonoChannels, signal.size());
+
+ std::vector<float> output_signal;
+ output_signal.reserve(kernel.size() + signal.size() * 3);
+
+ FftManager fft_manager(kBufferSize);
+ PartitionedFftFilter filter(kernel.size(), kBufferSize, &fft_manager);
+ filter.SetTimeDomainKernel(kernel_buffer[0]);
+
+ PartitionedFftFilter::FreqDomainBuffer freq_domain_buffer(
+ 1, NextPowTwo(kBufferSize) * 2);
+ fft_manager.FreqFromTimeDomain(signal_buffer[0], &freq_domain_buffer[0]);
+ filter.Filter(freq_domain_buffer[0]);
+ filter.GetFilteredSignal(&output_buffer[0]);
+ output_signal.insert(output_signal.end(), output_buffer[0].begin(),
+ output_buffer[0].end());
+
+ // Filter again with the same input.
+ filter.Filter(freq_domain_buffer[0]);
+ filter.GetFilteredSignal(&output_buffer[0]);
+ output_signal.insert(output_signal.end(), output_buffer[0].begin(),
+ output_buffer[0].end());
+
+ // Filter zeros to flush out internal buffers.
+ signal_buffer.Clear();
+ fft_manager.FreqFromTimeDomain(signal_buffer[0], &freq_domain_buffer[0]);
+ filter.Filter(freq_domain_buffer[0]);
+ filter.GetFilteredSignal(&output_buffer[0]);
+ output_signal.insert(output_signal.end(), output_buffer[0].begin(),
+ output_buffer[0].end());
+
+ for (size_t sample = 0; sample < ideal_output.size(); ++sample) {
+ EXPECT_NEAR(output_signal[sample], ideal_output[sample], kFftEpsilon);
+ }
+}
+
+// Tests that we can switch to a time domain filter kernel of greater
+// length than the original kernal provided at instantiation. It then tests that
+// we can switch to a time domain filter kernel of lesser length than the
+// original kernel.
+TEST(PartitionedFftFilterTest, LongerShorterTimeDomainKernelTest) {
+ AudioBuffer small_kernel_buffer(kNumMonoChannels, kLength);
+ AudioBuffer big_kernel_buffer(kNumMonoChannels, kLength * 2);
+
+ // Place to collect all of the output.
+ std::vector<float> total_output_signal;
+
+ // First set the kernels to linear ramps of differing lengths.
+ for (size_t i = 0; i < kLength * 2; ++i) {
+ if (i < kLength) {
+ small_kernel_buffer[0][i] = static_cast<float>(i) / 4.0f;
+ }
+ big_kernel_buffer[0][i] = static_cast<float>(i) / 4.0f;
+ }
+
+ FftManager fft_manager(kLength);
+ PartitionedFftFilter filter(small_kernel_buffer.num_frames(), kLength,
+ big_kernel_buffer.num_frames(), &fft_manager);
+
+ filter.SetTimeDomainKernel(small_kernel_buffer[0]);
+ ProcessFilterWithImpulseSignal(&filter, &fft_manager, 2,
+ &total_output_signal);
+
+ filter.SetTimeDomainKernel(big_kernel_buffer[0]);
+ ProcessFilterWithImpulseSignal(&filter, &fft_manager, 3,
+ &total_output_signal);
+
+ filter.SetTimeDomainKernel(small_kernel_buffer[0]);
+ ProcessFilterWithImpulseSignal(&filter, &fft_manager, 2,
+ &total_output_signal);
+
+ // Test to see if output from both kernels is present kLength * 2 zeros in
+ // between.
+ for (size_t i = 0; i < kLength; ++i) {
+ EXPECT_NEAR(static_cast<float>(i) / 4.0f, total_output_signal[i],
+ kFftEpsilon);
+ EXPECT_NEAR(0.0f, total_output_signal[i + kLength], kFftEpsilon);
+ EXPECT_NEAR(0.0f, total_output_signal[i + 2 * kLength], kFftEpsilon);
+ EXPECT_NEAR(static_cast<float>(i) / 4.0f,
+ total_output_signal[i + 3 * kLength], kFftEpsilon);
+ EXPECT_NEAR(static_cast<float>(i + kLength) / 4.0f,
+ total_output_signal[i + 4 * kLength], kFftEpsilon);
+ EXPECT_NEAR(0.0f, total_output_signal[i + 5 * kLength], kFftEpsilon);
+ EXPECT_NEAR(0.0f, total_output_signal[i + 6 * kLength], kFftEpsilon);
+ EXPECT_NEAR(static_cast<float>(i) / 4.0f,
+ total_output_signal[i + 7 * kLength], kFftEpsilon);
+ EXPECT_NEAR(0.0f, total_output_signal[i + 8 * kLength], kFftEpsilon);
+ EXPECT_NEAR(0.0f, total_output_signal[i + 9 * kLength], kFftEpsilon);
+ }
+}
+
+// Tests that the outputs from the same convolution performed with different FFT
+// sizes will all return equal results, including when the FFT size is equal to
+// the kernel size.
+TEST(PartitionedFftFilterTest, PartitionSizeInvarianceTest) {
+ const std::vector<size_t> kFftSizes = {32, 64, 128};
+
+ // Create an arbirary vector of size 128 for both filter and input signal.
+ const size_t max_fft_size = kFftSizes[kFftSizes.size() - 1];
+ AudioBuffer kernel_buffer(kNumMonoChannels, max_fft_size);
+ AudioBuffer signal_buffer(kNumMonoChannels, max_fft_size);
+ for (size_t i = 0; i < max_fft_size; ++i) {
+ kernel_buffer[0][i] = static_cast<float>(static_cast<int>(i) % 13 - 7);
+ signal_buffer[0][i] = static_cast<float>(static_cast<int>(i) % 17 - 9);
+ }
+
+ std::vector<std::vector<float>> output_signal(
+ kFftSizes.size(), std::vector<float>(max_fft_size * 2));
+
+ // Iterate over 3 fft sizes.
+ for (size_t fft_idx = 0; fft_idx < kFftSizes.size(); ++fft_idx) {
+ const size_t chunk_size = kFftSizes[fft_idx] / 2;
+ FftManager fft_manager(chunk_size);
+ PartitionedFftFilter filter(max_fft_size, chunk_size, &fft_manager);
+ filter.SetTimeDomainKernel(kernel_buffer[0]);
+
+ AudioBuffer input_chunk(kNumMonoChannels, chunk_size);
+ AudioBuffer output_chunk(kNumMonoChannels, chunk_size);
+ PartitionedFftFilter::FreqDomainBuffer freq_domain_buffer(
+ 1, kFftSizes[fft_idx]);
+
+ // Break the input signal into chunks of fft size / 2.
+ for (size_t chunk = 0; chunk < (max_fft_size / chunk_size); ++chunk) {
+ AudioBuffer signal_block(kNumMonoChannels, chunk_size);
+ std::copy_n(signal_buffer[0].begin() + (chunk * chunk_size), chunk_size,
+ signal_block[0].begin());
+
+ fft_manager.FreqFromTimeDomain(signal_block[0], &freq_domain_buffer[0]);
+ filter.Filter(freq_domain_buffer[0]);
+ filter.GetFilteredSignal(&output_chunk[0]);
+ output_signal[fft_idx].insert(output_signal[fft_idx].end(),
+ output_chunk[0].begin(),
+ output_chunk[0].end());
+ }
+ }
+ // Now test the outputs are pretty much equal to one another (to the first).
+ for (size_t i = 1; i < kFftSizes.size(); ++i) {
+ for (size_t sample = 0; sample < output_signal[0].size(); ++sample) {
+ EXPECT_NEAR(output_signal[0][sample], output_signal[i][sample],
+ kFftEpsilon);
+ }
+ }
+}
+
+// Tests that the outputs from the convolution are correct based on a
+// precomputed vector from MATLAB.
+TEST(PartitionedFftFilterTest, CorrectOutputTest) {
+ const size_t kBufferSize = 32;
+
+ // Create an arbirary vector of size 32 for both filter and input signal.
+ const std::vector<float> kernel = {
+ 1.0f, 3.0f, 0.0f, 2.0f, 5.0f, 1.0f, 3.0f, 2.0f, 0.0f, 4.0f, 1.0f,
+ 3.0f, 0.0f, 2.0f, 1.0f, 2.0f, 2.0f, 1.0f, 0.0f, 3.0f, 5.0f, 2.0f,
+ 3.0f, 0.0f, 1.0f, 4.0f, 2.0f, 0.0f, 1.0f, 0.0f, 2.0f, 1.0f};
+ const std::vector<float> signal = {
+ 2.0f, 1.0f, 3.0f, 3.0f, 2.0f, 4.0f, 2.0f, 1.0f, 3.0f, 4.0f, 5.0f,
+ 3.0f, 2.0f, 2.0f, 5.0f, 4.0f, 5.0f, 3.0f, 3.0f, 4.0f, 0.0f, 0.0f,
+ 2.0f, 1.0f, 2.0f, 1.0f, 3.0f, 2.0f, 4.0f, 0.0f, 2.0f, 1.0f};
+
+ AudioBuffer kernel_buffer(kNumMonoChannels, kernel.size());
+ kernel_buffer[0] = kernel;
+
+ AudioBuffer signal_buffer(kNumMonoChannels, signal.size());
+ signal_buffer[0] = signal;
+
+ AudioBuffer output_buffer(kNumMonoChannels, kernel.size());
+
+ std::vector<float> output_signal;
+ output_signal.reserve(kernel.size() + signal.size());
+
+ FftManager fft_manager(kBufferSize);
+ PartitionedFftFilter filter(kernel.size(), kBufferSize, &fft_manager);
+ filter.SetTimeDomainKernel(kernel_buffer[0]);
+
+ PartitionedFftFilter::FreqDomainBuffer freq_domain_buffer(kNumMonoChannels,
+ kBufferSize * 2);
+ fft_manager.FreqFromTimeDomain(signal_buffer[0], &freq_domain_buffer[0]);
+ filter.Filter(freq_domain_buffer[0]);
+ filter.GetFilteredSignal(&output_buffer[0]);
+ output_signal.insert(output_signal.end(), output_buffer[0].begin(),
+ output_buffer[0].end());
+
+ // Filter zeros to flush out internal buffers.
+ signal_buffer.Clear();
+ fft_manager.FreqFromTimeDomain(signal_buffer[0], &freq_domain_buffer[0]);
+ filter.Filter(freq_domain_buffer[0]);
+ filter.GetFilteredSignal(&output_buffer[0]);
+ output_signal.insert(output_signal.end(), output_buffer[0].begin(),
+ output_buffer[0].end());
+
+ // Ideal output vector verified with MATLAB.
+ const std::vector<float> kIdeal = {
+ 2.0f, 7.0f, 6.0f, 16.0f, 23.0f, 23.0f, 42.0f, 36.0f, 38.0f,
+ 62.0f, 51.0f, 66.0f, 67.0f, 72.0f, 88.0f, 90.0f, 90.0f, 95.0f,
+ 104.0f, 118.0f, 127.0f, 113.0f, 123.0f, 131.0f, 116.0f, 144.0f, 119.0f,
+ 126.0f, 138.0f, 138.0f, 153.0f, 142.0f, 130.0f, 125.0f, 137.0f, 142.0f,
+ 121.0f, 113.0f, 99.0f, 112.0f, 84.0f, 89.0f, 68.0f, 66.0f, 74.0f,
+ 54.0f, 54.0f, 59.0f, 53.0f, 42.0f, 42.0f, 26.0f, 32.0f, 28.0f,
+ 18.0f, 15.0f, 19.0f, 9.0f, 12.0f, 5.0f, 4.0f, 4.0f, 1.0f};
+ for (size_t sample = 0; sample < kIdeal.size(); ++sample) {
+ EXPECT_NEAR(output_signal[sample], kIdeal[sample], kFftEpsilon);
+ }
+}
+
+// Tests that the outputs from the convolution are equal to zero when the inputs
+// are all zero.
+TEST(PartitionedFftFilterTest, ZeroInputZeroOutputTest) {
+ const size_t kChunkSize = 16;
+
+ // Create an arbirary vector of size 16 for filter.
+ const std::vector<float> kernel = {1.0f, 3.0f, 0.0f, 2.0f, 5.0f, 1.0f,
+ 3.0f, 2.0f, 0.0f, 4.0f, 1.0f, 3.0f,
+ 0.0f, 2.0f, 1.0f, 2.0f};
+
+ AudioBuffer kernel_buffer(kNumMonoChannels, kernel.size());
+ kernel_buffer[0] = kernel;
+
+ std::vector<float> output_signal;
+ output_signal.reserve(kernel.size() * 2);
+
+ FftManager fft_manager(kChunkSize);
+ PartitionedFftFilter filter(kernel_buffer[0].size(), kChunkSize,
+ &fft_manager);
+ filter.SetTimeDomainKernel(kernel_buffer[0]);
+
+ AudioBuffer output_chunk(kNumMonoChannels, kChunkSize);
+
+ // Filter zeros to flush out internal buffers.
+ AudioBuffer zero_signal(kNumMonoChannels, kChunkSize);
+ zero_signal.Clear();
+
+ PartitionedFftFilter::FreqDomainBuffer freq_domain_buffer(kNumMonoChannels,
+ kChunkSize * 2);
+ for (size_t j = 0; j < 2 * kernel.size() / kChunkSize; ++j) {
+ fft_manager.FreqFromTimeDomain(zero_signal[0], &freq_domain_buffer[0]);
+ filter.Filter(freq_domain_buffer[0]);
+ filter.GetFilteredSignal(&output_chunk[0]);
+ output_signal.insert(output_signal.end(), output_chunk[0].begin(),
+ output_chunk[0].end());
+ }
+
+ // Check that all output samples are practically 0.0.
+ for (size_t sample = 0; sample < output_signal.size(); ++sample) {
+ EXPECT_EQ(output_signal[sample], 0.0f);
+ }
+}
+
+// This test uses a simple shifted dirac impulse as kernel and checks the
+// resulting signal delay.
+TEST(PartitionedFftFilterTest, DiracImpulseTest) {
+ const size_t kFilterSize = 32;
+ const size_t kNumBlocks = 4;
+ const size_t kSignalSize = kFilterSize * kNumBlocks;
+
+ // Generate a saw tooth signal.
+ AudioBuffer test_signal(kNumMonoChannels, kSignalSize);
+ for (size_t i = 0; i < kSignalSize; ++i) {
+ test_signal[0][i] = static_cast<float>(i % 5);
+ }
+
+ FftManager fft_manager(kFilterSize);
+ PartitionedFftFilter fft_filter(kFilterSize, kFilterSize, &fft_manager);
+
+ AudioBuffer kernel(kNumMonoChannels, kFilterSize);
+ // Construct dirac impulse response. This kernel should result in a delay of
+ // length "|kFilterSize| / 2".
+ kernel.Clear();
+ kernel[0][kFilterSize / 2] = 1.0f;
+ fft_filter.SetTimeDomainKernel(kernel[0]);
+
+ std::vector<float> filtered_signal;
+ filtered_signal.reserve(kSignalSize);
+
+ AudioBuffer filtered_block(kNumMonoChannels, kFilterSize);
+ PartitionedFftFilter::FreqDomainBuffer freq_domain_buffer(kNumMonoChannels,
+ kFilterSize * 2);
+
+ for (size_t b = 0; b < kNumBlocks; ++b) {
+ AudioBuffer signal_block(kNumMonoChannels, kFilterSize);
+ std::copy_n(test_signal[0].begin() + b * kFilterSize, kFilterSize,
+ signal_block[0].begin());
+
+ fft_manager.FreqFromTimeDomain(signal_block[0], &freq_domain_buffer[0]);
+ fft_filter.Filter(freq_domain_buffer[0]);
+ fft_filter.GetFilteredSignal(&filtered_block[0]);
+ filtered_signal.insert(filtered_signal.end(), filtered_block[0].begin(),
+ filtered_block[0].end());
+ }
+
+ for (size_t i = 0; i < kFilterSize / 2; ++i) {
+ // First "filter_size / 2" samples should be zero padded due to the applied
+ // delay.
+ EXPECT_NEAR(filtered_signal[i], 0.0f, 1e-5f);
+ }
+ for (size_t i = kFilterSize / 2; i < kSignalSize; ++i) {
+ // Test if signal is delayed by exactly |kFilterSize| / 2 samples.
+ EXPECT_NEAR(filtered_signal[i], test_signal[0][i - kFilterSize / 2], 1e-5f);
+ }
+}
+
+} // namespace
+
+class PartitionedFftFilterFrequencyBufferTest : public ::testing::Test {
+ protected:
+ PartitionedFftFilterFrequencyBufferTest() {}
+ // Virtual methods from ::testing::Test
+ ~PartitionedFftFilterFrequencyBufferTest() override {}
+ void SetUp() override {}
+ void TearDown() override {}
+
+ void SetFreqDomainBuffer(PartitionedFftFilter* filter,
+ FftManager* fft_manager) {
+ std::vector<std::vector<float>> values_vectors(
+ filter->num_partitions_,
+ std::vector<float>(fft_manager->GetFftSize(), 0.0f));
+ for (size_t i = 0; i < filter->num_partitions_; ++i) {
+ values_vectors[i][0] = static_cast<float>(i + 1);
+ filter->freq_domain_buffer_[i] = values_vectors[i];
+ }
+ }
+
+ void SetFreqDomainKernel(PartitionedFftFilter* filter) {
+ // Put a Kronecker delta in the first partition, a kronecker delta times 2
+ // in the second, a kronecker delta times 3 in the third, etc...
+ AudioBuffer kernel(kNumMonoChannels, filter->filter_size_);
+ for (size_t i = 0; i < filter->num_partitions_; ++i) {
+ for (size_t sample = 0; sample < filter->chunk_size_; ++sample) {
+ kernel[0][i * filter->chunk_size_ + sample] = static_cast<float>(i + 1);
+ }
+ }
+ filter->SetTimeDomainKernel(kernel[0]);
+ }
+
+ void TestFrequencyBufferReset(size_t initial_size, size_t bigger_size,
+ size_t smaller_size,
+ PartitionedFftFilter* filter,
+ FftManager* fft_manager) {
+ const size_t initial_num_partitions = 2 * initial_size;
+ const size_t bigger_num_partitions = 2 * bigger_size;
+ const size_t smaller_num_partitions = 2 * smaller_size;
+ // Set the |freq_domain_buffer_| inside |filter| such that it has known
+ // values.
+ SetFreqDomainBuffer(filter, fft_manager);
+ AudioBuffer initial_freq_domain_buffer;
+ initial_freq_domain_buffer = filter->freq_domain_buffer_;
+
+ // Set the current front buffer to something other than zero and less than
+ // the number of partitions. If |num_partitions_| is 1 it is set to 0.
+ filter->curr_front_buffer_ = filter->num_partitions_ / 2;
+
+ // Reset the |freq_domain_buffers_| for a new bigger kernel size.
+ filter->ResetFreqDomainBuffers(bigger_size * fft_manager->GetFftSize());
+ AudioBuffer bigger_freq_domain_buffer;
+ bigger_freq_domain_buffer = filter->freq_domain_buffer_;
+ // Verify that the input index has been reset.
+ EXPECT_EQ(filter->curr_front_buffer_, 0U);
+
+ // Set the current front buffer to something other than zero and less than
+ // the number of partitions. If |num_partitions_| is 1 it is set to 0.
+ filter->curr_front_buffer_ = filter->num_partitions_ / 2;
+
+ // Reset the |freq_domain_buffers_| for a new smaller kernel size.
+ filter->ResetFreqDomainBuffers(smaller_size * fft_manager->GetFftSize());
+ AudioBuffer smaller_freq_domain_buffer;
+ smaller_freq_domain_buffer = filter->freq_domain_buffer_;
+ // Verify that the input index has been reset.
+ EXPECT_EQ(filter->curr_front_buffer_, 0U);
+
+ // Expect the following to have happened:
+ // Initially there are 16 partitions with the first element in each channel
+ // of the |freq_domain_buffers_| in |filter| being 1, 2, ..., 16.
+ for (size_t i = 0; i < initial_num_partitions; ++i) {
+ EXPECT_EQ(initial_freq_domain_buffer[i][0], static_cast<float>(i + 1));
+ }
+
+ // |curr_front_buffer_| is set to 8. After a reset with a larger kernel we
+ // expect all of the buffers to have been copied into the new
+ // |freq_domain_buffers_| starting from |curr_front_buffer_| and wrapping
+ // round with the remaining channels set to zero.
+ for (size_t i = 0; i < initial_num_partitions; ++i) {
+ EXPECT_EQ(bigger_freq_domain_buffer[i][0],
+ initial_freq_domain_buffer[(initial_size + i) %
+ initial_num_partitions][0]);
+ }
+ for (size_t i = initial_num_partitions; i < bigger_num_partitions; ++i) {
+ EXPECT_EQ(bigger_freq_domain_buffer[i][0], 0.0f);
+ }
+
+ // |curr_front_buffer_| is then set to 5. After a reset with a smaller
+ // kernel ammounting to 10 partitions we expect just the first 10 buffers to
+ // have been copied into the new |freq_domain_buffers_| starting from
+ // |curr_front_buffer_| and wrapping round.
+ for (size_t i = 0; i < smaller_num_partitions; ++i) {
+ EXPECT_EQ(smaller_freq_domain_buffer[i][0],
+ bigger_freq_domain_buffer[(bigger_size + i) %
+ bigger_num_partitions][0]);
+ }
+ }
+
+ void TestLongerShorterFrequencyDomainKernel(size_t buffer_size) {
+ AudioBuffer small_kernel_buffer(kNumMonoChannels, buffer_size);
+ AudioBuffer big_kernel_buffer(kNumMonoChannels, 2 * buffer_size);
+
+ // Place to collect all of the output.
+ std::vector<float> total_output_signal;
+
+ // First set the kernels to linear ramps of differing lengths.
+ for (size_t i = 0; i < 2 * buffer_size; ++i) {
+ if (i < buffer_size) {
+ small_kernel_buffer[0][i] = static_cast<float>(i) / 4.0f;
+ }
+ big_kernel_buffer[0][i] = static_cast<float>(i) / 4.0f;
+ }
+
+ FftManager fft_manager(buffer_size);
+ PartitionedFftFilter filter(buffer_size, buffer_size, 2 * buffer_size,
+ &fft_manager);
+
+ // Generate the small and large frequency domain buffers.
+ filter.SetTimeDomainKernel(small_kernel_buffer[0]);
+ const size_t small_num_partitions = filter.num_partitions_;
+ PartitionedFftFilter::FreqDomainBuffer small_freq_domain_kernel =
+ PartitionedFftFilter::FreqDomainBuffer(small_num_partitions,
+ fft_manager.GetFftSize());
+ for (size_t i = 0; i < small_num_partitions; ++i) {
+ small_freq_domain_kernel[i] = filter.kernel_freq_domain_buffer_[i];
+ }
+
+ filter.SetTimeDomainKernel(big_kernel_buffer[0]);
+ const size_t big_num_partitions = filter.num_partitions_;
+ PartitionedFftFilter::FreqDomainBuffer big_freq_domain_kernel =
+ PartitionedFftFilter::FreqDomainBuffer(big_num_partitions,
+ fft_manager.GetFftSize());
+ for (size_t i = 0; i < big_num_partitions; ++i) {
+ big_freq_domain_kernel[i] = filter.kernel_freq_domain_buffer_[i];
+ }
+
+ filter.SetFreqDomainKernel(small_freq_domain_kernel);
+ ProcessFilterWithImpulseSignal(&filter, &fft_manager, 2,
+ &total_output_signal);
+
+ filter.SetFreqDomainKernel(big_freq_domain_kernel);
+ ProcessFilterWithImpulseSignal(&filter, &fft_manager, 3,
+ &total_output_signal);
+
+ filter.SetFreqDomainKernel(small_freq_domain_kernel);
+ ProcessFilterWithImpulseSignal(&filter, &fft_manager, 2,
+ &total_output_signal);
+
+ // Test to see if output from both kernels is present |big_size| zeros in
+ // between.
+ for (size_t i = 0; i < buffer_size; ++i) {
+ EXPECT_NEAR(static_cast<float>(i) / 4.0f, total_output_signal[i],
+ kFftEpsilon);
+ EXPECT_NEAR(0.0f, total_output_signal[i + buffer_size], kFftEpsilon);
+ EXPECT_NEAR(0.0f, total_output_signal[i + 2 * buffer_size], kFftEpsilon);
+ EXPECT_NEAR(static_cast<float>(i) / 4.0f,
+ total_output_signal[i + 3 * buffer_size], kFftEpsilon);
+ EXPECT_NEAR(static_cast<float>(i + buffer_size) / 4.0f,
+ total_output_signal[i + 4 * buffer_size], kFftEpsilon);
+ EXPECT_NEAR(0.0f, total_output_signal[i + 5 * buffer_size], kFftEpsilon);
+ EXPECT_NEAR(0.0f, total_output_signal[i + 6 * buffer_size], kFftEpsilon);
+ EXPECT_NEAR(static_cast<float>(i) / 4.0f,
+ total_output_signal[i + 7 * buffer_size], kFftEpsilon);
+ EXPECT_NEAR(0.0f, total_output_signal[i + 8 * buffer_size], kFftEpsilon);
+ EXPECT_NEAR(0.0f, total_output_signal[i + 9 * buffer_size], kFftEpsilon);
+ }
+ }
+
+ void TestPartitionReplacement(PartitionedFftFilter* filter,
+ FftManager* fft_manager) {
+ // Fill the first partition with a kronecker delta in the first partition, a
+ // kronecker delta times 2 in the second, a kronecker delta times 3 in the
+ // third, etc...
+ SetFreqDomainKernel(filter);
+ // Get a copy of the kernel.
+ AudioBuffer initial_kernel;
+ initial_kernel = filter->kernel_freq_domain_buffer_;
+
+ // Create a freq domain kernel chunk with the same values as the final
+ // partition.
+ AudioBuffer kernel_chunk(kNumMonoChannels, filter->chunk_size_);
+ kernel_chunk.Clear();
+ AudioBuffer::Channel& kernel_chunk_channel = kernel_chunk[0];
+ for (size_t sample = 0; sample < filter->chunk_size_; ++sample) {
+ kernel_chunk_channel[sample] =
+ static_cast<float>(filter->num_partitions_);
+ }
+ // The partition we will replace.
+ const size_t kReplacePartitionIndex = 2;
+ // Replace a partition with a new time domain kernel chunk (all zeros).
+ filter->ReplacePartition(kReplacePartitionIndex, kernel_chunk_channel);
+
+ // Get a copy of the kernel after replacing the 3rd freq domain partition.
+ AudioBuffer replaced_kernel;
+ replaced_kernel = filter->kernel_freq_domain_buffer_;
+
+ for (size_t i = 0; i < filter->num_partitions_; ++i) {
+ for (size_t sample = 0; sample < fft_manager->GetFftSize(); ++sample) {
+ // Expect that all of the other partitions are unchanged.
+ if (i == kReplacePartitionIndex) {
+ // Check if the chosen partition has been altered.
+ EXPECT_EQ(initial_kernel[filter->num_partitions_ - 1][sample],
+ replaced_kernel[i][sample]);
+ } else {
+ // Check if all of the other partitions are unchanged.
+ EXPECT_EQ(initial_kernel[i][sample], replaced_kernel[i][sample]);
+ }
+ }
+ }
+ }
+
+ void TestFilterLengthSetter(PartitionedFftFilter* filter,
+ FftManager* fft_manager) {
+ // Set the |freq_domain_buffer_| inside |filter| such that it has known
+ // values (In the time domain, all value 1, then 2 etc.. per partition).
+ SetFreqDomainKernel(filter);
+
+ // Get a copy of the kernel and number of partitions before we set the
+ // filter length.
+ AudioBuffer initial_kernel;
+ initial_kernel = filter->kernel_freq_domain_buffer_;
+ const size_t initial_num_partitions = filter->num_partitions_;
+
+ filter->SetFilterLength(filter->filter_size_ / 2);
+
+ // Get a copy after we half the filter length.
+ AudioBuffer half_kernel;
+ half_kernel = filter->kernel_freq_domain_buffer_;
+ const size_t half_num_partitions = filter->num_partitions_;
+ EXPECT_EQ(initial_num_partitions, 2 * half_num_partitions);
+
+ filter->SetFilterLength(filter->filter_size_ * 2);
+
+ // Get a copy after we re-double the filter length.
+ AudioBuffer redouble_kernel;
+ redouble_kernel = filter->kernel_freq_domain_buffer_;
+ EXPECT_EQ(initial_num_partitions, filter->num_partitions_);
+
+ for (size_t i = 0; i < half_num_partitions; ++i) {
+ for (size_t sample = 0; sample < fft_manager->GetFftSize(); ++sample) {
+ // Check that the first two partitions in all 3 cases are the same.
+ EXPECT_EQ(initial_kernel[i][sample], half_kernel[i][sample]);
+ EXPECT_EQ(initial_kernel[i][sample], redouble_kernel[i][sample]);
+ // Check that the final two partitions, after resizing the filters
+ // frequency domain buffers, contain only zeros.
+ EXPECT_EQ(redouble_kernel[i + half_num_partitions][sample], 0.0f);
+ }
+ }
+ }
+};
+
+// Tests whether the reordering of the channels of |freq_domain_buffers_| is as
+// expected after calling |ResetFreqDomainBuffers| with both longer and shorter
+// filters.
+TEST_F(PartitionedFftFilterFrequencyBufferTest, FrequencyBufferResetTest) {
+ const size_t kBufferSize = 32;
+ const size_t kInitialFilterSizeFactor = 8;
+ const size_t kBiggerFilterSizeFactor = 10;
+ const size_t kSmallerFilterSizeFactor = 5;
+ // Initially there will be 16 partitions, then 20, then 10.
+ FftManager fft_manager(kBufferSize);
+ PartitionedFftFilter filter(
+ kInitialFilterSizeFactor * kBufferSize * 2, kBufferSize,
+ kBiggerFilterSizeFactor * kBufferSize * 2, &fft_manager);
+ TestFrequencyBufferReset(kInitialFilterSizeFactor, kBiggerFilterSizeFactor,
+ kSmallerFilterSizeFactor, &filter, &fft_manager);
+}
+
+// Tests that we can switch to a frequency domain filter kernel with a greater
+// number of partitions than the original kernel provided at instantiation.
+// Ensures that we can switch to a frequency domain filter kernel with less
+// partitions than the original kernel.
+TEST_F(PartitionedFftFilterFrequencyBufferTest,
+ LongerShorterFrequencyDomainKernelTest) {
+ TestLongerShorterFrequencyDomainKernel(kLength);
+}
+
+// Tests whether replacing an individual partition works correctly.
+TEST_F(PartitionedFftFilterFrequencyBufferTest, ReplacePartitionTest) {
+ const size_t kBufferSize = 32;
+ const size_t kFilterSizeFactor = 5;
+ // A filter with 5 partitions.
+ FftManager fft_manager(kBufferSize);
+ PartitionedFftFilter filter(kFilterSizeFactor * kBufferSize * 2, kBufferSize,
+ &fft_manager);
+ TestPartitionReplacement(&filter, &fft_manager);
+}
+
+// Tests whether setting the length of the filter kernel, and thus the number
+// of partitions gives the expected results.
+TEST_F(PartitionedFftFilterFrequencyBufferTest, SetFilterLengthTest) {
+ const size_t kBufferSize = 32;
+ const size_t kFilterSizeFactor = 5;
+ // A filter with 5 partitions.
+ FftManager fft_manager(kBufferSize);
+ PartitionedFftFilter filter(kFilterSizeFactor * kBufferSize * 2, kBufferSize,
+ kFilterSizeFactor * kBufferSize * 4,
+ &fft_manager);
+ TestFilterLengthSetter(&filter, &fft_manager);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/reflection.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/reflection.h
new file mode 100644
index 000000000..4b134f6f2
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/reflection.h
@@ -0,0 +1,34 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_REFLECTION_H_
+#define RESONANCE_AUDIO_DSP_REFLECTION_H_
+
+namespace vraudio {
+
+// Describes a room reflection that contains information on its time of arrival
+// and magnitude.
+struct Reflection {
+ // Time of arrival of the reflection in seconds.
+ float delay_time_seconds = 0.0f;
+
+ // Magnitude of the reflection.
+ float magnitude = 0.0f;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_REFLECTION_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/reflections_processor.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/reflections_processor.cc
new file mode 100644
index 000000000..9a9174cdc
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/reflections_processor.cc
@@ -0,0 +1,177 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/reflections_processor.h"
+
+#include <algorithm>
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "dsp/filter_coefficient_generators.h"
+#include "dsp/gain.h"
+#include "dsp/shoe_box_room.h"
+
+namespace vraudio {
+
+namespace {
+
+// Maximum allowed delay time for a reflection. Above 2s, the effective output
+// level of a reflection will fall below -60dB and thus perceived dynamic
+// changes should be negligible.
+const size_t kMaxDelayTimeSeconds = 2;
+
+// Returns the maximum delay time in the given set of reflections.
+float FindMaxReflectionDelayTime(const std::vector<Reflection>& reflections) {
+ float max_delay_time = 0.0f;
+ for (const auto& reflection : reflections) {
+ max_delay_time = std::max(max_delay_time, reflection.delay_time_seconds);
+ }
+ return max_delay_time;
+}
+
+} // namespace
+
+ReflectionsProcessor::ReflectionsProcessor(int sample_rate,
+ size_t frames_per_buffer)
+ : sample_rate_(sample_rate),
+ frames_per_buffer_(frames_per_buffer),
+ max_delay_samples_(kMaxDelayTimeSeconds * sample_rate_),
+ low_pass_filter_(0.0f),
+ temp_mono_buffer_(kNumMonoChannels, frames_per_buffer_),
+ current_reflection_buffer_(kNumFirstOrderAmbisonicChannels,
+ frames_per_buffer),
+ target_reflection_buffer_(kNumFirstOrderAmbisonicChannels,
+ frames_per_buffer),
+ target_reflections_(kNumRoomSurfaces),
+ crossfade_(false),
+ crossfader_(frames_per_buffer_),
+ num_frames_to_process_on_empty_input_(0),
+ delays_(kNumRoomSurfaces),
+ delay_filter_(max_delay_samples_, frames_per_buffer),
+ delay_buffer_(kNumRoomSurfaces, frames_per_buffer),
+ gains_(kNumRoomSurfaces),
+ gain_processors_(kNumRoomSurfaces) {
+ DCHECK_GT(sample_rate_, 0);
+ DCHECK_GT(frames_per_buffer_, 0U);
+}
+
+void ReflectionsProcessor::Update(
+ const ReflectionProperties& reflection_properties,
+ const WorldPosition& listener_position) {
+
+ // Initialize the low-pass filter.
+ const float low_pass_coefficient = ComputeLowPassMonoPoleCoefficient(
+ reflection_properties.cutoff_frequency, sample_rate_);
+ low_pass_filter_.SetCoefficient(low_pass_coefficient);
+ // Update the target reflections.
+ WorldPosition relative_listener_position;
+ GetRelativeDirection(
+ WorldPosition(reflection_properties.room_position),
+ WorldRotation(reflection_properties.room_rotation).conjugate(),
+ listener_position, &relative_listener_position);
+ ComputeReflections(relative_listener_position,
+ WorldPosition(reflection_properties.room_dimensions),
+ reflection_properties.coefficients, &target_reflections_);
+ // Additional |frames_per_buffer_| to process needed to compensate the
+ // crossfade between the current and target reflections.
+ num_frames_to_process_on_empty_input_ =
+ frames_per_buffer_ +
+ static_cast<size_t>(FindMaxReflectionDelayTime(target_reflections_) *
+ static_cast<float>(sample_rate_));
+ // Reflections have been updated so crossfade is required.
+ crossfade_ = true;
+}
+
+void ReflectionsProcessor::Process(const AudioBuffer& input,
+ AudioBuffer* output) {
+ DCHECK_EQ(input.num_channels(), kNumMonoChannels);
+ DCHECK_EQ(input.num_frames(), frames_per_buffer_);
+ DCHECK(output);
+ DCHECK_GE(output->num_channels(), kNumFirstOrderAmbisonicChannels);
+ DCHECK_EQ(output->num_frames(), frames_per_buffer_);
+ // Prefilter mono input.
+ const AudioBuffer::Channel& input_channel = input[0];
+ AudioBuffer::Channel* temp_channel = &temp_mono_buffer_[0];
+ const bool filter_success =
+ low_pass_filter_.Filter(input_channel, temp_channel);
+ const AudioBuffer::Channel& low_pass_channel =
+ filter_success ? *temp_channel : input_channel;
+ delay_filter_.InsertData(low_pass_channel);
+ // Process reflections.
+ if (crossfade_) {
+ ApplyReflections(&current_reflection_buffer_);
+ UpdateGainsAndDelays();
+ ApplyReflections(&target_reflection_buffer_);
+ crossfader_.ApplyLinearCrossfade(target_reflection_buffer_,
+ current_reflection_buffer_, output);
+ crossfade_ = false;
+ } else {
+ ApplyReflections(output);
+ }
+}
+
+void ReflectionsProcessor::UpdateGainsAndDelays() {
+ for (size_t i = 0; i < kNumRoomSurfaces; ++i) {
+ delays_[i] =
+ std::min(max_delay_samples_,
+ static_cast<size_t>(target_reflections_[i].delay_time_seconds *
+ static_cast<float>(sample_rate_)));
+ gains_[i] = target_reflections_[i].magnitude;
+ }
+}
+
+void ReflectionsProcessor::ApplyReflections(AudioBuffer* output) {
+ DCHECK(output);
+ DCHECK_GE(output->num_channels(), kNumFirstOrderAmbisonicChannels);
+ (*output).Clear();
+ for (size_t i = 0; i < kNumRoomSurfaces; ++i) {
+ auto* delay_channel = &delay_buffer_[i];
+ delay_filter_.GetDelayedData(delays_[i], delay_channel);
+ const bool zero_gain = IsGainNearZero(gains_[i]) &&
+ IsGainNearZero(gain_processors_[i].GetGain());
+ if (!zero_gain) {
+ gain_processors_[i].ApplyGain(gains_[i], *delay_channel, delay_channel,
+ false /* accumulate_output */);
+ // Applies fast Ambisonic reflections encoding.
+ (*output)[0] += *delay_channel;
+ switch (i) {
+ case 0: /* left wall reflection */
+ (*output)[1] += *delay_channel;
+ break;
+ case 1: /* right wall reflection */
+ (*output)[1] -= *delay_channel;
+ break;
+ case 2: /* floor reflection */
+ (*output)[2] -= *delay_channel;
+ break;
+ case 3: /* ceiling reflection */
+ (*output)[2] += *delay_channel;
+ break;
+ case 4: /* front wall reflection */
+ (*output)[3] += *delay_channel;
+ break;
+ case 5: /* back wall reflection */
+ (*output)[3] -= *delay_channel;
+ break;
+ }
+ } else {
+ // Make sure the gain processor is initialized.
+ gain_processors_[i].Reset(0.0f);
+ }
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/reflections_processor.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/reflections_processor.h
new file mode 100644
index 000000000..1b9765b0f
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/reflections_processor.h
@@ -0,0 +1,128 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_REFLECTIONS_PROCESSOR_H_
+#define RESONANCE_AUDIO_DSP_REFLECTIONS_PROCESSOR_H_
+
+#include <utility>
+#include <vector>
+
+#include "api/resonance_audio_api.h"
+#include "base/audio_buffer.h"
+#include "base/misc_math.h"
+#include "dsp/delay_filter.h"
+#include "dsp/gain_processor.h"
+#include "dsp/mono_pole_filter.h"
+#include "dsp/reflection.h"
+#include "utils/buffer_crossfader.h"
+
+namespace vraudio {
+
+// Class that accepts single mono buffer as input and outputs a first order
+// ambisonic buffer of the mix of all the rooms early reflections computed for
+// the buffer.
+//
+// The input consists of a mono mix of all the sound objects in the system. The
+// reflections are assumed to be aligned with the aabb axes and thus the first
+// order ambisonic axes.
+class ReflectionsProcessor {
+ public:
+ // Constructs a |ReflectionsProcessor|.
+ //
+ // @param sample_rate System sampling rate.
+ // @param frames_per_buffer System frames per buffer.
+ ReflectionsProcessor(int sample_rate, size_t frames_per_buffer);
+
+ // Updates reflections according to the new |ReflectionProperties|.
+ //
+ // @param reflection_properties New reflection properties.
+ // @param listener_position New listener position.
+ void Update(const ReflectionProperties& reflection_properties,
+ const WorldPosition& listener_position);
+
+ // Processes a mono |AudioBuffer| into an ambisonic |AudioBuffer|.
+ //
+ // @param input Mono input buffer.
+ // @param output Ambisonic output buffer.
+ void Process(const AudioBuffer& input, AudioBuffer* output);
+
+ // Returns the number of frames required to keep processing on empty input
+ // signal. This value can be used to avoid any potential artifacts on the
+ // final output signal when the input signal stream is empty.
+ size_t num_frames_to_process_on_empty_input() const {
+ return num_frames_to_process_on_empty_input_;
+ }
+
+ private:
+ // Updates |gains_| and |delays_| vectors of the |ReflectionsProcessor|.
+ void UpdateGainsAndDelays();
+
+ // Applies |target_reflections_| and fast-encodes them into first order
+ // ambisonics.
+ //
+ // @param output Ambisonic output buffer.
+ void ApplyReflections(AudioBuffer* output);
+
+ // System sampling rate.
+ const int sample_rate_;
+
+ // System number of frames per buffer.
+ const size_t frames_per_buffer_;
+
+ // Maximum allowed delay time for a reflection (in samples).
+ const size_t max_delay_samples_;
+
+ // Low pass filter to be applied to input signal.
+ MonoPoleFilter low_pass_filter_;
+
+ // Audio buffer to store mono low pass filtered buffers during processing.
+ AudioBuffer temp_mono_buffer_;
+
+ // Audio buffers to store FOA reflections buffers during crossfading.
+ AudioBuffer current_reflection_buffer_;
+ AudioBuffer target_reflection_buffer_;
+
+ // Target reflections filled with new data when |Update| is called.
+ std::vector<Reflection> target_reflections_;
+
+ // Indicates whether relfections have been updated and a crossfade is needed.
+ bool crossfade_;
+
+ // Buffer crossfader to apply linear crossfade during reflections update.
+ BufferCrossfader crossfader_;
+
+ // Number of frames needed to keep processing on empty input signal.
+ size_t num_frames_to_process_on_empty_input_;
+
+ // Number of samples of delay to be applied for each reflection.
+ std::vector<size_t> delays_;
+
+ // Delay filter to delay the incoming buffer.
+ DelayFilter delay_filter_;
+
+ // Delay buffer used to store delayed reflections before scaling and encoding.
+ AudioBuffer delay_buffer_;
+
+ // Gains to be applied for each reflection.
+ std::vector<float> gains_;
+
+ // |GainProcessor|s to apply |gains_|.
+ std::vector<GainProcessor> gain_processors_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_REFLECTIONS_PROCESSOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/reflections_processor_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/reflections_processor_test.cc
new file mode 100644
index 000000000..ab1688e51
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/reflections_processor_test.cc
@@ -0,0 +1,142 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/reflections_processor.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "api/resonance_audio_api.h"
+#include "base/constants_and_types.h"
+#include "utils/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+const size_t kFramesPerBuffer = 512;
+const int kSampleRate = 48000;
+const float kRoomDimensions[3] = {2.0f, 2.0f, 2.0f};
+const size_t kExpectedDelaySamples = 279;
+
+} // namespace
+
+class ReflectionsProcessorTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ const float kReflectionCoefficients[kNumRoomSurfaces] = {0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f};
+ const WorldPosition kListenerPosition(0.0f, 0.0f, 0.0f);
+ std::copy(std::begin(kReflectionCoefficients),
+ std::end(kReflectionCoefficients),
+ std::begin(reflection_properties_.coefficients));
+ std::copy(std::begin(kRoomDimensions), std::end(kRoomDimensions),
+ std::begin(reflection_properties_.room_dimensions));
+ listener_position_ = kListenerPosition;
+ }
+
+ ReflectionProperties reflection_properties_;
+ WorldPosition listener_position_;
+}; // namespace vraudio
+
+// Tests that the processed output is delayed, filtered, and scaled as expected.
+TEST_F(ReflectionsProcessorTest, ProcessTest) {
+ ReflectionsProcessor reflections_processor(kSampleRate, kFramesPerBuffer);
+ reflections_processor.Update(reflection_properties_, listener_position_);
+
+ AudioBuffer input(kNumMonoChannels, kFramesPerBuffer);
+ AudioBuffer output(kNumFirstOrderAmbisonicChannels, kFramesPerBuffer);
+ // Process until we have transitioned to the specified Gain.
+ for (size_t i = 0; i < kUnitRampLength; i += kFramesPerBuffer) {
+ input.Clear();
+ output.Clear();
+ GenerateDiracImpulseFilter(0, &input[0]);
+ reflections_processor.Process(input, &output);
+ }
+
+ // The reflections calculated should be as follows:
+ // delay = 280 samples, magnitude = 0.25, direction = {azim 90, elev 0}.
+ // delay = 280 samples, magnitude = 0.25, direction = {azim -90, elev 0}.
+ // delay = 280 samples, magnitude = 0.25, direction = {azim 0, elev -90}.
+ // delay = 280 samples, magnitude = 0.25, direction = {azim 0, elev 90}.
+ // delay = 280 samples, magnitude = 0.25, direction = {azim 0, elev 0}.
+ // delay = 280 samples, magnitude = 0.25, direction = {azim 180, elev 0}.
+ const float kExpectedMagnitude = 0.25f;
+ // We expect the following ambisonic encoding coefficients:
+ // {azim 90, elev 0} = {1 1 0 0}.
+ // {azim -90, elev 0} = {1 -1 0 0}.
+ // {azim 0, elev -90} = {1 0 -1 0}.
+ // {azim 0, elev 90} = {1 0 1 0}.
+ // {azim 0, elev 0} = {1 0 0 1}.
+ // {azim 180, elev 0} = {1 0 0 -1}.
+ const std::vector<std::vector<float>> kEncodingCoefficients = {
+ {1.0f, 1.0f, 0.0f, 0.0f}, {1.0f, -1.0f, 0.0f, 0.0f},
+ {1.0f, 0.0f, -1.0f, 0.0f}, {1.0f, 0.0f, 1.0f, 0.0f},
+ {1.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f, -1.0f}};
+
+ for (size_t j = kExpectedDelaySamples; j < kFramesPerBuffer; ++j) {
+ for (size_t k = 0; k < kNumFirstOrderAmbisonicChannels; ++k) {
+ float coefficient_sum = 0.0f;
+ for (size_t i = 0; i < kEncodingCoefficients.size(); ++i) {
+ coefficient_sum += kEncodingCoefficients[i][k];
+ }
+ const float expected_sample = input[0][j - kExpectedDelaySamples] *
+ kExpectedMagnitude * coefficient_sum;
+ EXPECT_NEAR(expected_sample, output[k][j], kEpsilonFloat);
+ }
+ }
+}
+
+// Tests that when transitioning from a reflection with zero magnitude to one
+// with non-zero magnitude, there will be a steady incremental increase in
+// the "fade in".
+TEST_F(ReflectionsProcessorTest, CrossFadeTest) {
+ ReflectionsProcessor reflections_processor(kSampleRate, kFramesPerBuffer);
+
+ AudioBuffer input(kNumMonoChannels, kFramesPerBuffer);
+ AudioBuffer output(kNumFirstOrderAmbisonicChannels, kFramesPerBuffer);
+
+ input.Clear();
+ output.Clear();
+ for (size_t i = 0; i < kFramesPerBuffer; ++i) {
+ input[0][i] = 1.0f;
+ }
+
+ reflections_processor.Process(input, &output);
+ reflections_processor.Update(reflection_properties_, listener_position_);
+ reflections_processor.Process(input, &output);
+
+ // All the reflections are expected to arrive at the listener at the same
+ // time. That is why their directional contributions are expected to cancel
+ // out. Only in first channel we will see a steady increase.
+ for (size_t channel = 0; channel < kNumFirstOrderAmbisonicChannels;
+ ++channel) {
+ if (channel < kNumMonoChannels) {
+ for (size_t frame = kExpectedDelaySamples; frame < kFramesPerBuffer;
+ ++frame) {
+ EXPECT_TRUE(output[channel][frame] > output[channel][frame - 1]);
+ }
+ } else {
+ for (size_t frame = 0; frame < kFramesPerBuffer; ++frame) {
+ EXPECT_NEAR(output[channel][frame], 0.0f, kEpsilonFloat);
+ }
+ }
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/resampler.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/resampler.cc
new file mode 100644
index 000000000..e135afcdf
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/resampler.cc
@@ -0,0 +1,335 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "dsp/resampler.h"
+
+#include <functional>
+#include <numeric>
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/misc_math.h"
+#include "base/simd_utils.h"
+
+#include "dsp/utils.h"
+
+namespace vraudio {
+
+namespace {
+
+// (kTransitionBandwidthRatio / 2) * (sample_rate / cutoff_frequency)
+// = filter_length.
+// The value below was chosen empirically as a tradeoff between execution time
+// and filter rolloff wrt. cutoff frequency.
+const size_t kTransitionBandwidthRatio = 13;
+
+// Maximum number of channels internally based upon the maximum supported
+// ambisonic order.
+const size_t kMaxNumChannels =
+ (kMaxSupportedAmbisonicOrder + 1) * (kMaxSupportedAmbisonicOrder + 1);
+
+} // namespace
+
+Resampler::Resampler()
+ : up_rate_(0),
+ down_rate_(0),
+ time_modulo_up_rate_(0),
+ last_processed_sample_(0),
+ num_channels_(0),
+ coeffs_per_phase_(0),
+ transposed_filter_coeffs_(kNumMonoChannels, kMaxSupportedNumFrames),
+ temporary_filter_coeffs_(kNumMonoChannels, kMaxSupportedNumFrames),
+ state_(kMaxNumChannels, kMaxSupportedNumFrames) {
+ state_.Clear();
+}
+
+void Resampler::Process(const AudioBuffer& input, AudioBuffer* output) {
+
+ // See "Digital Signal Processing, 4th Edition, Prolakis and Manolakis,
+ // Pearson, Chapter 11 (specificaly Figures 11.5.10 and 11.5.13).
+ DCHECK_EQ(input.num_channels(), num_channels_);
+ const size_t input_length = input.num_frames();
+ DCHECK_GE(output->num_frames(), GetNextOutputLength(input_length));
+ DCHECK_LE(output->num_frames(), GetMaxOutputLength(input_length));
+ DCHECK_EQ(output->num_channels(), num_channels_);
+ output->Clear();
+
+ if (up_rate_ == down_rate_) {
+ *output = input;
+ return;
+ }
+
+ // |input_sample| is the last processed sample.
+ size_t input_sample = last_processed_sample_;
+ // |output_sample| is the output.
+ size_t output_sample = 0;
+
+ const auto& filter_coefficients = transposed_filter_coeffs_[0];
+
+ while (input_sample < input_length) {
+ size_t filter_index = time_modulo_up_rate_ * coeffs_per_phase_;
+ size_t offset_input_index = input_sample - coeffs_per_phase_ + 1;
+ const int offset = -static_cast<int>(offset_input_index);
+
+ if (offset > 0) {
+ // We will need to draw data from the |state_| buffer.
+ const int state_num_frames = static_cast<int>(coeffs_per_phase_ - 1);
+ int state_index = state_num_frames - offset;
+ while (state_index < state_num_frames) {
+ for (size_t channel = 0; channel < num_channels_; ++channel) {
+ (*output)[channel][output_sample] +=
+ state_[channel][state_index] * filter_coefficients[filter_index];
+ }
+ state_index++;
+ filter_index++;
+ }
+ // Move along by |offset| samples up as far as |input|.
+ offset_input_index += offset;
+ }
+
+ // We now move back to where |input_sample| "points".
+ while (offset_input_index <= input_sample) {
+ for (size_t channel = 0; channel < num_channels_; ++channel) {
+ (*output)[channel][output_sample] +=
+ input[channel][offset_input_index] *
+ filter_coefficients[filter_index];
+ }
+ offset_input_index++;
+ filter_index++;
+ }
+ output_sample++;
+
+ time_modulo_up_rate_ += down_rate_;
+ // Advance the input pointer.
+ input_sample += time_modulo_up_rate_ / up_rate_;
+ // Decide which phase of the polyphase filter to use next.
+ time_modulo_up_rate_ %= up_rate_;
+ }
+ DCHECK_GE(input_sample, input_length);
+ last_processed_sample_ = input_sample - input_length;
+
+ // Take care of the |state_| buffer.
+ const int samples_left_in_input =
+ static_cast<int>(coeffs_per_phase_) - 1 - static_cast<int>(input_length);
+ if (samples_left_in_input > 0) {
+ for (size_t channel = 0; channel < num_channels_; ++channel) {
+ // Copy end of the |state_| buffer to the beginning.
+ auto& state_channel = state_[channel];
+ DCHECK_GE(static_cast<int>(state_channel.size()), samples_left_in_input);
+ std::copy_n(state_channel.end() - samples_left_in_input,
+ samples_left_in_input, state_channel.begin());
+ // Then copy input to the end of the buffer.
+ std::copy_n(input[channel].begin(), input_length,
+ state_channel.end() - input_length);
+ }
+ } else {
+ for (size_t channel = 0; channel < num_channels_; ++channel) {
+ // Copy the last of the |input| samples into the |state_| buffer.
+ DCHECK_GT(coeffs_per_phase_, 0U);
+ DCHECK_GT(input[channel].size(), coeffs_per_phase_ - 1);
+ std::copy_n(input[channel].end() - (coeffs_per_phase_ - 1),
+ coeffs_per_phase_ - 1, state_[channel].begin());
+ }
+ }
+}
+
+size_t Resampler::GetMaxOutputLength(size_t input_length) const {
+ if (up_rate_ == down_rate_) {
+ return input_length;
+ }
+ DCHECK_GT(down_rate_, 0U);
+ // The + 1 takes care of the case where:
+ // (time_modulo_up_rate_ + up_rate_ * last_processed_sample_) <
+ // ((input_length * up_rate_) % down_rate_)
+ // The output length will be equal to the return value or the return value -1.
+ return (input_length * up_rate_) / down_rate_ + 1;
+}
+
+size_t Resampler::GetNextOutputLength(size_t input_length) const {
+ if (up_rate_ == down_rate_) {
+ return input_length;
+ }
+ const size_t max_length = GetMaxOutputLength(input_length);
+ if ((time_modulo_up_rate_ + up_rate_ * last_processed_sample_) >=
+ ((input_length * up_rate_) % down_rate_)) {
+ return max_length - 1;
+ }
+ return max_length;
+}
+
+void Resampler::SetRateAndNumChannels(int source_frequency,
+ int destination_frequency,
+ size_t num_channels) {
+
+ // Convert sampling rates to be relatively prime.
+ DCHECK_GT(source_frequency, 0);
+ DCHECK_GT(destination_frequency, 0);
+ DCHECK_GT(num_channels, 0U);
+ const int greatest_common_divisor =
+ FindGcd(destination_frequency, source_frequency);
+ const size_t destination =
+ static_cast<size_t>(destination_frequency / greatest_common_divisor);
+ const size_t source =
+ static_cast<size_t>(source_frequency / greatest_common_divisor);
+
+ // Obtain the size of the |state_| before |coeffs_per_phase_| is updated in
+ // |GenerateInterpolatingFilter()|.
+ const size_t old_state_size =
+ coeffs_per_phase_ > 0 ? coeffs_per_phase_ - 1 : 0;
+ if ((destination != up_rate_) || (source != down_rate_)) {
+ up_rate_ = destination;
+ down_rate_ = source;
+ if (up_rate_ == down_rate_) {
+ return;
+ }
+ // Create transposed multirate filters from sincs.
+ GenerateInterpolatingFilter(source_frequency);
+ // Reset the time variable as it may be longer than the new filter length if
+ // we switched from upsampling to downsampling via a call to SetRate().
+ time_modulo_up_rate_ = 0;
+ }
+
+ // Update the |state_| buffer.
+ if (num_channels_ != num_channels) {
+ num_channels_ = num_channels;
+ InitializeStateBuffer(old_state_size);
+ }
+}
+
+bool Resampler::AreSampleRatesSupported(int source, int destination) {
+ DCHECK_GT(source, 0);
+ DCHECK_GT(destination, 0);
+ // Determines whether sample rates are supported based upon whether our
+ // maximul filter lenhgth is big enough to hold the corresponding
+ // interpolation filter.
+ const int max_rate =
+ std::max(source, destination) / FindGcd(source, destination);
+ size_t filter_length = max_rate * kTransitionBandwidthRatio;
+ filter_length += filter_length % 2;
+ return filter_length <= kMaxSupportedNumFrames;
+}
+
+void Resampler::ResetState() {
+
+ time_modulo_up_rate_ = 0;
+ last_processed_sample_ = 0;
+ state_.Clear();
+}
+
+void Resampler::InitializeStateBuffer(size_t old_state_num_frames) {
+ // Update the |state_| buffer if it is null or if the number of coefficients
+ // per phase in the polyphase filter has changed.
+ if (up_rate_ == down_rate_ || num_channels_ == 0) {
+ return;
+ }
+ // If the |state_| buffer is to be kept. For example in the case of a change
+ // in either source or destination sampling rate, maintaining the old |state_|
+ // buffers contents allows a glitch free transition.
+ const size_t new_state_num_frames =
+ coeffs_per_phase_ > 0 ? coeffs_per_phase_ - 1 : 0;
+ if (old_state_num_frames != new_state_num_frames) {
+ const size_t min_size =
+ std::min(new_state_num_frames, old_state_num_frames);
+ const size_t max_size =
+ std::max(new_state_num_frames, old_state_num_frames);
+ for (size_t channel = 0; channel < num_channels_; ++channel) {
+ auto& state_channel = state_[channel];
+ DCHECK_LT(state_channel.begin() + max_size, state_channel.end());
+ std::fill(state_channel.begin() + min_size,
+ state_channel.begin() + max_size, 0.0f);
+ }
+ }
+}
+
+void Resampler::GenerateInterpolatingFilter(int sample_rate) {
+ // See "Digital Signal Processing, 4th Edition, Prolakis and Manolakis,
+ // Pearson, Chapter 11 (specificaly Figures 11.5.10 and 11.5.13).
+ const size_t max_rate = std::max(up_rate_, down_rate_);
+ const float cutoff_frequency =
+ static_cast<float>(sample_rate) / static_cast<float>(2 * max_rate);
+ size_t filter_length = max_rate * kTransitionBandwidthRatio;
+ filter_length += filter_length % 2;
+ auto* filter_channel = &temporary_filter_coeffs_[0];
+ filter_channel->Clear();
+ GenerateSincFilter(cutoff_frequency, static_cast<float>(sample_rate),
+ filter_length, filter_channel);
+
+ // Pad out the filter length so that it can be arranged in polyphase fashion.
+ const size_t transposed_length =
+ filter_length + max_rate - (filter_length % max_rate);
+ coeffs_per_phase_ = transposed_length / max_rate;
+ ArrangeFilterAsPolyphase(filter_length, *filter_channel);
+}
+
+void Resampler::ArrangeFilterAsPolyphase(size_t filter_length,
+ const AudioBuffer::Channel& filter) {
+ // Coefficients are transposed and flipped.
+ // Suppose |up_rate_| is 3, and the input number of coefficients is 10,
+ // h[0], ..., h[9].
+ // Then the |transposed_filter_coeffs_| buffer will look like this:
+ // h[9], h[6], h[3], h[0], flipped phase 0 coefs.
+ // 0, h[7], h[4], h[1], flipped phase 1 coefs (zero-padded).
+ // 0, h[8], h[5], h[2], flipped phase 2 coefs (zero-padded).
+ transposed_filter_coeffs_.Clear();
+ auto& transposed_coefficients_channel = transposed_filter_coeffs_[0];
+ for (size_t i = 0; i < up_rate_; ++i) {
+ for (size_t j = 0; j < coeffs_per_phase_; ++j) {
+ if (j * up_rate_ + i < filter_length) {
+ const size_t coeff_index =
+ (coeffs_per_phase_ - 1 - j) + i * coeffs_per_phase_;
+ transposed_coefficients_channel[coeff_index] = filter[j * up_rate_ + i];
+ }
+ }
+ }
+}
+
+void Resampler::GenerateSincFilter(float cutoff_frequency, float sample_rate,
+ size_t filter_length,
+ AudioBuffer::Channel* buffer) {
+
+ DCHECK_GT(sample_rate, 0.0f);
+ const float angular_cutoff_frequency =
+ kTwoPi * cutoff_frequency / sample_rate;
+
+ const size_t half_filter_length = filter_length / 2;
+ GenerateHannWindow(true /* Full Window */, filter_length, buffer);
+ auto* buffer_channel = &buffer[0];
+
+ for (size_t i = 0; i < filter_length; ++i) {
+ if (i == half_filter_length) {
+ (*buffer_channel)[half_filter_length] *= angular_cutoff_frequency;
+ } else {
+ const float denominator =
+ static_cast<float>(i) - (static_cast<float>(filter_length) / 2.0f);
+ DCHECK_GT(std::abs(denominator), kEpsilonFloat);
+ (*buffer_channel)[i] *=
+ std::sin(angular_cutoff_frequency * denominator) / denominator;
+ }
+ }
+ // Normalize.
+ const float normalizing_factor =
+ static_cast<float>(up_rate_) /
+ std::accumulate(buffer_channel->begin(), buffer_channel->end(), 0.0f);
+ ScalarMultiply(filter_length, normalizing_factor, buffer_channel->begin(),
+ buffer_channel->begin());
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/resampler.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/resampler.h
new file mode 100644
index 000000000..c2c079a80
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/resampler.h
@@ -0,0 +1,135 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_RESAMPLER_H_
+#define RESONANCE_AUDIO_DSP_RESAMPLER_H_
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Class that provides rational resampling of audio data.
+class Resampler {
+ public:
+ Resampler();
+
+ // Resamples an |AudioBuffer| of input data sampled at |source_frequency| to
+ // |destination_frequency|.
+ //
+ // @param input Input data to be resampled.
+ // @param output Resampled output data.
+ void Process(const AudioBuffer& input, AudioBuffer* output);
+
+ // Returns the maximum length which the output buffer will be, given the
+ // current source and destination frequencies and input length. The actual
+ // output length will either be this or one less.
+ //
+ // @param input_length Length of the input.
+ // @return Maximum length of the output.
+ size_t GetMaxOutputLength(size_t input_length) const;
+
+ // Returns the next length which the output buffer will be, given the
+ // current source and destination frequencies and input length.
+ //
+ // @param input_length Length of the input.
+ // @return Next length of the output.
+ size_t GetNextOutputLength(size_t input_length) const;
+
+ // Sets the source and destination sampling rate as well as the number of
+ // channels. Note this method only resets the filter state number of channel
+ // changes.
+ //
+ // @param source_frequency Sampling rate of input data.
+ // @param destination_frequency Desired output sampling rate.
+ // @param num_channels Number of channels to process.
+ void SetRateAndNumChannels(int source_frequency, int destination_frequency,
+ size_t num_channels);
+
+ // Returns whether the sampling rates provided are supported by the resampler.
+ //
+ // @param source Source sampling rate.
+ // @param destination Destination sampling rate.
+ // @return True if the sampling rate pair are supported.
+ static bool AreSampleRatesSupported(int source, int destination);
+
+ // Resets the inner state of the |Resampler| allowing its use repeatedly on
+ // different data streams.
+ void ResetState();
+
+ private:
+ friend class PolyphaseFilterTest;
+ // Initializes the |state_| buffer. Called when sampling rate is changed or
+ // the state is reset.
+ //
+ // @param size_t old_state_num_frames Number of frames in the |state_| buffer
+ // previous to the most recent call to |GenerateInterpolatingFilter|.
+ void InitializeStateBuffer(size_t old_state_num_frames);
+
+ // Generates a windowed sinc to act as the interpolating/anti-aliasing filter.
+ //
+ // @param sample_rate The system sampling rate.
+ void GenerateInterpolatingFilter(int sample_rate);
+
+ // Arranges the anti aliasing filter coefficients in polyphase filter format.
+ //
+ // @param filter_length Number of frames in |filter| containing filter
+ // coefficients.
+ // @param filter Vector of filter coefficients.
+ void ArrangeFilterAsPolyphase(size_t filter_length,
+ const AudioBuffer::Channel& filter);
+
+ // Generates Hann windowed sinc function anti aliasing filters.
+ //
+ // @param cutoff_frequency Transition band (-3dB) frequency of the filter.
+ // @param sample_rate The system sampling rate.
+ // @param filter_length Number of frames in |buffer| containing filter
+ // coefficients.
+ // @param buffer |AudioBuffer::Channel| to contain the filter coefficients.
+ void GenerateSincFilter(float cutoff_frequency, float sample_rate,
+ size_t filter_length, AudioBuffer::Channel* buffer);
+
+ // Rate of the interpolator section of the rational sampling rate converter.
+ size_t up_rate_;
+
+ // Rate of the decimator section of the rational sampling rate convereter.
+ size_t down_rate_;
+
+ // Time variable for the polyphase filter.
+ size_t time_modulo_up_rate_;
+
+ // Marks the last processed sample of the input.
+ size_t last_processed_sample_;
+
+ // Number of channels in the |AudioBuffer|s processed.
+ size_t num_channels_;
+
+ // Number of filter coefficients in each phase of the polyphase filter.
+ size_t coeffs_per_phase_;
+
+ // Filter coefficients stored in polyphase form.
+ AudioBuffer transposed_filter_coeffs_;
+
+ // Filter coefficients in planar form, used for calculating the transposed
+ // filter.
+ AudioBuffer temporary_filter_coeffs_;
+
+ // Buffer holding the samples of input required between input buffers.
+ AudioBuffer state_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_RESAMPLER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/resampler_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/resampler_test.cc
new file mode 100644
index 000000000..5b23de58f
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/resampler_test.cc
@@ -0,0 +1,325 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/resampler.h"
+
+#include <numeric>
+#include <utility>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "utils/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+const int kToneFrequency = 1000;
+
+const size_t kInputBufferLength = 441;
+
+const int kSourceDataSampleRate = 44100;
+const int kUpDestinationDataSampleRate = 48000;
+const int kDownDestinationDataSampleRate = 24000;
+
+TEST(ResamplerTest, UpSampleTest) {
+ // Create input buffer with 1000Hz sine wave at 44100Hz, 441 samples long.
+ // This should be ten periods of a sine wave.
+ AudioBuffer input(kNumMonoChannels, kInputBufferLength);
+ GenerateSineWave(kToneFrequency, kSourceDataSampleRate, &input[0]);
+
+ Resampler resampler;
+ resampler.SetRateAndNumChannels(
+ kSourceDataSampleRate, kUpDestinationDataSampleRate, kNumMonoChannels);
+ AudioBuffer output(kNumMonoChannels,
+ resampler.GetNextOutputLength(kInputBufferLength));
+ resampler.Process(input, &output);
+
+ // Ensure the length of the output is as expected.
+ const size_t input_length_proportion =
+ kSourceDataSampleRate / kInputBufferLength;
+ const size_t expected_output_length =
+ kUpDestinationDataSampleRate / input_length_proportion;
+ EXPECT_EQ(expected_output_length, output.num_frames());
+
+ // Ensure the output also contains 10 periods of a sine wave, confirming it is
+ // still at 1000Hz given the new sampling rate.
+ AudioBuffer thresholded_output(kNumMonoChannels, output.num_frames());
+ for (size_t i = 0; i < output.num_frames(); ++i) {
+ thresholded_output[0][i] = std::abs(output[0][i]) < 0.5f ? 0.0f : 1.0f;
+ }
+ size_t num_square_wave_corners = 1;
+ // We have now generated a square wave with twice the number of corners as
+ // there are zero crossings.
+ for (size_t i = 0; i < output.num_frames() - 1; ++i) {
+ if (thresholded_output[0][i] != thresholded_output[0][i + 1]) {
+ num_square_wave_corners++;
+ }
+ }
+ const size_t zerocross_count = num_square_wave_corners / 2;
+
+ const size_t num_expected_zero_crossings =
+ 2 * kToneFrequency / (kSourceDataSampleRate / kInputBufferLength);
+ EXPECT_EQ(num_expected_zero_crossings, zerocross_count);
+}
+
+TEST(ResamplerTest, DownSampleTest) {
+ // Create input buffer with 1000Hz sine wave at 44100Hz, 441 samples long.
+ // This should be ten periods of a sine wave.
+ AudioBuffer input(kNumMonoChannels, kInputBufferLength);
+ GenerateSineWave(kToneFrequency, kSourceDataSampleRate, &input[0]);
+
+ Resampler resampler;
+ resampler.SetRateAndNumChannels(
+ kSourceDataSampleRate, kDownDestinationDataSampleRate, kNumMonoChannels);
+ AudioBuffer output(kNumMonoChannels,
+ resampler.GetNextOutputLength(kInputBufferLength));
+ resampler.Process(input, &output);
+
+ // Ensure the length of the output is as expected.
+ const size_t input_length_proportion =
+ kSourceDataSampleRate / kInputBufferLength;
+ const size_t expected_output_length =
+ kDownDestinationDataSampleRate / input_length_proportion;
+ EXPECT_EQ(expected_output_length, output.num_frames());
+
+ // Ensure the output also contains one period of a sine wave, confirming it is
+ // still at 100Hz given the new sampling rate.
+ AudioBuffer thresholded_output(kNumMonoChannels, output.num_frames());
+ for (size_t i = 0; i < output.num_frames(); ++i) {
+ thresholded_output[0][i] = std::abs(output[0][i]) < 0.5f ? 0.0f : 1.0f;
+ }
+ int zero_cross_count = 1;
+ for (size_t i = 0; i < output.num_frames() - 1; ++i) {
+ if (thresholded_output[0][i] != thresholded_output[0][i + 1]) {
+ zero_cross_count++;
+ }
+ }
+ const int num_expected_zero_crossings =
+ 4 * kToneFrequency / (kSourceDataSampleRate / kInputBufferLength);
+ EXPECT_EQ(num_expected_zero_crossings, zero_cross_count);
+}
+
+TEST(ResamplerTest, ResetStateTest) {
+ // Create input buffer with 1000Hz sine wave at 44100Hz, 441 samples long.
+ // This should be ten periods of a sine wave.
+ AudioBuffer input(kNumMonoChannels, kInputBufferLength);
+ GenerateSineWave(kToneFrequency, kSourceDataSampleRate, &input[0]);
+
+ Resampler resampler;
+ resampler.SetRateAndNumChannels(
+ kSourceDataSampleRate, kUpDestinationDataSampleRate, kNumMonoChannels);
+ AudioBuffer output_1(kNumMonoChannels,
+ resampler.GetNextOutputLength(kInputBufferLength));
+ resampler.Process(input, &output_1);
+
+ resampler.ResetState();
+ AudioBuffer output_2(kNumMonoChannels,
+ resampler.GetNextOutputLength(kInputBufferLength));
+ resampler.Process(input, &output_2);
+
+ // If the clearing of the resampler worked properly there should be no
+ // internal state between process calls and thus both outputs should be
+ // identical.
+ for (size_t sample = 0; sample < output_1.num_frames(); ++sample) {
+ EXPECT_NEAR(output_1[0][sample], output_2[0][sample], kEpsilonFloat);
+ }
+}
+
+TEST(ResamplerTest, TwoChannelTest) {
+ AudioBuffer input(kNumStereoChannels, kInputBufferLength);
+ GenerateSineWave(kToneFrequency, kSourceDataSampleRate, &input[0]);
+ GenerateSineWave(2 * kToneFrequency, kSourceDataSampleRate, &input[1]);
+
+ Resampler resampler;
+ resampler.SetRateAndNumChannels(
+ kSourceDataSampleRate, kUpDestinationDataSampleRate, kNumStereoChannels);
+ AudioBuffer output(kNumStereoChannels,
+ resampler.GetNextOutputLength(kInputBufferLength));
+ resampler.Process(input, &output);
+
+ AudioBuffer thresholded_output(kNumStereoChannels, output.num_frames());
+ for (size_t i = 0; i < output.num_frames(); ++i) {
+ thresholded_output[0][i] = std::abs(output[0][i]) < 0.5f ? 0.0f : 1.0f;
+ thresholded_output[1][i] = std::abs(output[1][i]) < 0.5f ? 0.0f : 1.0f;
+ }
+ int channel_0_zero_cross_count = 1;
+ int channel_1_zero_cross_count = 1;
+ for (size_t i = 0; i < output.num_frames() - 1; ++i) {
+ if (thresholded_output[0][i] != thresholded_output[0][i + 1]) {
+ channel_0_zero_cross_count++;
+ }
+ if (thresholded_output[1][i] != thresholded_output[1][i + 1]) {
+ channel_1_zero_cross_count++;
+ }
+ }
+ const int channel_0_expected_zero_crossings =
+ 4 * kToneFrequency / (kSourceDataSampleRate / kInputBufferLength);
+ const int channel_1_expected_zero_crossings =
+ 8 * kToneFrequency / (kSourceDataSampleRate / kInputBufferLength);
+ EXPECT_EQ(channel_0_zero_cross_count, channel_0_expected_zero_crossings);
+ EXPECT_EQ(channel_1_zero_cross_count, channel_1_expected_zero_crossings);
+}
+
+TEST(ResamplerTest, DiracImpulseUpsampleTest) {
+ const size_t kInputSignalSize = 128;
+ AudioBuffer input_signal(kNumMonoChannels, kInputSignalSize);
+ GenerateDiracImpulseFilter(kInputSignalSize / 2, &input_signal[0]);
+
+ const int kSourceSamplingRate = 100;
+ const int kDestinationSamplingRate = 200;
+
+ Resampler resampler;
+ resampler.SetRateAndNumChannels(kSourceSamplingRate, kDestinationSamplingRate,
+ kNumMonoChannels);
+
+ AudioBuffer output_signal(kNumMonoChannels,
+ resampler.GetNextOutputLength(kInputSignalSize));
+ const int resampling_factor = kDestinationSamplingRate / kSourceSamplingRate;
+ EXPECT_EQ(kInputSignalSize * resampling_factor, output_signal.num_frames());
+ // Perform resampling. Dirac impulse position should shift according to
+ // "resampling_factor".
+ resampler.Process(input_signal, &output_signal);
+ EXPECT_EQ(kInputSignalSize * resampling_factor, output_signal.num_frames());
+
+ DelayCompare(input_signal[0], output_signal[0], kInputSignalSize / 2,
+ kEpsilonFloat);
+}
+
+TEST(ResamplerTest, GetNextOutputLengthTest) {
+ const size_t input_length = 10;
+ Resampler resampler;
+ resampler.SetRateAndNumChannels(kSourceDataSampleRate,
+ kSourceDataSampleRate / 2, kNumMonoChannels);
+ // In this case the source rate is twice the destination rate, we can expect
+ // the output length to be half the input_length.
+
+ resampler.SetRateAndNumChannels(kSourceDataSampleRate,
+ kSourceDataSampleRate * 2, kNumMonoChannels);
+ // In this case the source rate is half the destination rate, we can
+ // expect the output length to be twice the input_length.
+ EXPECT_EQ(input_length * 2, resampler.GetNextOutputLength(input_length));
+}
+
+TEST(Resampler, AreSampleRatesSupportedTest) {
+ const size_t kNumPairs = 6;
+ const int kSourceRates[kNumPairs] = {4000, 16000, 44100, 96000, 41999, 48000};
+ const int kDestRates[kNumPairs] = {96000, 44100, 48000, 32000, 44100, 43210};
+ const bool kExpectedResults[kNumPairs] = {true, true, true,
+ true, false, false};
+
+ // We generally support only rates with relatively high GCD.
+ for (size_t i = 0; i < kNumPairs; ++i) {
+ const bool result =
+ Resampler::AreSampleRatesSupported(kSourceRates[i], kDestRates[i]);
+ EXPECT_EQ(result, kExpectedResults[i]);
+ }
+}
+
+class OutputLengthTest : public ::testing::TestWithParam<std::pair<int, int>> {
+};
+
+// Tests that the lengths returned by the |Process| method are always equal to
+// the length returned by |GetMaxOutputLength|, or one sample less.
+TEST_P(OutputLengthTest, OutputLengthTest) {
+ const size_t kInputSize = 512;
+ Resampler resampler;
+ const auto rates = GetParam();
+ resampler.SetRateAndNumChannels(rates.first, rates.second, kNumMonoChannels);
+ AudioBuffer input(kNumMonoChannels, kInputSize);
+ const size_t max_output_length = resampler.GetMaxOutputLength(kInputSize);
+ AudioBuffer output(kNumMonoChannels, max_output_length);
+
+ // Process 100 times and expect the lengts to be maximum length or one less.
+ const size_t kTimesToProcess = 100;
+ for (size_t i = 0; i < kTimesToProcess; ++i) {
+ const size_t next_output_length = resampler.GetNextOutputLength(kInputSize);
+ resampler.Process(input, &output);
+ EXPECT_LE(next_output_length, max_output_length);
+ EXPECT_LE(max_output_length - next_output_length, 1U);
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(RatePairs, OutputLengthTest,
+ ::testing::Values(std::make_pair(44100, 48000),
+ std::make_pair(48000, 44100)));
+
+} // namespace
+
+class PolyphaseFilterTest : public ::testing::Test {
+ protected:
+ PolyphaseFilterTest() {}
+ // Virtual methods from ::testing::Test
+ ~PolyphaseFilterTest() override {}
+ void SetUp() override {}
+ void TearDown() override {}
+
+ std::unique_ptr<AudioBuffer> GetPolyphaseFilter(
+ const AudioBuffer::Channel& filter_coefficients, int coeffs_per_phase,
+ int up_rate) {
+ // Create a resampler with arbitrary source and destination sampling rates.
+ Resampler resampler;
+ resampler.SetRateAndNumChannels(kSourceDataSampleRate,
+ kSourceDataSampleRate, kNumMonoChannels);
+ resampler.up_rate_ = up_rate;
+ resampler.transposed_filter_coeffs_.Clear();
+ resampler.coeffs_per_phase_ = coeffs_per_phase;
+ resampler.ArrangeFilterAsPolyphase(filter_coefficients.size(),
+ filter_coefficients);
+ std::unique_ptr<AudioBuffer> polyphase_filter(new AudioBuffer());
+ *polyphase_filter = resampler.transposed_filter_coeffs_;
+ return polyphase_filter;
+ }
+};
+
+TEST_F(PolyphaseFilterTest, CorrectPolyphaseFilterTest) {
+ // Choose an uprate which is a factor of the filters length.
+ const int filter_length = 24;
+ const int uprate = 4;
+ const int phase_length = filter_length / uprate;
+
+ // Create a vector of ascending numbers (1:24).
+ AudioBuffer ascending(kNumMonoChannels, filter_length);
+ ascending.Clear();
+ std::iota(ascending[0].begin(), ascending[0].end(), 1.0f);
+
+ std::unique_ptr<AudioBuffer> output =
+ GetPolyphaseFilter(ascending[0], phase_length, uprate);
+
+ // In polyphase format, the filter coefficients can be thought of as a matrix
+ // with uprate columns. The last uprate coeffecients are in the first row and
+ // the first uprate coefficients are in the last row. This is stored in a
+ // vector, column by column.
+ //
+ // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
+ // becomes:
+ // 21 22 23 24
+ // 17 18 19 20
+ // 13 14 15 16
+ // 9 10 11 12
+ // 5 6 7 8
+ // 1 2 3 4
+ size_t index = 0;
+ for (int phase = uprate; phase > 0; --phase) {
+ for (int value = filter_length - phase + 1; value > uprate - phase;
+ value -= uprate) {
+ EXPECT_EQ(static_cast<float>(value), (*output)[0][index++]);
+ }
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_compensator.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_compensator.cc
new file mode 100644
index 000000000..2996d3799
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_compensator.cc
@@ -0,0 +1,214 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/reverb_onset_compensator.h"
+
+#include <algorithm>
+#include <cmath>
+#include <iterator>
+
+#include "base/constants_and_types.h"
+#include "dsp/spectral_reverb_constants_and_tables.h"
+#include "dsp/utils.h"
+
+namespace vraudio {
+
+namespace {
+
+// Number of reverb updaters. Twelve were chosen as this represents one update
+// per buffer length at 24kHz, a number we are very unlikely to exceed.
+const size_t kNumReverbUpdaters = 12;
+
+} // namespace
+
+ReverbOnsetCompensator::ReverbOnsetCompensator(int sampling_rate,
+ size_t frames_per_buffer,
+ FftManager* fft_manager)
+ : fft_manager_(fft_manager),
+ sampling_rate_(sampling_rate),
+ frames_per_buffer_(frames_per_buffer),
+ base_curves_(kNumStereoChannels, kCorrectionCurveLength),
+ adder_curves_(kNumStereoChannels, kCorrectionCurveLength),
+ left_filter_(CeilToMultipleOfFramesPerBuffer(kCorrectionCurveLength,
+ frames_per_buffer_),
+ frames_per_buffer_, fft_manager_),
+ right_filter_(CeilToMultipleOfFramesPerBuffer(kCorrectionCurveLength,
+ frames_per_buffer_),
+ frames_per_buffer_, fft_manager_),
+ delay_filter_(CeilToMultipleOfFramesPerBuffer(kCorrectionCurveLength,
+ frames_per_buffer_),
+ frames_per_buffer_),
+ num_active_processors_(0),
+ temp_kernel_buffer_(kNumStereoChannels, frames_per_buffer_),
+ temp_freq_buffer_(kNumMonoChannels, fft_manager_->GetFftSize()) {
+ CHECK(fft_manager_);
+ DCHECK_GT(sampling_rate_, 0);
+ DCHECK_GT(frames_per_buffer_, 0U);
+
+ temp_kernel_buffer_.Clear();
+ temp_freq_buffer_.Clear();
+
+ GenerateNoiseVectors();
+ GenerateCorrectionCurves();
+
+ // Insert reverb updaters.
+ for (size_t i = 0; i < kNumReverbUpdaters; ++i) {
+ update_processors_.emplace_front(new ReverbOnsetUpdateProcessor(
+ frames_per_buffer_, sampling_rate_, &base_curves_, &adder_curves_));
+ }
+}
+
+void ReverbOnsetCompensator::Process(const AudioBuffer& input,
+ AudioBuffer* output) {
+ DCHECK(output);
+ DCHECK_EQ(kNumMonoChannels, input.num_channels());
+ DCHECK_EQ(frames_per_buffer_, input.num_frames());
+ DCHECK_EQ(kNumStereoChannels, output->num_channels());
+ DCHECK_EQ(frames_per_buffer_, output->num_frames());
+
+ delay_filter_.InsertData(input[0]);
+ delay_filter_.GetDelayedData(kCompensationOnsetLength, &(*output)[0]);
+
+ // Process reverb updates.
+ AudioBuffer::Channel* kernel_channel_left = &temp_kernel_buffer_[0];
+ AudioBuffer::Channel* kernel_channel_right = &temp_kernel_buffer_[1];
+
+ size_t processor_index = 0;
+ while (processor_index < num_active_processors_) {
+ auto current_processor = update_processors_.begin();
+ std::advance(current_processor, processor_index);
+ const size_t partition_index =
+ (*current_processor)->GetCurrentPartitionIndex();
+ if ((*current_processor)
+ ->Process(bandpassed_noise_left_, bandpassed_noise_right_,
+ kernel_channel_left, kernel_channel_right)) {
+ left_filter_.ReplacePartition(partition_index, *kernel_channel_left);
+ right_filter_.ReplacePartition(partition_index, *kernel_channel_right);
+ ++processor_index;
+ } else {
+ // Update of the |current_processor| is finished, move it to the end of
+ // the list and reduce the number of active processors.
+ update_processors_.splice(update_processors_.end(), update_processors_,
+ current_processor);
+ --num_active_processors_;
+ }
+ }
+
+ // Filter the input (Using the output buffer due to the delay operation).
+ fft_manager_->FreqFromTimeDomain((*output)[0], &temp_freq_buffer_[0]);
+
+ left_filter_.Filter(temp_freq_buffer_[0]);
+ right_filter_.Filter(temp_freq_buffer_[0]);
+
+ left_filter_.GetFilteredSignal(&(*output)[0]);
+ right_filter_.GetFilteredSignal(&(*output)[1]);
+}
+
+void ReverbOnsetCompensator::Update(const float* rt60_values, float gain) {
+ DCHECK(rt60_values);
+ // Reset a reverb update processor from the end of the list and place it at
+ // the front. If the list is full, rotate the list and reuse the oldest active
+ // processor.
+ std::list<std::unique_ptr<ReverbOnsetUpdateProcessor>>::iterator
+ new_processor;
+ if (num_active_processors_ < kNumReverbUpdaters) {
+ new_processor = update_processors_.end();
+ std::advance(new_processor, -1);
+ } else {
+ new_processor = update_processors_.begin();
+ }
+
+ (*new_processor)->SetReverbTimes(rt60_values);
+ (*new_processor)->SetGain(gain);
+
+ if (new_processor != update_processors_.begin()) {
+ auto list_item = update_processors_.begin();
+ std::advance(list_item, num_active_processors_);
+ if (list_item != new_processor) {
+ update_processors_.splice(list_item, update_processors_, new_processor,
+ std::next(new_processor));
+ }
+ ++num_active_processors_;
+ } else {
+ std::rotate(update_processors_.begin(),
+ std::next(update_processors_.begin()),
+ update_processors_.end());
+ }
+}
+
+void ReverbOnsetCompensator::GenerateCorrectionCurves() {
+ // Copy into the adder curves such that the memory is aligned.
+ std::copy(kLowCorrectionCurve, kLowCorrectionCurve + kCorrectionCurveLength,
+ adder_curves_[0].begin());
+ std::copy(kHighCorrectionCurve, kHighCorrectionCurve + kCorrectionCurveLength,
+ adder_curves_[1].begin());
+
+ // Evaluate the polynomials to generate the base curves. Here the 'low' and
+ // 'high' names refer to the reverberation times.
+ AudioBuffer::Channel* low_channel = &base_curves_[0];
+ AudioBuffer::Channel* high_channel = &base_curves_[1];
+ for (size_t i = 0; i < kCorrectionCurveLength; ++i) {
+ // Scaled independent variable (Allowed better conditioning).
+ const float conditioning_scalar =
+ (static_cast<float>(i) - kCurveOffset) * kCurveScale;
+ (*low_channel)[i] = kLowReverberationCorrectionCurve[0];
+ (*high_channel)[i] = kHighReverberationCorrectionCurve[0];
+ float power = conditioning_scalar;
+ for (size_t k = 1; k < kCurvePolynomialLength; ++k) {
+ (*low_channel)[i] += power * kLowReverberationCorrectionCurve[k];
+ (*high_channel)[i] += power * kHighReverberationCorrectionCurve[k];
+ power *= conditioning_scalar;
+ }
+ (*low_channel)[i] = std::max((*low_channel)[i], 0.0f);
+ (*high_channel)[i] = std::max((*high_channel)[i], 0.0f);
+ }
+}
+
+void ReverbOnsetCompensator::GenerateNoiseVectors() {
+ const size_t num_octave_bands = GetNumReverbOctaveBands(sampling_rate_);
+ const size_t noise_length = CeilToMultipleOfFramesPerBuffer(
+ kCorrectionCurveLength, frames_per_buffer_);
+ for (size_t band = 0; band < num_octave_bands; ++band) {
+ // Generate preset tail.
+ bandpassed_noise_left_.emplace_back(kNumMonoChannels, noise_length);
+ GenerateBandLimitedGaussianNoise(kOctaveBandCentres[band], sampling_rate_,
+ /*seed=*/1U,
+ &bandpassed_noise_left_[band]);
+ bandpassed_noise_right_.emplace_back(kNumMonoChannels, noise_length);
+ GenerateBandLimitedGaussianNoise(kOctaveBandCentres[band], sampling_rate_,
+ /*seed=*/2U,
+ &bandpassed_noise_right_[band]);
+
+ auto min_max = std::minmax_element(bandpassed_noise_left_[band][0].begin(),
+ bandpassed_noise_left_[band][0].end());
+ const float left_scale =
+ std::max(std::fabs(*min_max.first), std::fabs(*min_max.second));
+ min_max = std::minmax_element(bandpassed_noise_right_[band][0].begin(),
+ bandpassed_noise_right_[band][0].end());
+ const float right_scale =
+ std::max(std::fabs(*min_max.first), std::fabs(*min_max.second));
+
+ const float scale = std::max(left_scale, right_scale);
+
+ ScalarMultiply(noise_length, scale, bandpassed_noise_left_[band][0].begin(),
+ bandpassed_noise_left_[band][0].begin());
+ ScalarMultiply(noise_length, scale,
+ bandpassed_noise_right_[band][0].begin(),
+ bandpassed_noise_right_[band][0].begin());
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_compensator.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_compensator.h
new file mode 100644
index 000000000..4bf76f8f2
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_compensator.h
@@ -0,0 +1,110 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_REVERB_ONSET_COMPENSATOR_H_
+#define RESONANCE_AUDIO_DSP_REVERB_ONSET_COMPENSATOR_H_
+
+#include <list>
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "dsp/delay_filter.h"
+#include "dsp/fft_manager.h"
+#include "dsp/partitioned_fft_filter.h"
+#include "dsp/reverb_onset_update_processor.h"
+
+namespace vraudio {
+
+// Implements a convolutional compensator for the spectral reverb onset curve.
+class ReverbOnsetCompensator {
+ public:
+ // Constructs a |ReverbOnsetCompensator|.
+ //
+ // @param sampling_rate The sampling rate in Hz.
+ // @param frames_per_buffer The number of frames per buffer in the system.
+ // @param fft_manager Pointer to a FftManager to perform FFT transformations.
+ ReverbOnsetCompensator(int sampling_rate, size_t frames_per_buffer,
+ FftManager* fft_manager);
+
+ // Resets the reverb with a new set of reverberation times. The new tail is
+ // generated by replacing the current tail buffer by buffer.
+ //
+ // @param rt60_values |kNumReverbOctaveBands| values denoting the
+ // reverberation decay time to -60dB in octave bands starting at
+ // |kLowestOctaveBand|.
+ // @param gain Gain to be applied across all frequencies.
+ void Update(const float* rt60_values, float gain);
+
+ // Processes a mono |AudioBuffer| with a reverberant tail.
+ //
+ // @param input A mono |AudioBuffer| of input data.
+ // @param output Pointer to stereo output buffer.
+ void Process(const AudioBuffer& input, AudioBuffer* output);
+
+ private:
+ // Generates the constituent curves which are combined to make up the
+ // correction curve envelopes. These envelopes ensure the initial part of the
+ // specral reverb's impulse response, which exhibits 'growing' behaviour,
+ // follows the desired exponential decay.
+ void GenerateCorrectionCurves();
+
+ // Generates a pair of |kNumOctaveBands| band, octave filtered, noise buffers.
+ void GenerateNoiseVectors();
+
+ // Manager for all FFT related functionality (not owned).
+ FftManager* const fft_manager_;
+
+ // The system sampling rate.
+ const int sampling_rate_;
+
+ // The system number of frames per buffer.
+ const size_t frames_per_buffer_;
+
+ // Pre-generated band-passed noise to be used as a base for the reverb tail.
+ std::vector<AudioBuffer> bandpassed_noise_left_;
+ std::vector<AudioBuffer> bandpassed_noise_right_;
+
+ // The constituent curves used to generate the onset compensation envelopes.
+ AudioBuffer base_curves_;
+ AudioBuffer adder_curves_;
+
+ // Filter for processing the left reverberant channel.
+ PartitionedFftFilter left_filter_;
+
+ // Filter for processing the right reverberant channel.
+ PartitionedFftFilter right_filter_;
+
+ // Delay filter used to ensure the compensation curve starts at the same point
+ // as the spectral reverb.
+ DelayFilter delay_filter_;
+
+ // Number of active update processors.
+ size_t num_active_processors_;
+
+ // Active reverb update processors to replace the corresponding filter
+ // partitions of the reverb tail within each process call.
+ std::list<std::unique_ptr<ReverbOnsetUpdateProcessor>> update_processors_;
+
+ // Temporary buffer used to process filter kernel partitions.
+ AudioBuffer temp_kernel_buffer_;
+
+ // Temporary buffer to hold FFT frequency domain output.
+ PartitionedFftFilter::FreqDomainBuffer temp_freq_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_REVERB_ONSET_COMPENSATOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_update_processor.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_update_processor.cc
new file mode 100644
index 000000000..3a5dbcf02
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_update_processor.cc
@@ -0,0 +1,184 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/reverb_onset_update_processor.h"
+
+#include <algorithm>
+
+#include "base/constants_and_types.h"
+#include "base/simd_utils.h"
+#include "dsp/spectral_reverb_constants_and_tables.h"
+#include "dsp/utils.h"
+
+namespace vraudio {
+
+namespace {
+
+// Find the absolute difference between two size_t values.
+inline size_t absdiff(size_t lhs, size_t rhs) {
+ return lhs > rhs ? lhs - rhs : rhs - lhs;
+}
+
+} // namespace
+
+ReverbOnsetUpdateProcessor::ReverbOnsetUpdateProcessor(
+ size_t frames_per_buffer, int sampling_rate, AudioBuffer* base_curves,
+ AudioBuffer* adder_curves)
+ : sampling_rate_(sampling_rate),
+ tail_update_cursor_(0),
+ tail_length_(CeilToMultipleOfFramesPerBuffer(kCorrectionCurveLength,
+ frames_per_buffer)),
+ gain_(1.0f),
+ curve_indices_(GetNumReverbOctaveBands(sampling_rate_), kInvalidIndex),
+ pure_decay_coefficients_(curve_indices_.size(), 0.0f),
+ pure_decay_exponents_(curve_indices_.size(), 0.0f),
+ band_buffer_(kNumStereoChannels, frames_per_buffer),
+ envelope_buffer_(kNumMonoChannels, frames_per_buffer),
+ base_curves_(base_curves),
+ adder_curves_(adder_curves) {}
+
+void ReverbOnsetUpdateProcessor::SetReverbTimes(const float* rt60_values) {
+ DCHECK(rt60_values);
+ const size_t num_octave_bands = curve_indices_.size();
+ const float sampling_rate_float = static_cast<float>(sampling_rate_);
+ tail_update_cursor_ = 0;
+ // Choose curves for each band.
+ for (size_t band = 0; band < num_octave_bands; ++band) {
+ curve_indices_[band] =
+ GetFeedbackIndexFromRt60(rt60_values[band], sampling_rate_float);
+ // Deal with the case where only the convolution is needed.
+ if (curve_indices_[band] == kInvalidIndex) {
+ const float min_reverb_time =
+ kMinReverbTimeForFeedback48kHz *
+ (sampling_rate_float / kDefaultSpectralReverbSampleRate);
+ const float effective_rt =
+ rt60_values[band] <= min_reverb_time ? rt60_values[band] : 0.0f;
+ pure_decay_exponents_[band] =
+ std::abs(effective_rt) > kEpsilonFloat
+ ? std::exp(kNegativeLog1000 /
+ (sampling_rate_float * effective_rt))
+ : 0.0f;
+ pure_decay_coefficients_[band] = pure_decay_exponents_[band];
+ }
+ }
+}
+
+bool ReverbOnsetUpdateProcessor::Process(
+ const std::vector<AudioBuffer>& bandpassed_noise_left,
+ const std::vector<AudioBuffer>& bandpassed_noise_right,
+ AudioBuffer::Channel* kernel_channel_left,
+ AudioBuffer::Channel* kernel_channel_right) {
+ if (tail_update_cursor_ >= tail_length_) {
+ // Processing the reverb tail is finished.
+ tail_update_cursor_ = 0;
+ return false;
+ }
+ const size_t frames_per_buffer = band_buffer_.num_frames();
+ DCHECK(kernel_channel_left);
+ DCHECK(kernel_channel_right);
+ DCHECK_EQ(bandpassed_noise_left.size(), curve_indices_.size());
+ DCHECK_EQ(bandpassed_noise_right.size(), curve_indices_.size());
+ DCHECK_EQ(bandpassed_noise_left[0].num_frames(), tail_length_);
+ DCHECK_EQ(bandpassed_noise_right[0].num_frames(), tail_length_);
+ DCHECK_GE(tail_length_, kCorrectionCurveLength);
+ DCHECK_EQ(kernel_channel_left->size(), frames_per_buffer);
+ DCHECK_EQ(kernel_channel_right->size(), frames_per_buffer);
+
+ // Clear for accumulation per frequency band.
+ kernel_channel_left->Clear();
+ kernel_channel_right->Clear();
+
+ AudioBuffer::Channel& band_channel_left = band_buffer_[0];
+ AudioBuffer::Channel& band_channel_right = band_buffer_[1];
+ // Define the number of samples we are still able to copy from the multiplier
+ // and adder curves.
+ const size_t copy_length =
+ frames_per_buffer + tail_update_cursor_ <= kCorrectionCurveLength
+ ? frames_per_buffer
+ : absdiff(kCorrectionCurveLength, tail_update_cursor_);
+ AudioBuffer::Channel* envelope_channel = &envelope_buffer_[0];
+ // Compute the band buffer for each band response.
+ for (size_t band = 0; band < curve_indices_.size(); ++band) {
+ const AudioBuffer::Channel& noise_channel_left =
+ bandpassed_noise_left[band][0];
+ const AudioBuffer::Channel& noise_channel_right =
+ bandpassed_noise_right[band][0];
+ // Fill the band buffer with the next noise buffer and apply gain.
+ ScalarMultiply(frames_per_buffer, gain_,
+ noise_channel_left.begin() + tail_update_cursor_,
+ band_channel_left.begin());
+ ScalarMultiply(frames_per_buffer, gain_,
+ noise_channel_right.begin() + tail_update_cursor_,
+ band_channel_right.begin());
+ // Skip the band if we have an invalid index
+ const int curve_index = curve_indices_[band];
+ if (curve_index != kInvalidIndex) {
+ // Apply the correct compensation curve to the buffer.
+ const float scale = kCurveCorrectionMultipliers[curve_index];
+ AudioBuffer::Channel* adder_curve_channel;
+ if (tail_update_cursor_ < kCorrectionCurveLength) {
+ // Use either the high frequency or low frequency curve.
+ if (static_cast<size_t>(curve_index) >= kCurveChangeoverIndex) {
+ adder_curve_channel = &(*adder_curves_)[1];
+ std::copy_n((*base_curves_)[1].begin() + tail_update_cursor_,
+ copy_length, envelope_channel->begin());
+ } else {
+ adder_curve_channel = &(*adder_curves_)[0];
+ std::copy_n((*base_curves_)[0].begin() + tail_update_cursor_,
+ copy_length, envelope_channel->begin());
+ }
+ // Construct the correct envelope (chunk thereof).
+ ScalarMultiplyAndAccumulate(
+ copy_length, scale,
+ adder_curve_channel->begin() + tail_update_cursor_,
+ envelope_channel->begin());
+ // Ensure the end part of the envelope does not contain spurious data.
+ std::fill(envelope_channel->begin() + copy_length,
+ envelope_channel->end(), 0.0f);
+ } else {
+ // If we have moved past the length of the correction curve, fill the
+ // envelope chunk with zeros.
+ envelope_channel->Clear();
+ }
+
+ // Apply that envelope to the given band and accumulate into the output.
+ MultiplyAndAccumulatePointwise(
+ frames_per_buffer, envelope_channel->begin(),
+ band_channel_left.begin(), kernel_channel_left->begin());
+ MultiplyAndAccumulatePointwise(
+ frames_per_buffer, envelope_channel->begin(),
+ band_channel_right.begin(), kernel_channel_right->begin());
+ } else {
+ // If the decay time is too short for the spectral reverb to make a
+ // contribution (0.15s @48kHz), the compensation filter will consist of
+ // the entire tail.
+ for (size_t frame = 0; frame < frames_per_buffer; ++frame) {
+ (*kernel_channel_left)[frame] +=
+ pure_decay_coefficients_[band] * band_channel_left[frame];
+ (*kernel_channel_right)[frame] +=
+ pure_decay_coefficients_[band] * band_channel_right[frame];
+ // Update the decay coefficient.
+ pure_decay_coefficients_[band] *= pure_decay_exponents_[band];
+ }
+ }
+ }
+ // Update the cursor.
+ tail_update_cursor_ += frames_per_buffer;
+
+ return true;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_update_processor.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_update_processor.h
new file mode 100644
index 000000000..19fee4b42
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/reverb_onset_update_processor.h
@@ -0,0 +1,113 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_REVERB_ONSET_UPDATE_PROCESSOR_H_
+#define RESONANCE_AUDIO_DSP_REVERB_ONSET_UPDATE_PROCESSOR_H_
+
+#include <vector>
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Updater for the |ReverbOnsetCompensator|.
+class ReverbOnsetUpdateProcessor {
+ public:
+ // Constructs a update processor for the sprectral reverb onset compensator.
+ //
+ // @param frames_per_buffer_ System buffer length in frames.
+ // @param sampling_rate System sample rate.
+ // @param base_curves Constituent curve used for envelope generation.
+ // @param ader_curves Constituent curve used for envelope generation.
+ ReverbOnsetUpdateProcessor(size_t frames_per_buffer, int sampling_rate,
+ AudioBuffer* base_curves,
+ AudioBuffer* adder_curves);
+
+ // Sets reverberation times in different frequency bands.
+ //
+ // @param rt60_values |kNumReverbOctaveBands| values denoting the
+ // reverberation decay time to -60dB in octave bands starting at
+ // |kLowestOctaveBand|.
+ void SetReverbTimes(const float* rt60_values);
+
+ // Sets the gain applied to the overall compensation envelope.
+ //
+ // @param gain Gain applied to overall envelope.
+ void SetGain(float gain) { gain_ = gain; }
+
+ // Processes the next tail update.
+ //
+ // @param bandpassed_noise_left Pre-computed bandpassed noise buffer.
+ // @param bandpassed_noise_right Pre-computed bandpassed noise buffer.
+ // @param kernel_channel_left Kernel channel to fill in the processed output.
+ // @param kernel_channel_right Kernel channel to fill in the processed output.
+ // @return True if the tail update continues.
+ bool Process(const std::vector<AudioBuffer>& bandpassed_noise_left,
+ const std::vector<AudioBuffer>& bandpassed_noise_right,
+ AudioBuffer::Channel* kernel_channel_left,
+ AudioBuffer::Channel* kernel_channel_right);
+
+ // Returns the partition index of the current update state.
+ //
+ // @return Current partition index.
+ size_t GetCurrentPartitionIndex() const {
+ const size_t frames_per_buffer = band_buffer_.num_frames();
+ DCHECK_NE(frames_per_buffer, 0U);
+ return tail_update_cursor_ / frames_per_buffer;
+ }
+
+ // Disable copy and assignment operator.
+ ReverbOnsetUpdateProcessor(ReverbOnsetUpdateProcessor const&) = delete;
+ void operator=(ReverbOnsetUpdateProcessor const&) = delete;
+
+ private:
+ // System sample rate.
+ int sampling_rate_;
+
+ // Current frame position of the reverb tail to be updated.
+ size_t tail_update_cursor_;
+
+ // Length of the new reverb tail to be replaced in frames.
+ size_t tail_length_;
+
+ // Gain applied to the reverb compensation.
+ float gain_;
+
+ // Indices of the multiplication factor to be used to create the onset
+ // compensation curve at each frequency band.
+ std::vector<int> curve_indices_;
+
+ // Decay coefficients per each band of the reverb tail, used below 0.15s
+ // @48kHz.
+ std::vector<float> pure_decay_coefficients_;
+
+ // Decay exponential per each band of the reverb tail, used below 0.15s
+ // @48kHz.
+ std::vector<float> pure_decay_exponents_;
+
+ // Temporary buffers used to process the decayed noise per each band.
+ AudioBuffer band_buffer_;
+ AudioBuffer envelope_buffer_;
+
+ // Pointers to audio buffers owned by the |ReverbOnsetCompensator| storing the
+ // constituent curves used to generate the onset compensation envelopes.
+ AudioBuffer* base_curves_;
+ AudioBuffer* adder_curves_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_REVERB_ONSET_UPDATE_PROCESSOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/sh_hrir_creator.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/sh_hrir_creator.cc
new file mode 100644
index 000000000..11226a6fc
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/sh_hrir_creator.cc
@@ -0,0 +1,72 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/sh_hrir_creator.h"
+
+#include "third_party/SADIE_hrtf_database/generated/hrtf_assets.h"
+#include "ambisonics/utils.h"
+#include "base/logging.h"
+#include "dsp/resampler.h"
+#include "utils/planar_interleaved_conversion.h"
+
+namespace vraudio {
+
+std::unique_ptr<AudioBuffer> CreateShHrirsFromWav(const Wav& wav,
+ int target_sample_rate_hz,
+ Resampler* resampler) {
+ DCHECK(resampler);
+ const size_t num_channels = wav.GetNumChannels();
+ CHECK(IsValidAmbisonicOrder(num_channels));
+
+ const size_t sh_hrir_length = wav.interleaved_samples().size() / num_channels;
+ std::unique_ptr<AudioBuffer> sh_hrirs(
+ new AudioBuffer(num_channels, sh_hrir_length));
+ FillAudioBuffer(wav.interleaved_samples(), num_channels, sh_hrirs.get());
+
+ const int wav_sample_rate_hz = wav.GetSampleRateHz();
+ CHECK_GT(wav_sample_rate_hz, 0);
+ CHECK_GT(target_sample_rate_hz, 0);
+ if (wav_sample_rate_hz != target_sample_rate_hz) {
+ if (!Resampler::AreSampleRatesSupported(wav_sample_rate_hz,
+ target_sample_rate_hz)) {
+ LOG(FATAL) << "Unsupported sampling rates for loading HRIRs: "
+ << wav_sample_rate_hz << ", " << target_sample_rate_hz;
+ }
+ resampler->ResetState();
+ // Resample the SH HRIRs if necessary.
+ resampler->SetRateAndNumChannels(wav_sample_rate_hz, target_sample_rate_hz,
+ num_channels);
+ std::unique_ptr<AudioBuffer> resampled_sh_hrirs(new AudioBuffer(
+ num_channels, resampler->GetNextOutputLength(sh_hrir_length)));
+ resampler->Process(*sh_hrirs, resampled_sh_hrirs.get());
+ return resampled_sh_hrirs;
+ }
+ return sh_hrirs;
+}
+
+std::unique_ptr<AudioBuffer> CreateShHrirsFromAssets(
+ const std::string& filename, int target_sample_rate_hz,
+ Resampler* resampler) {
+ // Read SH HRIR from asset store.
+ sadie::HrtfAssets hrtf_assets;
+ std::unique_ptr<std::string> sh_hrir_data = hrtf_assets.GetFile(filename);
+ CHECK_NOTNULL(sh_hrir_data.get());
+ std::istringstream wav_data_stream(*sh_hrir_data);
+ std::unique_ptr<const Wav> wav = Wav::CreateOrNull(&wav_data_stream);
+ return CreateShHrirsFromWav(*wav, target_sample_rate_hz, resampler);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/sh_hrir_creator.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/sh_hrir_creator.h
new file mode 100644
index 000000000..5a11926f5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/sh_hrir_creator.h
@@ -0,0 +1,54 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_SH_HRIR_CREATOR_H_
+#define RESONANCE_AUDIO_DSP_SH_HRIR_CREATOR_H_
+
+#include "base/audio_buffer.h"
+#include "dsp/resampler.h"
+#include "utils/wav.h"
+
+namespace vraudio {
+
+// Creates a multichannel audio buffer of Spherical Harmonic-encoded Head
+// Related Impulse Responses (SH HRIRs) from Wav SH HRIR assets. It also
+// checks if the channel count of the SH HRIR file is correct and resamples the
+// SH HRIRs if necessary to match the system (target) sampling rate.
+//
+// @param wav |Wav| instance that contains SH HRIRs.
+// @param target_sample_rate_hz Target sampling rate in Hertz.
+// @param resampler Pointer to a resampler used to convert HRIRs to the system
+// rate,
+// (This resampler's internal state will be reset on each function call).
+// @return Unique pointer to |AudioBuffer| where the SH HRIRs will be written.
+std::unique_ptr<AudioBuffer> CreateShHrirsFromWav(const Wav& wav,
+ int target_sample_rate_hz,
+ Resampler* resampler);
+
+// Creates a SH HRIR multichannel audio buffer from assets.
+//
+// @param filename Name of the Wav file that contains SH HRIRs.
+// @param target_sample_rate_hz Target sampling rate in Hertz.
+// @param resampler Pointer to a resampler used to convert HRIRs to the system
+// rate.
+// @return Unique pointer to |AudioBuffer| where the SH HRIRs will be written.
+std::unique_ptr<AudioBuffer> CreateShHrirsFromAssets(
+ const std::string& filename, int target_sample_rate_hz,
+ Resampler* resampler);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_SH_HRIR_CREATOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/shoe_box_room.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/shoe_box_room.cc
new file mode 100644
index 000000000..769166248
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/shoe_box_room.cc
@@ -0,0 +1,61 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/shoe_box_room.h"
+
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+void ComputeReflections(const WorldPosition& relative_listener_position,
+ const WorldPosition& room_dimensions,
+ const float* reflection_coefficients,
+ std::vector<Reflection>* reflections) {
+ DCHECK(reflection_coefficients);
+ DCHECK(reflections);
+ DCHECK_EQ(reflections->size(), kNumRoomSurfaces);
+ const WorldPosition kOrigin(0.0f, 0.0f, 0.0f);
+ if (!IsPositionInAabb(relative_listener_position, kOrigin, room_dimensions)) {
+ // Listener is outside the room, skip computation.
+ std::fill(reflections->begin(), reflections->end(), Reflection());
+ return;
+ }
+ // Calculate the distance of the listener to each wall.
+ // Since all the sources are 'attached' to the listener in the computation
+ // of reflections, the distance travelled is arbitrary. So, we add 1.0f to
+ // the computed distance in order to avoid delay time approaching 0 and the
+ // magnitude approaching +inf.
+ const WorldPosition offsets = 0.5f * room_dimensions;
+ const float distances_travelled[kNumRoomSurfaces] = {
+ offsets[0] + relative_listener_position[0] + 1.0f,
+ offsets[0] - relative_listener_position[0] + 1.0f,
+ offsets[1] + relative_listener_position[1] + 1.0f,
+ offsets[1] - relative_listener_position[1] + 1.0f,
+ offsets[2] + relative_listener_position[2] + 1.0f,
+ offsets[2] - relative_listener_position[2] + 1.0f};
+
+ for (size_t i = 0; i < kNumRoomSurfaces; ++i) {
+ // Convert distances to time delays in seconds.
+ (*reflections)[i].delay_time_seconds =
+ distances_travelled[i] / kSpeedOfSound;
+ // Division by distance is performed here as we don't want this applied more
+ // than once.
+ (*reflections)[i].magnitude =
+ reflection_coefficients[i] / distances_travelled[i];
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/shoe_box_room.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/shoe_box_room.h
new file mode 100644
index 000000000..eab514922
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/shoe_box_room.h
@@ -0,0 +1,44 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_SHOE_BOX_ROOM_H_
+#define RESONANCE_AUDIO_DSP_SHOE_BOX_ROOM_H_
+
+#include <vector>
+
+#include "base/misc_math.h"
+#include "dsp/reflection.h"
+
+namespace vraudio {
+
+// Computes a set of reflections from each surface of a shoe-box room model.
+// Uses a simplified calculation method which assumes that all the sources are
+// 'attached' to the listener. Also, assumes that the listener is inside the
+// shoe-box room.
+//
+// @param relative_listener_position Relative listener position to the center of
+// the room.
+// @param room_dimensions Dimensions of the room.
+// @param reflection_coefficients Reflection coefficients.
+// @return List of computed reflections by the image source method.
+void ComputeReflections(const WorldPosition& relative_listener_position,
+ const WorldPosition& room_dimensions,
+ const float* reflection_coefficients,
+ std::vector<Reflection>* reflections);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_SHOE_BOX_ROOM_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/shoe_box_room_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/shoe_box_room_test.cc
new file mode 100644
index 000000000..78393604f
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/shoe_box_room_test.cc
@@ -0,0 +1,96 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/shoe_box_room.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+// Expected reflection delay times in seconds.
+const float kExpectedDelays[kNumRoomSurfaces] = {
+ 0.011661f, 0.005830f, 0.016035f, 0.004373f, 0.020408f, 0.002915f};
+
+// Expected reflection magnitudes.
+const float kExpectedMagnitudes[kNumRoomSurfaces] = {
+ 0.25f, 0.5f, 0.181818f, 0.666666f, 0.142857f, 1.0f};
+
+// Tests that a set of single reflections arrive in from the correct directions.
+TEST(ShoeBoxRoomTest, SingleReflectionsTest) {
+ const WorldPosition kListenerPosition(1.0f, 2.0f, 3.0f);
+ const WorldPosition kRoomDimensions(4.0f, 5.0f, 6.0f);
+
+ // Perform the simplified image source method with the wall at the given index
+ // having a reflection coefficient of 1 and all of the other walls having a
+ // reflection coefficient of 0. Thus we expect one reflection in the output
+ // and we should be able to predict its magnitude, delay and direction of
+ // arrival in terms of azimuth and elevation.
+ float reflection_coefficients[kNumRoomSurfaces];
+ std::fill(std::begin(reflection_coefficients),
+ std::end(reflection_coefficients), 0.0f);
+ std::vector<Reflection> reflections(kNumRoomSurfaces);
+ for (size_t index = 0; index < kNumRoomSurfaces; ++index) {
+ reflection_coefficients[index] = 1.0f;
+
+ ComputeReflections(kListenerPosition, kRoomDimensions,
+ reflection_coefficients, &reflections);
+ EXPECT_EQ(kNumRoomSurfaces, reflections.size());
+
+ // Check that the correct reflection is returned for the given call.
+ size_t num_returned = 0;
+ for (size_t i = 0; i < kNumRoomSurfaces; ++i) {
+ if (reflections[i].magnitude > 0.0f) {
+ EXPECT_NEAR(kExpectedDelays[index], reflections[i].delay_time_seconds,
+ kEpsilonFloat);
+ EXPECT_NEAR(kExpectedMagnitudes[index], reflections[i].magnitude,
+ kEpsilonFloat);
+ ++num_returned;
+ }
+ }
+ EXPECT_EQ(1U, num_returned);
+
+ // Reset so that all reflection reflection_coefficients are 0.0f again.
+ reflection_coefficients[index] = 0.0f;
+ }
+}
+
+// Tests that no reflections arrive when the listener is outside the room.
+TEST(ShoeBoxRoomTest, ReflectionsOutsideRoomTest) {
+ const WorldPosition kListenerPosition(4.0f, 5.0f, 6.0f);
+ const WorldPosition kRoomDimensions(1.0f, 2.0f, 3.0f);
+ const float kReflectionCoefficients[] = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
+
+ std::vector<Reflection> reflections(kNumRoomSurfaces);
+ ComputeReflections(kListenerPosition, kRoomDimensions,
+ kReflectionCoefficients, &reflections);
+ EXPECT_EQ(kNumRoomSurfaces, reflections.size());
+
+ // Check that all the reflections have zeros.
+ for (size_t i = 0; i < kNumRoomSurfaces; ++i) {
+ EXPECT_NEAR(0.0f, reflections[i].delay_time_seconds, kEpsilonFloat);
+ EXPECT_NEAR(0.0f, reflections[i].magnitude, kEpsilonFloat);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb.cc
new file mode 100644
index 000000000..e03d34ccd
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb.cc
@@ -0,0 +1,350 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "dsp/spectral_reverb.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <numeric>
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/simd_utils.h"
+#include "dsp/spectral_reverb_constants_and_tables.h"
+#include "dsp/utils.h"
+
+namespace vraudio {
+
+namespace {
+
+// FFT length and length of time domain data processed.
+const size_t kFftSize = 4096;
+
+// Length of magnitude and phase buffers.
+const size_t kMagnitudeLength = kFftSize / 2 + 1;
+
+// Number of overlaps per kFftSize time domain chunk of input.
+const size_t kNumOverlap = 4;
+
+// Number of samples per overlap.
+const size_t kOverlapLength = kFftSize / kNumOverlap;
+
+// Three quarters FFT size, used for copying chunks of input.
+const size_t kThreeQuarterFftSize = kFftSize - kOverlapLength;
+
+// Number of channels needed to overlap add output data.
+const size_t kNumQuadChannels = 4;
+
+// Number of buffers of delay applies to the magnitude spectrum.
+const size_t kMagnitudeDelay = 3;
+
+// Length of a buffer of noise used to provide random phase.
+const size_t kNoiseLength = 16384;
+
+// Length of the noise buffer from which a phase buffer can start.
+const size_t kAvailableNoiselength = kNoiseLength - kMagnitudeLength;
+
+// Returns a random integer in the range provided. Used to index into the random
+// buffer for phase values.
+inline size_t GetRandomIntegerInRange(size_t min, size_t max) {
+ DCHECK_GE(max, min);
+ return min + static_cast<size_t>(std::rand()) % (max - min);
+}
+
+} // namespace
+
+SpectralReverb::SpectralReverb(int sample_rate, size_t frames_per_buffer)
+ : sample_rate_(sample_rate),
+ frames_per_buffer_(frames_per_buffer),
+ magnitude_delay_index_(0),
+ overlap_add_index_(0),
+ fft_manager_(kFftSize / 2),
+ sin_cos_random_phase_buffer_(kNumStereoChannels, kNoiseLength),
+ unscaled_window_(kNumMonoChannels, kFftSize),
+ window_(kNumMonoChannels, kFftSize),
+ feedback_(kNumMonoChannels, kMagnitudeLength),
+ magnitude_compensation_(kNumMonoChannels, kMagnitudeLength),
+ magnitude_delay_(kMagnitudeDelay, kMagnitudeLength),
+ fft_size_input_(kNumMonoChannels, kFftSize),
+ input_circular_buffer_(kFftSize + frames_per_buffer_, frames_per_buffer_,
+ kOverlapLength),
+ output_circular_buffers_(kNumStereoChannels),
+ out_time_buffer_(kNumQuadChannels, kFftSize),
+ temp_freq_buffer_(kNumStereoChannels, kFftSize),
+ scaled_magnitude_buffer_(kNumMonoChannels, kMagnitudeLength),
+ temp_magnitude_buffer_(kNumMonoChannels, kMagnitudeLength),
+ temp_phase_buffer_(kNumStereoChannels, kMagnitudeLength),
+ output_accumulator_(kNumStereoChannels),
+ is_gain_near_zero_(false),
+ is_feedback_near_zero_(false) {
+ DCHECK_GT(sample_rate, 0);
+ DCHECK_GT(frames_per_buffer_, 0U);
+
+ // Seed std::rand, used for phase selection.
+ std::srand(1);
+ GenerateRandomPhaseBuffer();
+ GenerateAnalysisWindow();
+ InitializeCircularBuffersAndAccumulators();
+ fft_size_input_.Clear();
+ magnitude_compensation_.Clear();
+}
+
+void SpectralReverb::SetGain(float gain) {
+ DCHECK_GE(gain, 0.0f);
+ ScalarMultiply(window_.num_frames(), gain, &unscaled_window_[0][0],
+ &window_[0][0]);
+ // If the gain is less than -60dB we will bypass all processing.
+ is_gain_near_zero_ = gain <= kNegative60dbInAmplitude;
+ // If we are to bypass processing we clear the circular buffer so that we
+ // don't process old input when the spectral reverb is restarted.
+ if (is_gain_near_zero_ || is_feedback_near_zero_) {
+ input_circular_buffer_.Clear();
+ }
+}
+
+void SpectralReverb::SetRt60PerOctaveBand(const float* rt60_values) {
+ DCHECK(rt60_values);
+ const float sample_rate_float = static_cast<float>(sample_rate_);
+ // Fill the entries in the feedback channel with the feedback values up to
+ // that frequency. The feedback channels entries are per frequency in the same
+ // way as the magnitude vectors. Also fill the magnitude compensation vector
+ // such that the peak value at each frequency for any given reverberation time
+ // will be |kDefaultReverbGain|.
+ AudioBuffer::Channel* feedback_channel = &feedback_[0];
+ feedback_channel->Clear();
+ AudioBuffer::Channel* magnitude_compensation_channel =
+ &magnitude_compensation_[0];
+ magnitude_compensation_channel->Clear();
+ const float frequency_step = sample_rate_float / static_cast<float>(kFftSize);
+ int index = GetFeedbackIndexFromRt60(rt60_values[0], sample_rate_float);
+ float current_feedback =
+ index == kInvalidIndex ? 0.0f : kSpectralReverbFeedback[index];
+ float magnitude_compensation_value =
+ index == kInvalidIndex ? 0.0f : kMagnitudeCompensation[index];
+ const size_t max_frequency_bin =
+ std::min(static_cast<size_t>(
+ (kOctaveBandCentres[kNumReverbOctaveBands - 1] * kSqrtTwo) /
+ frequency_step),
+ feedback_channel->size());
+ // The upper edge of the octave band is sqrt(2) * centre_frequency :
+ // https://en.wikipedia.org/wiki/Octave_band#Octave_Bands
+ float upper_octave_band_edge = kOctaveBandCentres[0] * kSqrtTwo;
+ for (size_t i = 0, j = 0; i < max_frequency_bin; ++i) {
+ const float current_freq = static_cast<float>(i) * frequency_step;
+ if (current_freq > upper_octave_band_edge) {
+ ++j;
+ DCHECK_LT(j, kNumReverbOctaveBands);
+ upper_octave_band_edge = kOctaveBandCentres[j] * kSqrtTwo;
+ index = GetFeedbackIndexFromRt60(rt60_values[j], sample_rate_float);
+ current_feedback =
+ index == kInvalidIndex ? 0.0f : kSpectralReverbFeedback[index];
+ magnitude_compensation_value =
+ index == kInvalidIndex ? 0.0f : kMagnitudeCompensation[index];
+ }
+ (*feedback_channel)[i] = current_feedback;
+ (*magnitude_compensation_channel)[i] = magnitude_compensation_value;
+ }
+ // If the sum of all feedback values is below the minimum feedback value, it
+ // is safe to assume we can bypass the spectral reverb entirely.
+ is_feedback_near_zero_ =
+ std::accumulate(feedback_channel->begin(), feedback_channel->end(),
+ 0.0f) < kSpectralReverbFeedback[0];
+ // If we are to bypass processing we clear the circular buffer so that we
+ // don't process old input when the spectral reverb is restarted.
+ if (is_gain_near_zero_ || is_feedback_near_zero_) {
+ input_circular_buffer_.Clear();
+ }
+}
+
+void SpectralReverb::Process(const AudioBuffer::Channel& input,
+ AudioBuffer::Channel* left_out,
+ AudioBuffer::Channel* right_out) {
+ DCHECK_EQ(input.size(), left_out->size());
+ DCHECK_EQ(input.size(), right_out->size());
+ DCHECK_EQ(input.size(), frames_per_buffer_);
+
+
+ if (is_gain_near_zero_ || is_feedback_near_zero_) {
+ left_out->Clear();
+ right_out->Clear();
+ return;
+ }
+
+ // Here we insert |frames_per_buffer_| samples on each function call. Then,
+ // once there are |kOverlapLength| samples in the input circular buffer we
+ // retrieve |kOverlapLength| samples from it and process |kFftSize| samples of
+ // input at a time, sliding along by |kOverlapLength| samples. We then place
+ // |kOverlapLength| samples into the output buffer and we will extract
+ // |frames_per_buffer_| samples from the output buffer on each function call.
+ input_circular_buffer_.InsertBuffer(input);
+ while (input_circular_buffer_.GetOccupancy() >= kOverlapLength) {
+ std::copy_n(&fft_size_input_[0][kOverlapLength], kThreeQuarterFftSize,
+ &fft_size_input_[0][0]);
+ input_circular_buffer_.RetrieveBufferWithOffset(kThreeQuarterFftSize,
+ &fft_size_input_[0]);
+ fft_manager_.FreqFromTimeDomain(fft_size_input_[0], &temp_freq_buffer_[0]);
+ fft_manager_.GetCanonicalFormatFreqBuffer(temp_freq_buffer_[0],
+ &temp_freq_buffer_[1]);
+ fft_manager_.MagnitudeFromCanonicalFreqBuffer(temp_freq_buffer_[1],
+ &scaled_magnitude_buffer_[0]);
+ // Apply the magnitude compensation to the input magnitude spectrum before
+ // feedback is applied.
+ MultiplyPointwise(kMagnitudeLength, magnitude_compensation_[0].begin(),
+ scaled_magnitude_buffer_[0].begin(),
+ scaled_magnitude_buffer_[0].begin());
+ // Generate time domain reverb blocks.
+ GetNextReverbBlock(magnitude_delay_index_, &out_time_buffer_[0],
+ &out_time_buffer_[1]);
+ magnitude_delay_index_ = (magnitude_delay_index_ + 1) % kMagnitudeDelay;
+ GetNextReverbBlock(magnitude_delay_index_, &out_time_buffer_[2],
+ &out_time_buffer_[3]);
+
+ // Combine the reverb blocks for both left and right output.
+ AddPointwise(kFftSize, out_time_buffer_[0].begin(),
+ out_time_buffer_[2].begin(), out_time_buffer_[0].begin());
+ AddPointwise(kFftSize, out_time_buffer_[1].begin(),
+ out_time_buffer_[3].begin(), out_time_buffer_[1].begin());
+
+ // Window the left and right output (While applying inverse FFT scaling).
+ MultiplyPointwise(kFftSize, out_time_buffer_[0].begin(), window_[0].begin(),
+ out_time_buffer_[0].begin());
+ MultiplyPointwise(kFftSize, out_time_buffer_[1].begin(), window_[0].begin(),
+ out_time_buffer_[1].begin());
+
+ // Next perform the addition and the submission into the output buffer.
+ AccumulateOverlap(0 /*channel*/, out_time_buffer_[0]);
+ AccumulateOverlap(1 /*channel*/, out_time_buffer_[1]);
+ overlap_add_index_ = (overlap_add_index_ + 1) % kNumOverlap;
+ }
+ output_circular_buffers_[0]->RetrieveBuffer(left_out);
+ output_circular_buffers_[1]->RetrieveBuffer(right_out);
+}
+
+void SpectralReverb::AccumulateOverlap(size_t channel_index,
+ const AudioBuffer::Channel& buffer) {
+ // Use a modulo indexed multi channel audio buffer with each channel of length
+ // |kOverlapLength| to perform an overlap add.
+ for (size_t i = 0, index = overlap_add_index_; i < kNumOverlap;
+ ++i, index = (index + 1) % kNumOverlap) {
+ float* accumulator_start_point =
+ output_accumulator_[channel_index][index].begin();
+ AddPointwise(kOverlapLength, buffer.begin() + i * kOverlapLength,
+ accumulator_start_point, accumulator_start_point);
+ }
+ output_circular_buffers_[channel_index]->InsertBuffer(
+ output_accumulator_[channel_index][overlap_add_index_]);
+ output_accumulator_[channel_index][overlap_add_index_].Clear();
+}
+
+void SpectralReverb::GenerateAnalysisWindow() {
+ // Genarate a pseudo tukey window from three overlapping hann windows, scaled
+ // by the inverse fft scale.
+ AudioBuffer::Channel* window_channel = &window_[0];
+ // Use the unscaled window buffer as temporary storage.
+ GenerateHannWindow(true /* full */, kMagnitudeLength, &unscaled_window_[0]);
+ float* hann_window = &unscaled_window_[0][0];
+ // Scale the hann window such that the sum of three will have unity peak.
+ const float kThreeQuarters = 0.75f;
+ ScalarMultiply(kMagnitudeLength, kThreeQuarters, hann_window, hann_window);
+ for (size_t offset = 0; offset < kThreeQuarterFftSize;
+ offset += kOverlapLength) {
+ float* tripple_hann_window = &(*window_channel)[offset];
+ AddPointwise(kMagnitudeLength, hann_window, tripple_hann_window,
+ tripple_hann_window);
+ }
+ fft_manager_.ApplyReverseFftScaling(window_channel);
+ unscaled_window_[0] = *window_channel;
+}
+
+void SpectralReverb::GenerateRandomPhaseBuffer() {
+ AudioBuffer::Channel* sin_phase_channel = &sin_cos_random_phase_buffer_[0];
+ AudioBuffer::Channel* cos_phase_channel = &sin_cos_random_phase_buffer_[1];
+ // Initially use the sin channel to store the random data before taking sine
+ // and cosine.
+ GenerateUniformNoise(/*min=*/0.0f, /*max=*/kPi, /*seed=*/1U,
+ sin_phase_channel);
+ for (size_t i = 0; i < sin_cos_random_phase_buffer_.num_frames(); ++i) {
+
+ (*cos_phase_channel)[i] = std::cos((*sin_phase_channel)[i]);
+ (*sin_phase_channel)[i] = std::sin((*sin_phase_channel)[i]);
+ }
+}
+
+void SpectralReverb::GetNextReverbBlock(size_t delay_index,
+ AudioBuffer::Channel* left_channel,
+ AudioBuffer::Channel* right_channel) {
+ DCHECK(left_channel);
+ DCHECK(right_channel);
+
+ // Generate reverb magnitude by combining the delayed magnitude with the
+ // current magnitude.
+ AudioBuffer::Channel* temp_magnitude_channel = &temp_magnitude_buffer_[0];
+ *temp_magnitude_channel = scaled_magnitude_buffer_[0];
+ MultiplyAndAccumulatePointwise(
+ kMagnitudeLength, magnitude_delay_[delay_index].begin(),
+ feedback_[0].begin(), temp_magnitude_channel->begin());
+ // Reinsert this new reverb magnitude into the delay buffer.
+ magnitude_delay_[delay_index] = *temp_magnitude_channel;
+
+ for (size_t i = 0; i < kNumStereoChannels; ++i) {
+ // Extract a random phase buffer.
+ const size_t random_offset =
+ GetRandomIntegerInRange(0, kAvailableNoiselength);
+ // We gaurantee an aligned offset as when SSE is used we need it.
+ const size_t phase_offset = FindNextAlignedArrayIndex(
+ random_offset, sizeof(float), kMemoryAlignmentBytes);
+ // Convert from magnitude and phase to a time domain output.
+ fft_manager_.CanonicalFreqBufferFromMagnitudeAndSinCosPhase(
+ phase_offset, (*temp_magnitude_channel),
+ sin_cos_random_phase_buffer_[0], sin_cos_random_phase_buffer_[1],
+ &temp_freq_buffer_[0]);
+
+ AudioBuffer::Channel* out_channel = i == 0 ? left_channel : right_channel;
+ fft_manager_.GetPffftFormatFreqBuffer(temp_freq_buffer_[0],
+ &temp_freq_buffer_[1]);
+ fft_manager_.TimeFromFreqDomain(temp_freq_buffer_[1], out_channel);
+ }
+}
+
+void SpectralReverb::InitializeCircularBuffersAndAccumulators() {
+ AudioBuffer zeros(kNumMonoChannels, kOverlapLength);
+ zeros.Clear();
+ for (size_t channel = 0; channel < kNumStereoChannels; ++channel) {
+ // Prefill the |output_circular_buffers_| with |kOverlapLength| /
+ // |frames_per_buffer_| calls worth of zeros.
+ output_circular_buffers_[channel].reset(
+ new CircularBuffer(kOverlapLength + frames_per_buffer_, kOverlapLength,
+ frames_per_buffer_));
+ // Due to differences in the |frames_per_buffer_| used for input and output
+ // and |kOverlapLength| used for processing, a certain number of buffers of
+ // zeros must be inserted into the output buffers such that enough input can
+ // build up to process |kOverlapLength| worth, and enough output will build
+ // up to return |frames_per_buffer_| worth.
+ const size_t zeroed_buffers_of_output = kOverlapLength / frames_per_buffer_;
+ for (size_t i = 0; i < zeroed_buffers_of_output; ++i) {
+ output_circular_buffers_[channel]->InsertBuffer(zeros[0]);
+ }
+ output_accumulator_[channel] = AudioBuffer(kNumOverlap, kOverlapLength);
+ output_accumulator_[channel].Clear();
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb.h
new file mode 100644
index 000000000..0734772e0
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb.h
@@ -0,0 +1,173 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_SPECTRAL_REVERB_H_
+#define RESONANCE_AUDIO_DSP_SPECTRAL_REVERB_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "dsp/circular_buffer.h"
+#include "dsp/fft_manager.h"
+
+namespace vraudio {
+
+// Implements a spectral reverb producing a decorrelated stereo output. See:
+// [1] E. Vickers, J-L Wu, P.G. Krishnan, R. N. K. Sadanandam, "Frequency Domain
+// Artificial Reverberation using Spectral Magnitude Decay",
+// https://goo.gl/hv1pdJ.
+class SpectralReverb {
+ public:
+ // Constructs a spectral reverb.
+ //
+ // @param sample_rate The system sample rate.
+ // @param frames_per_buffer System frames per buffer of input and output.
+ // Note that this class expects power of two buffers of input and output.
+ SpectralReverb(int sample_rate, size_t frames_per_buffer);
+
+ // Sets the overall gain to be applied to the output of the reverb.
+ //
+ // @param gain Gain to be applied to the reverb output, min value 0.0f.
+ void SetGain(float gain);
+
+ // Sets the |SpectralReverb|'s reverberation times in different frequency
+ // bands. Supports times between:
+ // (0.15 * 48000 / |sample_rate|)s and (25 * 48000 / |sample_rate|)s.
+ //
+ // @param rt60_values |kNumReverbOctaveBands| values denoting the
+ // reverberation decay time to -60dB in octave bands starting at
+ // |kLowestOctaveBand|.
+ void SetRt60PerOctaveBand(const float* rt60_values);
+
+ // Applies reverb to an input channel of audio data and produces a stereo
+ // output.
+ //
+ // @param input Mono inpu data.
+ // @param left_out Left channel of reverberated output.
+ // @param right_out Right channel of reverberated output.
+ void Process(const AudioBuffer::Channel& input,
+ AudioBuffer::Channel* left_out, AudioBuffer::Channel* right_out);
+
+ private:
+ // Uses an AudioBuffer with four channels to overlap add and insert the final
+ // reverb into the output circular buffers.
+ //
+ // @param channel_index Denotes the (left or right) channel to output to.
+ // @param buffer The buffer to be added onto the pre-existing reverb output.
+ void AccumulateOverlap(size_t channel_index,
+ const AudioBuffer::Channel& buffer);
+
+ // Generates a window function which is a normalized sum of three overlapping
+ // (50%) hann windows of length (|kFftSize| / 2) that also incorporates the
+ // inverse fft scaling.
+ void GenerateAnalysisWindow();
+
+ // Generates a large buffer of sines and cosines of random noise between 0 and
+ // pi to be randomly indexed into in order to cheaply generate highly
+ // decorrelated phase buffers,
+ void GenerateRandomPhaseBuffer();
+
+ // Obtains the next stero pair of time domain reverb blocks which can then be
+ // summed together in an overlap add fashion to provide the reverb output.
+ //
+ // @param delay_index An index into the frequency domain magnitude ring
+ // buffer.
+ // @param left_channel Channel to contain the left partial reverb output.
+ // @param right_channel Channel to contain the right partial reverb output.
+ void GetNextReverbBlock(size_t delay_index,
+ AudioBuffer::Channel* left_channel,
+ AudioBuffer::Channel* right_channel);
+
+ // Initializes the output circular buffers such that they contain zeros if the
+ // value of |frames_per_buffer_| is sufficiently smaller than that of
+ // |kOverlapLength| that buffering of input will be required prior to
+ // processing. Also allocates memory for the output accumulators.
+ void InitializeCircularBuffersAndAccumulators();
+
+ // System sample rate.
+ const int sample_rate_;
+
+ // System frames per buffer.
+ const size_t frames_per_buffer_;
+
+ // Indices into the magnitude and overlap add delay lines, modulo of their
+ // respective lengths.
+ size_t magnitude_delay_index_;
+ size_t overlap_add_index_;
+
+ // Manages the time-frequency transforms and phase/magnitude-frequency
+ // transforms.
+ FftManager fft_manager_;
+
+ // Buffer containing sines and cosines of random values between 0 and pi to be
+ // used for phase.
+ AudioBuffer sin_cos_random_phase_buffer_;
+
+ // Buffer containing a triple overlapping hann window for windowing time
+ // domain data.
+ AudioBuffer unscaled_window_;
+
+ // Buffer containing a triple overlapping hann window for windowing time
+ // domain data, this window has been scaled by the output gain factor.
+ AudioBuffer window_;
+
+ // Buffer containing RT60 tuned feedback values.
+ AudioBuffer feedback_;
+
+ // Buffer used to store scaling values which account for the different initial
+ // peak magnitudes for different RT60s.
+ AudioBuffer magnitude_compensation_;
+
+ // Buffer that acts as the frequency domain magnitde delay.
+ AudioBuffer magnitude_delay_;
+
+ // Buffer to contain a linear |kFftSize| chunk of input data.
+ AudioBuffer fft_size_input_;
+
+ // Circular buffers to sit at the input and output of the |Process()| method
+ // to allow |frames_per_buffer_| to differ from |kFftSize|.
+ CircularBuffer input_circular_buffer_;
+ std::vector<std::unique_ptr<CircularBuffer>> output_circular_buffers_;
+
+ // Time domain buffer used to store reverb before the overlap add operation.
+ AudioBuffer out_time_buffer_;
+
+ // Temporary frequency domain buffer, used to store frequency domain data when
+ // transforming between Pffft and Canonical format frequency domain data.
+ AudioBuffer temp_freq_buffer_;
+
+ // Buffer used to store feedback scaled magnitude values.
+ AudioBuffer scaled_magnitude_buffer_;
+
+ // Buffer used for the accumulation of scaled magnitude buffers.
+ AudioBuffer temp_magnitude_buffer_;
+
+ // Buffer used to store randomized phase.
+ AudioBuffer temp_phase_buffer_;
+
+ // Buffers used to calculate the overlap add at the output.
+ std::vector<AudioBuffer> output_accumulator_;
+
+ // Processing of the spectral reverb is bypassed when the feedback values are
+ // all approximately zero OR when the gain is set to near zero.
+ bool is_gain_near_zero_;
+ bool is_feedback_near_zero_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_SPECTRAL_REVERB_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb_constants_and_tables.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb_constants_and_tables.h
new file mode 100644
index 000000000..84329f329
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb_constants_and_tables.h
@@ -0,0 +1,6508 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_SPECTRAL_REVERB_CONSTANTS_AND_TABLES_H_
+#define RESONANCE_AUDIO_DSP_SPECTRAL_REVERB_CONSTANTS_AND_TABLES_H_
+
+#include <algorithm>
+#include <cstddef>
+
+namespace vraudio {
+
+// Sampling rate for which the spectral reverb feedback coefficients are
+// calculated.
+static const float kDefaultSpectralReverbSampleRate = 48000.0f;
+
+// Minimum reverb time supported by the feedback table below (seconds).
+static const float kMinReverbTimeForFeedback48kHz = 0.15f;
+
+// Step size between each reverberation time supported by the feedback table
+// (seconds).
+static const float kTimeStepSizeForFeedback48kHz = 0.01f;
+
+// Number of entries in the feedback table below.
+static const size_t kNumFeedbackValues = 2485;
+
+// Value denoting an invalid index into the feedback and magnitude correction
+// tables. The feedback and magnitude correction factors will be 0.0 when this
+// value is returned from the index lookup method.
+const int kInvalidIndex = -1;
+
+// Number of frames of zeros before the reverb onset curve.
+const size_t kCompensationOnsetLength = 1024;
+
+// Length in samples of the spectral reverb onset correction curves.
+const size_t kCorrectionCurveLength = 6144;
+
+// Index of the feedback factor and curve multipliers. below which the low
+// frequency correction curves must be used.
+const size_t kCurveChangeoverIndex = 51;
+
+// Offset and scaling values for the compensation curve polynomials. Used to
+// ensure the polynomial fitting was well conditioned.
+const float kCurveOffset = 3584.5f;
+const float kCurveScale = 0.00027897893709025f;
+
+// Number of coefficients in the curve generation polynomials.
+const size_t kCurvePolynomialLength = 5;
+
+// Coefficients of the curves making up the first part of the compensation
+// envelopes, split into low and high frequency part.
+const float kHighReverberationCorrectionCurve[kCurvePolynomialLength] = {
+ 0.207891278205479f, -0.32101050261694f, 0.124608132159297f,
+ 0.0119620847734548f, -0.0093840571415877f};
+
+const float kLowReverberationCorrectionCurve[kCurvePolynomialLength] = {
+ 0.0277040197264401f, -0.0394421854392145f, 0.0519110103856154f,
+ -0.0778183422829366f, 0.0343221444553963f};
+
+// Offset from zero for indexing into the feedback array.
+const size_t kReverbLookupOffset = static_cast<size_t>(
+ kMinReverbTimeForFeedback48kHz / kTimeStepSizeForFeedback48kHz);
+
+// Returns an index to the feedback factor corresponding to the given
+// reverberation time. Supports times between:
+// (0.15 * 48000 / |sample_rate|)s and (25 * 48000 / |sample_rate|)s.
+//
+// @param reverberation_time RT60 in seconds.
+// @return Index to the closest spectral reverb feedback factor in
+// |kSpectralReverbFeedback|, or |kInvalidIndex| if |reverberation_time|
+// is too small.
+static inline int GetFeedbackIndexFromRt60(float reverberation_time,
+ float sample_rate) {
+ // Scaling factor on reverb times applied due to current |sample_rate|. All
+ // feedback factors calculated correspond to 48kHz.
+ const float scale_factor = sample_rate / kDefaultSpectralReverbSampleRate;
+ if (reverberation_time * scale_factor < kMinReverbTimeForFeedback48kHz) {
+ return kInvalidIndex;
+ }
+ return static_cast<int>(
+ std::min(kNumFeedbackValues - 1,
+ static_cast<size_t>((reverberation_time * scale_factor) /
+ kTimeStepSizeForFeedback48kHz) -
+ kReverbLookupOffset));
+}
+
+// Mapping between reverberation times at a given frequency and the
+// corresponding feedback value required in the specral reverb. This table
+// covers reverberation times between kMinReverbTimeForFeedback and
+// kNumFeedbackValues * kTimeStepSizeForFeedback, i.e. 0.15s and 25s @48kHz.
+static const float kSpectralReverbFeedback[kNumFeedbackValues] = {
+ 0.231626448720861f, 0.231626448720861f, 0.271965742827169f,
+ 0.291599040391330f, 0.313450848869688f, 0.332864905289138f,
+ 0.350285532738638f, 0.366049684043223f, 0.383143567924904f,
+ 0.398574170268545f, 0.427500186399158f, 0.441038795823222f,
+ 0.453430889363735f, 0.466397294153677f, 0.478272702354267f,
+ 0.490519904155818f, 0.501757394995813f, 0.512125955040727f,
+ 0.521739757418252f, 0.548135448526531f, 0.548135448526531f,
+ 0.563990087718933f, 0.571497329505391f, 0.578784680803617f,
+ 0.585892965998708f, 0.592858971592466f, 0.599714945736390f,
+ 0.606488097764666f, 0.613200097727167f, 0.619866575922435f,
+ 0.626496622430680f, 0.633092286646765f, 0.639648076813205f,
+ 0.646150459553150f, 0.652577359403386f, 0.658897658347319f,
+ 0.665070695347976f, 0.671045765880981f, 0.676761621467567f,
+ 0.682145969207546f, 0.682145969207546f, 0.687114971312322f,
+ 0.691572744637866f, 0.695410860217716f, 0.700728670360266f,
+ 0.701931035813262f, 0.700570291691920f, 0.718477641957808f,
+ 0.718477641957808f, 0.721948612036913f, 0.725341703212013f,
+ 0.728658944837357f, 0.731902322614612f, 0.735073779107144f,
+ 0.738175214254303f, 0.741208485885712f, 0.744175410235544f,
+ 0.747077762456819f, 0.749917277135678f, 0.752695648805674f,
+ 0.755414532462056f, 0.758075544076055f, 0.760680261109167f,
+ 0.763230223027440f, 0.765726931815758f, 0.768171852492127f,
+ 0.770566413621959f, 0.772912007832361f, 0.775209992326414f,
+ 0.777461689397460f, 0.779668386943394f, 0.781831338980938f,
+ 0.783951766159935f, 0.786030856277629f, 0.788069764792952f,
+ 0.790069615340812f, 0.792031500246373f, 0.793956481039341f,
+ 0.795845588968255f, 0.797699825514762f, 0.799520162907914f,
+ 0.801307544638442f, 0.803062885973050f, 0.804787074468694f,
+ 0.806480970486870f, 0.808145407707900f, 0.809781193645214f,
+ 0.811389110159637f, 0.812969913973676f, 0.814524337185802f,
+ 0.816053087784737f, 0.817556850163737f, 0.819036285634880f,
+ 0.820492032943351f, 0.821924708781723f, 0.823334908304248f,
+ 0.824723205641138f, 0.826090154412852f, 0.827436288244380f,
+ 0.828762121279528f, 0.830068148695207f, 0.831354847215712f,
+ 0.833872075291032f, 0.835103470659940f, 0.836317269790432f,
+ 0.837513864858018f, 0.838693632671302f, 0.841004120020598f,
+ 0.841004120020598f, 0.842135520967878f, 0.843251458511968f,
+ 0.844352240341239f, 0.846509506717154f, 0.847566547291731f,
+ 0.848609545235926f, 0.849638751975006f, 0.850654409224479f,
+ 0.851656749504368f, 0.852645996653501f, 0.853622366343797f,
+ 0.854586066594550f, 0.855537298286716f, 0.856476255677194f,
+ 0.857403126913115f, 0.858318094546124f, 0.859221336046671f,
+ 0.860113024318288f, 0.860993328211881f, 0.861862413040010f,
+ 0.862720441091179f, 0.863567572144118f, 0.864403963982066f,
+ 0.865229772907064f, 0.866045154254231f, 0.866850262906056f,
+ 0.867645253806680f, 0.868430282476178f, 0.869205505524854f,
+ 0.869971081167516f, 0.870727169737766f, 0.871473934202282f,
+ 0.872211540675109f, 0.872940158931938f, 0.873659962924396f,
+ 0.874371131294327f, 0.875073847888079f, 0.876454690240667f,
+ 0.877133214343288f, 0.877804084385866f, 0.878467517951544f,
+ 0.879123740913686f, 0.879772987950149f, 0.880415503057582f,
+ 0.881051540065697f, 0.881681363151570f, 0.882305247353907f,
+ 0.882923479087349f, 0.883536356656744f, 0.884144190771436f,
+ 0.884747305059551f, 0.885346036582283f, 0.885940736348172f,
+ 0.886531769827401f, 0.887119517466070f, 0.887704375200491f,
+ 0.888286754971462f, 0.888867085238566f, 0.889355868225441f,
+ 0.889898852584961f, 0.890436987253855f, 0.890970321870216f,
+ 0.891498905661491f, 0.892022787446309f, 0.892542015636295f,
+ 0.893056638237894f, 0.893566702854191f, 0.894072256686731f,
+ 0.894573346537340f, 0.895070018809945f, 0.895562319512397f,
+ 0.896533988268773f, 0.897013446374392f, 0.897488713016890f,
+ 0.897959832251035f, 0.898426847746442f, 0.898889802789393f,
+ 0.899348740284655f, 0.899803702757303f, 0.900254732354541f,
+ 0.900701870847520f, 0.901145159633161f, 0.901584639735976f,
+ 0.902020351809884f, 0.902452336140039f, 0.902880632644642f,
+ 0.903305280876771f, 0.903726320026192f, 0.904143788921187f,
+ 0.904557726030372f, 0.904968169464516f, 0.905375156978364f,
+ 0.905778725972456f, 0.906178913494948f, 0.906575756243433f,
+ 0.906969290566763f, 0.907359552466865f, 0.907746577600566f,
+ 0.908130401281413f, 0.908511058481491f, 0.908888583833246f,
+ 0.909263011631306f, 0.909634375834297f, 0.910002710066671f,
+ 0.910368047620521f, 0.910730421457403f, 0.911446408184725f,
+ 0.911800085361980f, 0.912150927399534f, 0.912498965633569f,
+ 0.912844231080653f, 0.913186754439560f, 0.913526566093093f,
+ 0.913863696109903f, 0.914198174246311f, 0.914530029948127f,
+ 0.914859292352470f, 0.915185990289593f, 0.915510152284696f,
+ 0.915831806559755f, 0.916150981035335f, 0.916467703332418f,
+ 0.916782000774218f, 0.917093900388001f, 0.917403428906911f,
+ 0.917710612771787f, 0.918015478132983f, 0.918318050852190f,
+ 0.918916420379010f, 0.919212267483073f, 0.919505922541691f,
+ 0.919797410000546f, 0.920086754027583f, 0.920373978514824f,
+ 0.920659107080196f, 0.920942163069346f, 0.921223169557465f,
+ 0.921502149351104f, 0.921779124990001f, 0.922054118748897f,
+ 0.922327152639358f, 0.922598248411594f, 0.922867427556284f,
+ 0.923134711306391f, 0.923400120638986f, 0.923663676277069f,
+ 0.923925398691385f, 0.924185308102252f, 0.924443424481376f,
+ 0.924699767553671f, 0.924954356799086f, 0.925207211454416f,
+ 0.925458350515133f, 0.925707792737198f, 0.925955556638886f,
+ 0.926201660502607f, 0.926446122376723f, 0.926688960077372f,
+ 0.926930191190288f, 0.927169833072619f, 0.927407902854752f,
+ 0.927644417442129f, 0.927879393517071f, 0.928112847540597f,
+ 0.928344795754244f, 0.928575254181890f, 0.928804238631573f,
+ 0.929031764697310f, 0.929257847760921f, 0.929705745358969f,
+ 0.929927589612438f, 0.930148050305480f, 0.930367141786232f,
+ 0.930584878201552f, 0.930801273498844f, 0.931016341427878f,
+ 0.931230095542612f, 0.931442549203009f, 0.931653715576860f,
+ 0.931863607641606f, 0.932072238186154f, 0.932279619812703f,
+ 0.932485764938561f, 0.932690685797968f, 0.932894394443911f,
+ 0.933096902749953f, 0.933298222412048f, 0.933498364950364f,
+ 0.933697341711099f, 0.933895163868310f, 0.934091842425725f,
+ 0.934287388218568f, 0.934481811915380f, 0.934675124019838f,
+ 0.934867334872576f, 0.935058454653005f, 0.935437460919395f,
+ 0.935625366974452f, 0.935812221099035f, 0.935998032693751f,
+ 0.936182811008910f, 0.936182811008910f, 0.936366565146343f,
+ 0.936731036563891f, 0.936911771321662f, 0.937091516860660f,
+ 0.937270281567636f, 0.937448073691783f, 0.937624901346560f,
+ 0.937800772511514f, 0.937975695034098f, 0.938149676631492f,
+ 0.938322724892425f, 0.938494847278995f, 0.938666051128488f,
+ 0.938836343655203f, 0.939005731952265f, 0.939174222993452f,
+ 0.939341823635016f, 0.939508540617498f, 0.939674380567555f,
+ 0.939839349999772f, 0.940003455318495f, 0.940166702819640f,
+ 0.940329098692519f, 0.940490649021660f, 0.940651359788628f,
+ 0.940811236873844f, 0.940970286058407f, 0.941128513025914f,
+ 0.941285923364282f, 0.941442522567564f, 0.941598316037775f,
+ 0.941753309086713f, 0.941907506937771f, 0.942060914727768f,
+ 0.942213537508766f, 0.942365380249885f, 0.942516447839133f,
+ 0.942666745085221f, 0.942816276719382f, 0.942965047397198f,
+ 0.943113061700413f, 0.943260324138759f, 0.943406839151775f,
+ 0.943552611110629f, 0.943697644319934f, 0.943841943019572f,
+ 0.944128353536651f, 0.944270473526585f, 0.944411875355484f,
+ 0.944552562966882f, 0.944692540250507f, 0.944831811044098f,
+ 0.944970379135229f, 0.945108248263126f, 0.945245422120492f,
+ 0.945381904355324f, 0.945517698572732f, 0.945652808336767f,
+ 0.945787237172233f, 0.945920988566514f, 0.946054065971390f,
+ 0.946186472804861f, 0.946318212452965f, 0.946449288271601f,
+ 0.946579703588348f, 0.946709461704285f, 0.946838565895813f,
+ 0.946967019416476f, 0.947094825498780f, 0.947221987356014f,
+ 0.947348508184070f, 0.947474391163268f, 0.947599639460168f,
+ 0.947724256229401f, 0.947848244615479f, 0.947971607754624f,
+ 0.948016355753743f, 0.948131749028067f, 0.948256528604491f,
+ 0.948381070023128f, 0.948495820690419f, 0.948619906515573f,
+ 0.948734238085242f, 0.948857871450992f, 0.948971786801436f,
+ 0.949094970810335f, 0.949208472791043f, 0.949321777358027f,
+ 0.949444301946475f, 0.949557197308729f, 0.949669897241678f,
+ 0.949782402382633f, 0.949894713365863f, 0.950016165228152f,
+ 0.950128073740068f, 0.950239790030961f, 0.950351314722867f,
+ 0.950462648434873f, 0.950573791783135f, 0.950684745380899f,
+ 0.950786286676309f, 0.950896878289179f, 0.951007281923720f,
+ 0.951117498181945f, 0.951227527663044f, 0.951328224451603f,
+ 0.951437897607799f, 0.951547385718536f, 0.951647587765244f,
+ 0.951756722847544f, 0.952281634952469f, 0.952380709437317f,
+ 0.952479632641358f, 0.952587376840327f, 0.952685985134598f,
+ 0.952784443487780f, 0.952891682098038f, 0.952989828324488f,
+ 0.953087825932233f, 0.953185675345440f, 0.953283376986503f,
+ 0.953380931276052f, 0.953478338632963f, 0.953584434118178f,
+ 0.953681535598181f, 0.953778491429264f, 0.953875302023227f,
+ 0.953963185967004f, 0.954059720427442f, 0.954156110839052f,
+ 0.954252357607017f, 0.954348461134851f, 0.954444421824404f,
+ 0.954627224309327f, 0.954627224309327f, 0.954722771738916f,
+ 0.954818177885333f, 0.954999925992284f, 0.955086293022387f,
+ 0.955181163332660f, 0.955267288091851f, 0.955361892609296f,
+ 0.955447776377266f, 0.955542116504091f, 0.955627760550641f,
+ 0.955721837678226f, 0.955807243263361f, 0.955892535735487f,
+ 0.955986227146404f, 0.956071282999438f, 0.956156226611782f,
+ 0.956241058262726f, 0.956334244095430f, 0.956418841531991f,
+ 0.956503327866496f, 0.956587703374087f, 0.956671968328910f,
+ 0.956756123004115f, 0.956840167671866f, 0.956924102603339f,
+ 0.957007928068733f, 0.957091644337270f, 0.957175251677203f,
+ 0.957258750355817f, 0.957342140639439f, 0.957425422793436f,
+ 0.957508597082225f, 0.957591663769275f, 0.957666332005129f,
+ 0.957749194971355f, 0.957831951094582f, 0.957914600634626f,
+ 0.957988894306034f, 0.958071342050505f, 0.958153683960035f,
+ 0.958227701401528f, 0.958309842929826f, 0.958383680442363f,
+ 0.958465622508191f, 0.958539280915875f, 0.958621024431827f,
+ 0.958694504553236f, 0.958776050425793f, 0.958849353074026f,
+ 0.958930702203609f, 0.959003828186335f, 0.959076870765751f,
+ 0.959157931586863f, 0.959230798681224f, 0.959303582928693f,
+ 0.959384357364767f, 0.959456967296096f, 0.959529494931521f,
+ 0.959601940447006f, 0.959674304017958f, 0.959746585819239f,
+ 0.959826803241069f, 0.959898912989265f, 0.959970941508314f,
+ 0.960042888970841f, 0.960114755548935f, 0.960186541414150f,
+ 0.960258246737504f, 0.960329871689484f, 0.960401416440053f,
+ 0.960472881158639f, 0.960544266014151f, 0.960607652309644f,
+ 0.960678886771683f, 0.960750041855675f, 0.960821117728505f,
+ 0.960892114556548f, 0.960955156623649f, 0.961026004596989f,
+ 0.961096774003302f, 0.961167465006980f, 0.961230235767696f,
+ 0.961300779124251f, 0.961433815192630f, 0.961433815192630f,
+ 0.961504133859284f, 0.961566574305389f, 0.961636746788933f,
+ 0.961699057549504f, 0.961769084422862f, 0.961901147838506f,
+ 0.961963200748498f, 0.961963200748498f, 0.962032938107125f,
+ 0.962094862846269f, 0.962164456293847f, 0.962226253361271f,
+ 0.962287990519784f, 0.962357373349702f, 0.962418983565341f,
+ 0.962480534213617f, 0.962611132054796f, 0.962672497254291f,
+ 0.962733803332908f, 0.962795050398145f, 0.962863882940590f,
+ 0.962925004958022f, 0.962986068296167f, 0.963047073061348f,
+ 0.963108019359607f, 0.963168907296701f, 0.963229736978111f,
+ 0.963290508509035f, 0.963351221994394f, 0.963411877538831f,
+ 0.963472475246711f, 0.963533015222125f, 0.963593497568891f,
+ 0.963653922390548f, 0.963714289790367f, 0.963774599871346f,
+ 0.963834852736211f, 0.963895048487418f, 0.963947672999015f,
+ 0.964007761937330f, 0.964067794055055f, 0.964127769453608f,
+ 0.964187688234142f, 0.964240070801961f, 0.964299883695432f,
+ 0.964359640260232f, 0.964471532344270f, 0.964531127521006f,
+ 0.964583227407251f, 0.964642717773864f, 0.964642717773864f,
+ 0.964702152384023f, 0.964813442340238f, 0.964865310872176f,
+ 0.964924537272103f, 0.964976315013494f, 0.965035437824303f,
+ 0.965035437824303f, 0.965146144638393f, 0.965197741696581f,
+ 0.965256658352829f, 0.965308165515470f, 0.965366979602982f,
+ 0.965418397165533f, 0.965469773015420f, 0.965528437277655f,
+ 0.965528437277655f, 0.965630969126429f, 0.965689484282883f,
+ 0.965740640709502f, 0.965791755822677f, 0.965842829685402f,
+ 0.965901149384097f, 0.965952135064471f, 0.966003079691463f,
+ 0.966053983327487f, 0.966104846034818f, 0.966155667875590f,
+ 0.966213700018171f, 0.966264434496339f, 0.966315128302334f,
+ 0.966365781497721f, 0.966416394143929f, 0.966466966302251f,
+ 0.966517498033843f, 0.966567989399724f, 0.966618440460782f,
+ 0.966668851277767f, 0.966719221911296f, 0.966769552421851f,
+ 0.966819842869783f, 0.966870093315306f, 0.966920303818505f,
+ 0.966970474439330f, 0.967020605237600f, 0.967063542843005f,
+ 0.967113599843356f, 0.967163617191341f, 0.967213594946271f,
+ 0.967263533167330f, 0.967306305935844f, 0.967356170893433f,
+ 0.967405996485532f, 0.967505529807874f, 0.967548138931234f,
+ 0.967597813233595f, 0.967647448454308f, 0.967689961867422f,
+ 0.967739524661887f, 0.967789048540268f, 0.967831466648599f,
+ 0.967880918407252f, 0.967923274783712f, 0.967972654620856f,
+ 0.968021995812854f, 0.968064257548057f, 0.968113527121939f,
+ 0.968155727554356f, 0.968204925706321f, 0.968247065003786f,
+ 0.968296191929245f, 0.968338270258917f, 0.968387326152496f,
+ 0.968429343680863f, 0.968478328736411f, 0.968520285629296f,
+ 0.968562214592319f, 0.968611096462447f, 0.968652965031151f,
+ 0.968701776537117f, 0.968743584875698f, 0.968785365470961f,
+ 0.968834074480683f, 0.968875795084627f, 0.968917488055148f,
+ 0.968959153426817f, 0.969007728191248f, 0.969090912086360f,
+ 0.969132462833532f, 0.969180904050232f, 0.969222395427913f,
+ 0.969263859458335f, 0.969305296175547f, 0.969346705613531f,
+ 0.969394982191855f, 0.969436332641141f, 0.969477655918411f,
+ 0.969518952057392f, 0.969560221091750f, 0.969601463055090f,
+ 0.969642677980956f, 0.969683865902829f, 0.969725026854133f,
+ 0.969766160868228f, 0.969807267978414f, 0.969848348217932f,
+ 0.969889401619961f, 0.969930428217621f, 0.969971428043973f,
+ 0.970012401132016f, 0.970053347514690f, 0.970094267224876f,
+ 0.970135160295395f, 0.970176026759010f, 0.970216866648424f,
+ 0.970257679996279f, 0.970298466835163f, 0.970339227197601f,
+ 0.970373173964662f, 0.970413885871233f, 0.970454571393199f,
+ 0.970495230562863f, 0.970535863412469f, 0.970650847168530f,
+ 0.970691379422253f, 0.970731885510953f, 0.970765620620724f,
+ 0.970806078823052f, 0.970846510950618f, 0.970886917035171f,
+ 0.970920568901195f, 0.970960927322491f, 0.971001259790416f,
+ 0.971034850377613f, 0.971075135346213f, 0.971115394450634f,
+ 0.971148923969595f, 0.971189135737796f, 0.971222625853735f,
+ 0.971262790390816f, 0.971302929209645f, 0.971336358601896f,
+ 0.971376450351361f, 0.971409840562608f, 0.971449885346942f,
+ 0.971483236463907f, 0.971523234387005f, 0.971556546496124f,
+ 0.971596497661541f, 0.971629770848972f, 0.971669675359932f,
+ 0.971702909711553f, 0.971742767670943f, 0.971775963272355f,
+ 0.971809141288259f, 0.971848931719255f, 0.971921815239525f,
+ 0.971954916087967f, 0.971987999445650f, 0.972027676411655f,
+ 0.972060721351105f, 0.972093748855591f, 0.972133358871293f,
+ 0.972166348080007f, 0.972199319909290f, 0.972238863187940f,
+ 0.972271796843339f, 0.972304713174582f, 0.972344189928433f,
+ 0.972377068207112f, 0.972409929216652f, 0.972442772974193f,
+ 0.972482162734699f, 0.972514968598050f, 0.972547757264083f,
+ 0.972580528749833f, 0.972613283072310f, 0.972646020248501f,
+ 0.972685282250713f, 0.972717981764744f, 0.972750664186678f,
+ 0.972783329533400f, 0.972815977821766f, 0.972848609068610f,
+ 0.972881223290743f, 0.972920337908315f, 0.972952914735131f,
+ 0.972985474590863f, 0.973018017492218f, 0.973050543455880f,
+ 0.973083052498507f, 0.973115544636735f, 0.973148019887177f,
+ 0.973180478266418f, 0.973212919791023f, 0.973245344477530f,
+ 0.973277752342457f, 0.973310143402295f, 0.973342517673512f,
+ 0.973374875172553f, 0.973407215915839f, 0.973439539919767f,
+ 0.973471847200711f, 0.973504137775022f, 0.973562218760164f,
+ 0.973594462642685f, 0.973626689880490f, 0.973658900489817f,
+ 0.973691094486881f, 0.973723271887874f, 0.973755432708964f,
+ 0.973787576966297f, 0.973813280457093f, 0.973845394940276f,
+ 0.973877492904786f, 0.973909574366681f, 0.973941639341995f,
+ 0.973967279462671f, 0.973999314802478f, 0.974031333700481f,
+ 0.974063336172629f, 0.974088926334435f, 0.974120899280163f,
+ 0.974152855844570f, 0.974184796043519f, 0.974210336430194f,
+ 0.974242247211220f, 0.974274141671084f, 0.974299645498340f,
+ 0.974331510619852f, 0.974363359464321f, 0.974388826830985f,
+ 0.974420646416359f, 0.974452449768633f, 0.974477880773140f,
+ 0.974509654945262f, 0.974541412928054f, 0.974566807668448f,
+ 0.974598536549719f, 0.974630249285257f, 0.974655607859195f,
+ 0.974687291571531f, 0.974712626946994f, 0.974744281686313f,
+ 0.974769593903399f, 0.974801219719758f, 0.974826508818462f,
+ 0.974858105761792f, 0.974889686686078f, 0.974914939902112f,
+ 0.974946492030788f, 0.974971722230190f, 0.975003245612865f,
+ 0.975028452835272f, 0.975053649858377f, 0.975085131806375f,
+ 0.975110305909561f, 0.975141759232295f, 0.975166910454954f,
+ 0.975198335201602f, 0.975223463583028f, 0.975254859802644f,
+ 0.975279965382029f, 0.975305060839729f, 0.975336415939985f,
+ 0.975361488652084f, 0.975392815344700f, 0.975417865350151f,
+ 0.975442905276238f, 0.975474191021417f, 0.975499208296949f,
+ 0.975524215518001f, 0.975555460416840f, 0.975605419639930f,
+ 0.975605419639930f, 0.975636623793157f, 0.975661575850883f,
+ 0.975686517903629f, 0.975711449958978f, 0.975742600980867f,
+ 0.975767510569758f, 0.975792410185843f, 0.975823520693185f,
+ 0.975848397898037f, 0.975898122470337f, 0.975922969852819f,
+ 0.975922969852819f, 0.975954015123685f, 0.976003655334843f,
+ 0.976028460584524f, 0.976053255940233f, 0.976084236232837f,
+ 0.976109009354376f, 0.976133772606159f, 0.976158525995621f,
+ 0.976183269530186f, 0.976208003217274f, 0.976208003217274f,
+ 0.976238906489402f, 0.976263618046739f, 0.976288319780646f,
+ 0.976313011698507f, 0.976362366115577f, 0.976387028629510f,
+ 0.976411681356846f, 0.976436324304925f, 0.976467114249117f,
+ 0.976467114249117f, 0.976491735220672f, 0.976516346436775f,
+ 0.976565539631852f, 0.976590121625417f, 0.976614693892712f,
+ 0.976639256441014f, 0.976663809277591f, 0.976688352409702f,
+ 0.976712885844598f, 0.976737409589524f, 0.976761923651714f,
+ 0.976761923651714f, 0.976786428038397f, 0.976810922756793f,
+ 0.976835407814114f, 0.976859883217562f, 0.976884348974336f,
+ 0.976908805091622f, 0.976933251576602f, 0.976957688436448f,
+ 0.977098013951529f, 0.977122386028731f, 0.977146748536122f,
+ 0.977171101480806f, 0.977195444869879f, 0.977213696145079f,
+ 0.977238022828887f, 0.977262339976560f, 0.977286647595167f,
+ 0.977310945691766f, 0.977335234273410f, 0.977353444469693f,
+ 0.977377716417114f, 0.977401978868934f, 0.977426231832178f,
+ 0.977450475313860f, 0.977474709320991f, 0.977492878612876f,
+ 0.977517096056381f, 0.977541304044568f, 0.977565502584416f,
+ 0.977583645293024f, 0.977607827315053f, 0.977631999907899f,
+ 0.977631999907899f, 0.977674279277313f, 0.977698425975728f,
+ 0.977722563270990f, 0.977722563270990f, 0.977764780932182f,
+ 0.977788892404450f, 0.977812994499489f, 0.977831064921111f,
+ 0.977855150622588f, 0.977879226965781f, 0.977897278085939f,
+ 0.977921338068673f, 0.977945388712013f, 0.977963420569662f,
+ 0.977987454885549f, 0.978011479880872f, 0.978029492514857f,
+ 0.978053501215636f, 0.978077500614628f, 0.978095494063675f,
+ 0.978119477200933f, 0.978137458461494f, 0.978161425357848f,
+ 0.978185382983022f, 0.978203345121770f, 0.978227286538691f,
+ 0.978245236529027f, 0.978269161758424f, 0.978293077747100f,
+ 0.978311008678442f, 0.978334908512089f, 0.978352827334900f,
+ 0.978376711034146f, 0.978394617763882f, 0.978418485349318f,
+ 0.978436380001407f, 0.978460231493585f, 0.978478114083426f,
+ 0.978501949502858f, 0.978519820045822f, 0.978543639412983f,
+ 0.978561497924412f, 0.978585301259738f, 0.978603147754944f,
+ 0.978626935078835f, 0.978644769573101f, 0.978668540905916f,
+ 0.978686363414501f, 0.978710118776561f, 0.978727929314691f,
+ 0.978751668726282f, 0.978769467309155f, 0.978793190790523f,
+ 0.978810977433311f, 0.978828758963556f, 0.978852459722509f,
+ 0.978870229334292f, 0.978893914212034f, 0.978911671920457f,
+ 0.978929424532057f, 0.978953086757244f, 0.978970827487009f,
+ 0.978994473879778f, 0.979012202742743f, 0.979029926524532f,
+ 0.979053550334292f, 0.979071262270710f, 0.979088969135123f,
+ 0.979112570402636f, 0.979130265443050f, 0.979147955420607f,
+ 0.979171534186528f, 0.979189212361401f, 0.979206885482540f,
+ 0.979230441787417f, 0.979248103127132f, 0.979265759422211f,
+ 0.979289293306483f, 0.979306937841341f, 0.979324577340638f,
+ 0.979348088844638f, 0.979365716604861f, 0.979383339338575f,
+ 0.979400957048490f, 0.979424439518260f, 0.979442045516508f,
+ 0.979459646499977f, 0.979477242471370f, 0.979500695974660f,
+ 0.979518280261699f, 0.979535859545650f, 0.979553433829205f,
+ 0.979576858433533f, 0.979594421059955f, 0.979611978694939f,
+ 0.979629531341168f, 0.979647079001324f, 0.979670468130047f,
+ 0.979688004166443f, 0.979723061310450f, 0.979740582423400f,
+ 0.979763936178014f, 0.979763936178014f, 0.979781445700440f,
+ 0.979816449859165f, 0.979833944500784f, 0.979851434187449f,
+ 0.979874746066551f, 0.979874746066551f, 0.979909697391286f,
+ 0.979927165637130f, 0.979944628942151f, 0.979962087308993f,
+ 0.979979540740298f, 0.979996989238702f, 0.980020246234401f,
+ 0.980037683232959f, 0.980055115307399f, 0.980072542460351f,
+ 0.980089964694443f, 0.980107382012301f, 0.980124794416549f,
+ 0.980142201909809f, 0.980159604494699f, 0.980177002173838f,
+ 0.980194394949842f, 0.980211782825323f, 0.980229165802893f,
+ 0.980252335491858f, 0.980269707051114f, 0.980287073721149f,
+ 0.980304435504567f, 0.980321792403967f, 0.980339144421948f,
+ 0.980356491561107f, 0.980373833824038f, 0.980391171213333f,
+ 0.980408503731583f, 0.980425831381378f, 0.980443154165302f,
+ 0.980460472085940f, 0.980477785145876f, 0.980495093347688f,
+ 0.980512396693956f, 0.980529695187255f, 0.980546988830161f,
+ 0.980564277625245f, 0.980581561575078f, 0.980598840682228f,
+ 0.980616114949261f, 0.980627628439596f, 0.980644894645466f,
+ 0.980662156018053f, 0.980679412559917f, 0.980696664273614f,
+ 0.980713911161699f, 0.980731153226725f, 0.980748390471244f,
+ 0.980765622897805f, 0.980782850508954f, 0.980800073307237f,
+ 0.980817291295196f, 0.980845977259090f, 0.980863182431940f,
+ 0.980880382803776f, 0.980897578377130f, 0.980914769154535f,
+ 0.980931955138521f, 0.980931955138521f, 0.980949136331616f,
+ 0.980966312736347f, 0.980994929443608f, 0.981012093091701f,
+ 0.981012093091701f, 0.981029251960675f, 0.981046406053047f,
+ 0.981063555371332f, 0.981109263565884f, 0.981126395400670f,
+ 0.981126395400670f, 0.981143522473080f, 0.981172057017508f,
+ 0.981189171402487f, 0.981206281034263f, 0.981223385915333f,
+ 0.981240486048191f, 0.981251883500099f, 0.981268975724811f,
+ 0.981286063207956f, 0.981303145952021f, 0.981320223959493f,
+ 0.981331606667595f, 0.981348676786265f, 0.981365742174963f,
+ 0.981382802836166f, 0.981394173985141f, 0.981411226772692f,
+ 0.981428274839352f, 0.981445318187591f, 0.981456677799626f,
+ 0.981473713289321f, 0.981490744067179f, 0.981507770135665f,
+ 0.981519118232888f, 0.981536136457894f, 0.981553149980093f,
+ 0.981564489716769f, 0.981581495406417f, 0.981598496399810f,
+ 0.981615492699399f, 0.981626820959334f, 0.981643809441364f,
+ 0.981660793236125f, 0.981672113163134f, 0.981689089151212f,
+ 0.981706060458539f, 0.981717372064213f, 0.981734335575712f,
+ 0.981751294412966f, 0.981762597708870f, 0.981779548761129f,
+ 0.981796495145636f, 0.981807790143312f, 0.981824728753634f,
+ 0.981841662702683f, 0.981852949413651f, 0.981869875599303f,
+ 0.981886797130148f, 0.981898075565905f, 0.981914989344119f,
+ 0.981926262613694f, 0.981943168645996f, 0.981960070033971f,
+ 0.981971335046964f, 0.981988228699756f, 0.982005117714653f,
+ 0.982016374482499f, 0.982033255772922f, 0.982044507393347f,
+ 0.982061380965979f, 0.982089493304948f, 0.982106354542990f,
+ 0.982117592800940f, 0.982134446338523f, 0.982145679465052f,
+ 0.982162525308829f, 0.982179366539510f, 0.982190591464981f,
+ 0.982207425012481f, 0.982218644818040f, 0.982235470688987f,
+ 0.982246685379053f, 0.982263503580068f, 0.982274713159051f,
+ 0.982291523696748f, 0.982302728169054f, 0.982319531050036f,
+ 0.982330730420066f, 0.982347525650929f, 0.982364316294885f,
+ 0.982375507510410f, 0.982392290514783f, 0.982403476639445f,
+ 0.982420252010811f, 0.982431433048991f, 0.982448200793916f,
+ 0.982459376749988f, 0.982476136875028f, 0.982487307753362f,
+ 0.982498476601823f, 0.982515226070027f, 0.982526389846857f,
+ 0.982543131710882f, 0.982554290420436f, 0.982571024686812f,
+ 0.982582178333442f, 0.982598905008691f, 0.982610053596740f,
+ 0.982626772687374f, 0.982637916221183f, 0.982654627733708f,
+ 0.982665766217610f, 0.982676902682761f, 0.982693603596846f,
+ 0.982704735018149f, 0.982721428369703f, 0.982732554751479f,
+ 0.982749240546979f, 0.982760361893543f, 0.982771481227230f,
+ 0.982788156455121f, 0.982799270759628f, 0.982815938446979f,
+ 0.982827047726609f, 0.982838154997492f, 0.982854812138917f,
+ 0.982865914390938f, 0.982882564007288f, 0.982893661244735f,
+ 0.982904756477552f, 0.982921395569597f, 0.982932485793838f,
+ 0.982949117376225f, 0.982960202596166f, 0.982971285815582f,
+ 0.982987906895218f, 0.982998985116313f, 0.983010061339272f,
+ 0.983026671928701f, 0.983037743159306f, 0.983048812394161f,
+ 0.983065412505902f, 0.983076476754361f, 0.983087539009450f,
+ 0.983104128656004f, 0.983115185930644f, 0.983126241214290f,
+ 0.983142820408134f, 0.983164919037780f, 0.983164919037780f,
+ 0.983181487791369f, 0.983203572508969f, 0.983203572508969f,
+ 0.983220130834737f, 0.983242201656853f, 0.983242201656853f,
+ 0.983258749567212f, 0.983280806510381f, 0.983280806510381f,
+ 0.983297344017722f, 0.983308366547671f, 0.983330405670727f,
+ 0.983330405670727f, 0.983346929820930f, 0.983368955102724f,
+ 0.983379964780070f, 0.983396475593535f, 0.983396475593535f,
+ 0.983418483103573f, 0.983418483103573f, 0.983434983558095f,
+ 0.983445981396647f, 0.983467971162045f, 0.983467971162045f,
+ 0.983484458315984f, 0.983495447291238f, 0.983517419339052f,
+ 0.983517419339052f, 0.983533893212681f, 0.983555851498819f,
+ 0.983566827695369f, 0.983577801928461f, 0.983594259598068f,
+ 0.983605228925419f, 0.983616196291637f, 0.983627161697383f,
+ 0.983627161697383f, 0.983643606131571f, 0.983654566639040f,
+ 0.983676481780187f, 0.983687436415187f, 0.983703864700081f,
+ 0.983703864700081f, 0.983714814445903f, 0.983736708074663f,
+ 0.983747651958919f, 0.983758593890641f, 0.983775003128660f,
+ 0.983785940181924f, 0.983829668899257f, 0.983846059132147f,
+ 0.983856983520667f, 0.983867905963228f, 0.983878826460486f,
+ 0.983900661621713f, 0.983917032891085f, 0.983917032891085f,
+ 0.983927944642585f, 0.983938854452384f, 0.983960668249489f,
+ 0.983971572238103f, 0.983982474287629f, 0.983998823727552f,
+ 0.983998823727552f, 0.984020616200059f, 0.984031509531754f,
+ 0.984042400927945f, 0.984053290389283f, 0.984064177916417f,
+ 0.984075063509998f, 0.984085947170676f, 0.984102269038919f,
+ 0.984113147870179f, 0.984124024770807f, 0.984134899741452f,
+ 0.984145772782762f, 0.984156643895383f, 0.984167513079964f,
+ 0.984178380337151f, 0.984189245667592f, 0.984200109071931f,
+ 0.984210970550817f, 0.984227259160331f, 0.984238115828405f,
+ 0.984248970573283f, 0.984259823395610f, 0.984270674296031f,
+ 0.984281523275189f, 0.984292370333728f, 0.984303215472291f,
+ 0.984314058691521f, 0.984324899992062f, 0.984335739374555f,
+ 0.984346576839642f, 0.984357412387966f, 0.984368246020168f,
+ 0.984379077736889f, 0.984389907538769f, 0.984400735426450f,
+ 0.984411561400571f, 0.984422385461773f, 0.984433207610695f,
+ 0.984444027847976f, 0.984460254620969f, 0.984471070081943f,
+ 0.984481883633511f, 0.984492695276311f, 0.984503505010981f,
+ 0.984514312838157f, 0.984525118758478f, 0.984535922772579f,
+ 0.984546724881096f, 0.984557525084666f, 0.984568323383925f,
+ 0.984579119779507f, 0.984589914272047f, 0.984600706862181f,
+ 0.984611497550543f, 0.984622286337767f, 0.984633073224487f,
+ 0.984643858211335f, 0.984649249992506f, 0.984670812370678f,
+ 0.984681590712963f, 0.984692367158224f, 0.984703141707094f,
+ 0.984703141707094f, 0.984713914360202f, 0.984724685118182f,
+ 0.984735453981663f, 0.984746220951275f, 0.984756986027650f,
+ 0.984767749211417f, 0.984778510503206f, 0.984789269903646f,
+ 0.984800027413365f, 0.984810783032994f, 0.984821536763160f,
+ 0.984832288604491f, 0.984843038557615f, 0.984853786623160f,
+ 0.984869905183637f, 0.984880648532976f, 0.984891389996930f,
+ 0.984902129576124f, 0.984912867271185f, 0.984923603082736f,
+ 0.984934337011405f, 0.984945069057816f, 0.984955799222593f,
+ 0.984966527506360f, 0.984977253909743f, 0.984987978433363f,
+ 0.984993339990457f, 0.985004061695604f, 0.985014781522546f,
+ 0.985025499471908f, 0.985036215544310f, 0.985046929740375f,
+ 0.985057642060725f, 0.985068352505981f, 0.985079061076764f,
+ 0.985084414659422f, 0.985095120419660f, 0.985105824306976f,
+ 0.985116526321991f, 0.985127226465325f, 0.985137924737595f,
+ 0.985148621139423f, 0.985159315671427f, 0.985164662236437f,
+ 0.985175353964864f, 0.985186043825012f, 0.985196731817500f,
+ 0.985207417942944f, 0.985218102201962f, 0.985228784595170f,
+ 0.985244804687940f, 0.985255482419322f, 0.985266158287053f,
+ 0.985266158287053f, 0.985276832291746f, 0.985292839806936f,
+ 0.985303509156728f, 0.985314176645634f, 0.985324842274270f,
+ 0.985324842274270f, 0.985335506043249f, 0.985351498211202f,
+ 0.985362157333723f, 0.985372814598732f, 0.985383470006843f,
+ 0.985383470006843f, 0.985399449638666f, 0.985410100407211f,
+ 0.985420749320998f, 0.985431396380641f, 0.985431396380641f,
+ 0.985442041586749f, 0.985458005921870f, 0.985468646496813f,
+ 0.985479285220358f, 0.985479285220358f, 0.985495239835637f,
+ 0.985505873933352f, 0.985516506181800f, 0.985527136581592f,
+ 0.985527136581592f, 0.985543078716377f, 0.985553704497184f,
+ 0.985564328431462f, 0.985574950519818f, 0.985585570762857f,
+ 0.985590880192573f, 0.985601497668777f, 0.985612113301180f,
+ 0.985622727090390f, 0.985628033293987f, 0.985638644319542f,
+ 0.985649253503417f, 0.985659860846216f, 0.985665163827402f,
+ 0.985675768409722f, 0.985686371152477f, 0.985696972056273f,
+ 0.985702271818749f, 0.985712869965236f, 0.985723466274270f,
+ 0.985734060746455f, 0.985739357293918f, 0.985749949011959f,
+ 0.985760538894657f, 0.985776420278752f, 0.985787005575724f,
+ 0.985797589039459f, 0.985802880084049f, 0.985813460799053f,
+ 0.985824039682320f, 0.985834616734451f, 0.985839904574028f,
+ 0.985850478880578f, 0.985861051357492f, 0.985866336910022f,
+ 0.985876906643602f, 0.985887474549043f, 0.985898040626941f,
+ 0.985903322980749f, 0.985913886318456f, 0.985924447830116f,
+ 0.985929727901365f, 0.985940286675075f, 0.985950843624230f,
+ 0.985956121414786f, 0.985966675628229f, 0.985977228018608f,
+ 0.985987778586520f, 0.985993053187237f, 0.986003601022566f,
+ 0.986014147036915f, 0.986019419361409f, 0.986029962645406f,
+ 0.986040504109912f, 0.986045774160041f, 0.986056312896423f,
+ 0.986066849814798f, 0.986072117592419f, 0.986082651784897f,
+ 0.986093184160851f, 0.986098449667817f, 0.986108979320098f,
+ 0.986119507157337f, 0.986124770395501f, 0.986135295511287f,
+ 0.986145818813510f, 0.986151079784721f, 0.986161600367710f,
+ 0.986166859979636f, 0.986177377844719f, 0.986187893898605f,
+ 0.986193151246533f, 0.986203664584727f, 0.986214176113198f,
+ 0.986219431198972f, 0.986229940013964f, 0.986240447020705f,
+ 0.986245699846166f, 0.986256204141637f, 0.986261455611793f,
+ 0.986271957197316f, 0.986282456976943f, 0.986287706189730f,
+ 0.986298203261614f, 0.986308698529071f, 0.986313945486323f,
+ 0.986324438048239f, 0.986329683653050f, 0.986340173510745f,
+ 0.986350661566360f, 0.986355904918569f, 0.986366390272160f,
+ 0.986382114926572f, 0.986382114926572f, 0.986392595779718f,
+ 0.986397835531571f, 0.986413552089130f, 0.986424027546565f,
+ 0.986424027546565f, 0.986434501206598f, 0.986450208327797f,
+ 0.986455443136794f, 0.986465911408123f, 0.986465911408123f,
+ 0.986481610449544f, 0.986492074234293f, 0.986497305454026f,
+ 0.986507766548573f, 0.986512996423533f, 0.986523454829188f,
+ 0.986533911442975f, 0.986539139078099f, 0.986549593005172f,
+ 0.986554819297267f, 0.986565270538934f, 0.986570495488651f,
+ 0.986580944046217f, 0.986591390815106f, 0.986596613528979f,
+ 0.986607057615941f, 0.986612278989175f, 0.986622720395512f,
+ 0.986627940428760f, 0.986638379155774f, 0.986643597849686f,
+ 0.986654033898679f, 0.986659251253905f, 0.986669684626176f,
+ 0.986680116214689f, 0.986685331340216f, 0.986695760254172f,
+ 0.986700974042745f, 0.986711400283442f, 0.986716612735710f,
+ 0.986727036304445f, 0.986732247421057f, 0.986742668319126f,
+ 0.986747878100727f, 0.986758296329425f, 0.986763504776665f,
+ 0.986773920337285f, 0.986779127450809f, 0.986789540344645f,
+ 0.986794746125101f, 0.986805156353445f, 0.986810360801477f,
+ 0.986820768365621f, 0.986831174154347f, 0.986836376383108f,
+ 0.986846779509784f, 0.986851980407841f, 0.986862380873755f,
+ 0.986867580441755f, 0.986877978248194f, 0.986883176486778f,
+ 0.986893571635030f, 0.986898768544842f, 0.986909161036194f,
+ 0.986914356617876f, 0.986924746453612f, 0.986935134519650f,
+ 0.986945520816562f, 0.986950713301773f, 0.986961096946065f,
+ 0.986966288105286f, 0.986976669098239f, 0.986981858932111f,
+ 0.986992237275006f, 0.986997425784170f, 0.987007801478288f,
+ 0.987012988663383f, 0.987023361710002f, 0.987028547571668f,
+ 0.987038917972068f, 0.987044102510944f, 0.987054470266402f,
+ 0.987059653483127f, 0.987059653483127f, 0.987070018594919f,
+ 0.987080381944977f, 0.987090743533868f, 0.987095923668051f,
+ 0.987106282616252f, 0.987111461430409f, 0.987121817739193f,
+ 0.987126995233961f, 0.987137348904601f, 0.987142525080615f,
+ 0.987147700817186f, 0.987158050972279f, 0.987163225390944f,
+ 0.987173572910860f, 0.987178746012253f, 0.987189090898262f,
+ 0.987194262683019f, 0.987204604936389f, 0.987209775405143f,
+ 0.987214945435371f, 0.987225284180528f, 0.987230452895598f,
+ 0.987240789011073f, 0.987245956411619f, 0.987256289898679f,
+ 0.987261455985333f, 0.987292443313425f, 0.987276951618636f,
+ 0.987302768922823f, 0.987313092783174f, 0.987318254057631f,
+ 0.987328575295461f, 0.987333735258974f, 0.987344053875544f,
+ 0.987349212528741f, 0.987354370745308f, 0.987364685868824f,
+ 0.987369842775915f, 0.987380155281111f, 0.987385310879356f,
+ 0.987390466041458f, 0.987400775057515f, 0.987405928911610f,
+ 0.987416235312279f, 0.987421387858994f, 0.987426539970055f,
+ 0.987436842885494f, 0.987441993690012f, 0.987447144059154f,
+ 0.987457443491593f, 0.987462592555029f, 0.987472889376680f,
+ 0.987478037135035f, 0.987483184458503f, 0.987493477801052f,
+ 0.987503769404885f, 0.987503769404885f, 0.987514059270557f,
+ 0.987519203551756f, 0.987529490811229f, 0.987534633789641f,
+ 0.987539776333929f, 0.987550060120412f, 0.987555201362745f,
+ 0.987560342171233f, 0.987570622486945f, 0.987580901068104f,
+ 0.987580901068104f, 0.987591177915262f, 0.987596315688765f,
+ 0.987606589935962f, 0.987611726409794f, 0.987616862450543f,
+ 0.987627133233060f, 0.987632267974967f, 0.987637402284067f,
+ 0.987647669604116f, 0.987652802615204f, 0.987657935193759f,
+ 0.987668199053549f, 0.987673330334921f, 0.987678461184036f,
+ 0.987688721585774f, 0.987693851138533f, 0.987698980259311f,
+ 0.987709237205201f, 0.987714365030449f, 0.987719492423992f,
+ 0.987729745916237f, 0.987734872015076f, 0.987739997682485f,
+ 0.987750247723285f, 0.987760496039188f, 0.987760496039188f,
+ 0.987770742630743f, 0.987780987498498f, 0.987780987498498f,
+ 0.987791230643004f, 0.987796351569210f, 0.987801472064809f,
+ 0.987811711764460f, 0.987816830968650f, 0.987827068086100f,
+ 0.987827068086100f, 0.987837303482766f, 0.987842420535977f,
+ 0.987847537159197f, 0.987857769115940f, 0.987862884449599f,
+ 0.987867999353541f, 0.987878227872547f, 0.987883341487748f,
+ 0.987888454673505f, 0.987898679756961f, 0.987903791654796f,
+ 0.987908903123460f, 0.987914014163022f, 0.987924234955111f,
+ 0.987929344707774f, 0.987934454031607f, 0.987944671393056f,
+ 0.987949779430808f, 0.987959994220708f, 0.987959994220708f,
+ 0.987970207296921f, 0.987975313192566f, 0.987995732493650f,
+ 0.988000836248887f, 0.988005939576247f, 0.988016144947605f,
+ 0.988021246991739f, 0.988026348608267f, 0.988031449797257f,
+ 0.988041650892892f, 0.988046750799674f, 0.988051850279188f,
+ 0.988062047956687f, 0.988067146154806f, 0.988072243925930f,
+ 0.988077341270126f, 0.988087534678001f, 0.988092630741817f,
+ 0.988097726378975f, 0.988102821589542f, 0.988113010731178f,
+ 0.988118104662380f, 0.988123198167263f, 0.988128291245894f,
+ 0.988138476124667f, 0.988143567924946f, 0.988148659299242f,
+ 0.988153750247623f, 0.988163930866911f, 0.988169020537952f,
+ 0.988174109783348f, 0.988179198603165f, 0.988189374966338f,
+ 0.988194462509826f, 0.988199549628007f, 0.988204636320946f,
+ 0.988214808431370f, 0.988219893848989f, 0.988224978841637f,
+ 0.988230063409379f, 0.988235147552284f, 0.988245314563850f,
+ 0.988250397432645f, 0.988255479876871f, 0.988260561896595f,
+ 0.988270724662807f, 0.988275805409428f, 0.988280885731816f,
+ 0.988285965630037f, 0.988296124154249f, 0.988301202780374f,
+ 0.988306280982600f, 0.988311358760995f, 0.988316436115625f,
+ 0.988331665637599f, 0.988336741297841f, 0.988341816534654f,
+ 0.988346891348103f, 0.988357039705180f, 0.988362113248942f,
+ 0.988367186369607f, 0.988372259067245f, 0.988377331341919f,
+ 0.988387474622650f, 0.988392545628840f, 0.988397616212334f,
+ 0.988402686373199f, 0.988407756111503f, 0.988417894320692f,
+ 0.988422962791711f, 0.988428030840435f, 0.988433098466929f,
+ 0.988438165671262f, 0.988448298813708f, 0.988448298813708f,
+ 0.988453364751954f, 0.988463495362826f, 0.988468560035585f,
+ 0.988478688116080f, 0.988478688116080f, 0.988488814510321f,
+ 0.988493877075263f, 0.988498939218840f, 0.988509062242168f,
+ 0.988509062242168f, 0.988514123122051f, 0.988519183580837f,
+ 0.988529303235375f, 0.988534362431262f, 0.988534362431262f,
+ 0.988544479560602f, 0.988549537494187f, 0.988554595007138f,
+ 0.988564708771400f, 0.988569765022844f, 0.988579876264688f,
+ 0.988584931255221f, 0.988589985825582f, 0.988595039975838f,
+ 0.988605147016298f, 0.988605147016298f, 0.988615252377130f,
+ 0.988620304427851f, 0.988625356058862f, 0.988630407270231f,
+ 0.988635458062022f, 0.988640508434303f, 0.988650607920595f,
+ 0.988655657034738f, 0.988660705729634f, 0.988665754005349f,
+ 0.988670801861948f, 0.988680896318064f, 0.988680896318064f,
+ 0.988690989098507f, 0.988696034860517f, 0.988701080203806f,
+ 0.988706125128441f, 0.988711169634487f, 0.988716213722009f,
+ 0.988721257391074f, 0.988731343474094f, 0.988736385888181f,
+ 0.988741427884073f, 0.988746469461837f, 0.988751510621536f,
+ 0.988761591687008f, 0.988771671081014f, 0.988771671081014f,
+ 0.988781748804078f, 0.988786787039171f, 0.988791824856726f,
+ 0.988796862256806f, 0.988801899239479f, 0.988806935804810f,
+ 0.988817007683707f, 0.988817007683707f, 0.988822042997403f,
+ 0.988827077894019f, 0.988837146436271f, 0.988842180082038f,
+ 0.988847213310986f, 0.988852246123180f, 0.988862310497568f,
+ 0.988862310497568f, 0.988867342059893f, 0.988872373205725f,
+ 0.988877403935130f, 0.988887464144920f, 0.988892493625434f,
+ 0.988897522689783f, 0.988902551338030f, 0.988912607386482f,
+ 0.988912607386482f, 0.988922661771312f, 0.988927688340031f,
+ 0.988932714493039f, 0.988937740230403f, 0.988942765552186f,
+ 0.988947790458455f, 0.988952814949272f, 0.988957839024706f,
+ 0.988967885929677f, 0.988967885929677f, 0.988972908759345f,
+ 0.988982953173371f, 0.988987974757859f, 0.988992995927416f,
+ 0.988998016682109f, 0.989003037022000f, 0.989008056947156f,
+ 0.989013076457642f, 0.989018095553521f, 0.989028132501721f,
+ 0.989033150354172f, 0.989038167792276f, 0.989043184816098f,
+ 0.989048201425703f, 0.989053217621155f, 0.989058233402521f,
+ 0.989063248769862f, 0.989068263723246f, 0.989073278262736f,
+ 0.989078292388397f, 0.989083306100294f, 0.989093332283054f,
+ 0.989093332283054f, 0.989098344754046f, 0.989103356811533f,
+ 0.989108368455578f, 0.989113379686246f, 0.989123400907711f,
+ 0.989128410898637f, 0.989133420476444f, 0.989138429641197f,
+ 0.989143438392961f, 0.989148446731799f, 0.989153454657776f,
+ 0.989158462170958f, 0.989163469271408f, 0.989173482234369f,
+ 0.989173482234369f, 0.989178488097010f, 0.989188498584932f,
+ 0.989193503210343f, 0.989198507423472f, 0.989203511224385f,
+ 0.989208514613144f, 0.989213517589816f, 0.989218520154462f,
+ 0.989223522307149f, 0.989228524047940f, 0.989233525376900f,
+ 0.989238526294092f, 0.989243526799581f, 0.989248526893431f,
+ 0.989253526575707f, 0.989258525846471f, 0.989263524705789f,
+ 0.989268523153725f, 0.989278518815704f, 0.989283516029877f,
+ 0.989288512832923f, 0.989293509224907f, 0.989298505205893f,
+ 0.989303500775945f, 0.989308495935126f, 0.989313490683502f,
+ 0.989318485021135f, 0.989323478948089f, 0.989328472464430f,
+ 0.989333465570220f, 0.989338458265524f, 0.989343450550404f,
+ 0.989348442424926f, 0.989353433889154f, 0.989358424943150f,
+ 0.989363415586979f, 0.989368405820704f, 0.989373395644390f,
+ 0.989378385058100f, 0.989383374061898f, 0.989388362655847f,
+ 0.989393350840012f, 0.989398338614456f, 0.989403325979243f,
+ 0.989408312934436f, 0.989413299480100f, 0.989418285616298f,
+ 0.989423271343093f, 0.989428256660550f, 0.989433241568731f,
+ 0.989438226067700f, 0.989443210157522f, 0.989448193838259f,
+ 0.989453177109976f, 0.989458159972735f, 0.989463142426600f,
+ 0.989468124471636f, 0.989473106107904f, 0.989478087335469f,
+ 0.989483068154395f, 0.989488048564745f, 0.989493028566581f,
+ 0.989498008159969f, 0.989502987344970f, 0.989507966121648f,
+ 0.989512944490068f, 0.989517922450292f, 0.989522900002383f,
+ 0.989527877146405f, 0.989532853882422f, 0.989537830210496f,
+ 0.989542806130691f, 0.989547781643070f, 0.989552756747697f,
+ 0.989557731444634f, 0.989562705733946f, 0.989567679615695f,
+ 0.989572653089944f, 0.989577626156756f, 0.989582598816196f,
+ 0.989587571068325f, 0.989592542913208f, 0.989597514350907f,
+ 0.989602485381486f, 0.989607456005007f, 0.989612426221533f,
+ 0.989617396031129f, 0.989622365433856f, 0.989627334429779f,
+ 0.989632303018959f, 0.989637271201460f, 0.989642238977345f,
+ 0.989647206346678f, 0.989652173309519f, 0.989657139865935f,
+ 0.989662106015986f, 0.989667071759736f, 0.989667071759736f,
+ 0.989672037097247f, 0.989677002028584f, 0.989681966553808f,
+ 0.989686930672983f, 0.989691894386171f, 0.989696857693435f,
+ 0.989701820594839f, 0.989706783090444f, 0.989711745180314f,
+ 0.989716706864512f, 0.989721668143100f, 0.989726629016141f,
+ 0.989731589483698f, 0.989736549545834f, 0.989741509202611f,
+ 0.989746468454092f, 0.989751427300340f, 0.989756385741417f,
+ 0.989761343777386f, 0.989761343777386f, 0.989766301408311f,
+ 0.989771258634252f, 0.989776215455275f, 0.989781171871439f,
+ 0.989786127882809f, 0.989791083489447f, 0.989796038691415f,
+ 0.989800993488776f, 0.989805947881593f, 0.989810901869928f,
+ 0.989815855453843f, 0.989820808633402f, 0.989825761408667f,
+ 0.989830713779699f, 0.989835665746562f, 0.989840617309318f,
+ 0.989840617309318f, 0.989845568468030f, 0.989850519222759f,
+ 0.989855469573569f, 0.989860419520521f, 0.989865369063679f,
+ 0.989870318203104f, 0.989875266938858f, 0.989880215271005f,
+ 0.989885163199606f, 0.989885163199606f, 0.989890110724724f,
+ 0.989895057846421f, 0.989900004564759f, 0.989904950879801f,
+ 0.989909896791608f, 0.989914842300244f, 0.989919787405770f,
+ 0.989924732108248f, 0.989929676407741f, 0.989934620304311f,
+ 0.989939563798019f, 0.989944506888929f, 0.989944506888929f,
+ 0.989949449577102f, 0.989954391862601f, 0.989959333745488f,
+ 0.989964275225823f, 0.989969216303671f, 0.989974156979092f,
+ 0.989979097252149f, 0.989984037122904f, 0.989988976591419f,
+ 0.989993915657756f, 0.990102473491435f, 0.989998854321976f,
+ 0.990003792584142f, 0.990008730444317f, 0.990013667902561f,
+ 0.990018604958937f, 0.990127118651726f, 0.990132046482196f,
+ 0.990136973912279f, 0.990141900942038f, 0.990146827571534f,
+ 0.990097543257357f, 0.990048218861426f, 0.990053153106457f,
+ 0.990058086950115f, 0.990166530088123f, 0.990171454717229f,
+ 0.990176378946441f, 0.990181302775822f, 0.990077818312251f,
+ 0.990082750149969f, 0.990087681586685f, 0.990092612622461f,
+ 0.990200994096251f, 0.990205915927393f, 0.990210837359071f,
+ 0.990215758391347f, 0.990220679024282f, 0.990220679024282f,
+ 0.990225599257938f, 0.990230519092376f, 0.990235438527657f,
+ 0.990240357563843f, 0.990245276200994f, 0.990250194439173f,
+ 0.990255112278440f, 0.990255112278440f, 0.990260029718857f,
+ 0.990264946760485f, 0.990269863403386f, 0.990274779647619f,
+ 0.990279695493247f, 0.990284610940331f, 0.990284610940331f,
+ 0.990289525988932f, 0.990294440639111f, 0.990299354890930f,
+ 0.990304268744449f, 0.990309182199729f, 0.990314095256832f,
+ 0.990314095256832f, 0.990319007915819f, 0.990323920176751f,
+ 0.990328832039688f, 0.990333743504692f, 0.990338654571825f,
+ 0.990338654571825f, 0.990343565241146f, 0.990348475512717f,
+ 0.990353385386600f, 0.990358294862854f, 0.990363203941541f,
+ 0.990363203941541f, 0.990368112622723f, 0.990373020906459f,
+ 0.990377928792811f, 0.990382836281839f, 0.990387743373605f,
+ 0.990387743373605f, 0.990392650068170f, 0.990397556365594f,
+ 0.990402462265938f, 0.990407367769264f, 0.990412272875631f,
+ 0.990412272875631f, 0.990417177585100f, 0.990422081897733f,
+ 0.990426985813592f, 0.990431889332734f, 0.990436792455222f,
+ 0.990441695181118f, 0.990446597510480f, 0.990446597510480f,
+ 0.990451499443371f, 0.990456400979850f, 0.990461302119979f,
+ 0.990466202863817f, 0.990471103211427f, 0.990471103211427f,
+ 0.990476003162868f, 0.990480902718201f, 0.990485801877487f,
+ 0.990490700640787f, 0.990490700640787f, 0.990495599008160f,
+ 0.990500496979667f, 0.990505394555370f, 0.990510291735329f,
+ 0.990515188519603f, 0.990515188519603f, 0.990520084908255f,
+ 0.990524980901343f, 0.990529876498930f, 0.990534771701074f,
+ 0.990539666507838f, 0.990539666507838f, 0.990544560919280f,
+ 0.990549454935462f, 0.990554348556445f, 0.990559241782288f,
+ 0.990564134613051f, 0.990564134613051f, 0.990569027048796f,
+ 0.990573919089582f, 0.990578810735471f, 0.990583701986522f,
+ 0.990583701986522f, 0.990588592842796f, 0.990593483304352f,
+ 0.990598373371253f, 0.990598373371253f, 0.990603263043557f,
+ 0.990608152321324f, 0.990613041204617f, 0.990617929693493f,
+ 0.990622817788014f, 0.990622817788014f, 0.990627705488241f,
+ 0.990632592794232f, 0.990637479706049f, 0.990642366223752f,
+ 0.990642366223752f, 0.990647252347400f, 0.990652138077054f,
+ 0.990657023412774f, 0.990661908354621f, 0.990661908354621f,
+ 0.990666792902654f, 0.990671677056933f, 0.990676560817519f,
+ 0.990681444184472f, 0.990681444184472f, 0.990686327157852f,
+ 0.990691209737718f, 0.990696091924130f, 0.990700973717151f,
+ 0.990700973717151f, 0.990705855116837f, 0.990710736123251f,
+ 0.990715616736451f, 0.990715616736451f, 0.990720496956498f,
+ 0.990725376783453f, 0.990730256217374f, 0.990735135258321f,
+ 0.990735135258321f, 0.990740013906355f, 0.990744892161536f,
+ 0.990749770023923f, 0.990754647493576f, 0.990754647493576f,
+ 0.990759524570556f, 0.990764401254922f, 0.990769277546733f,
+ 0.990774153446051f, 0.990774153446051f, 0.990779028952933f,
+ 0.990783904067441f, 0.990788778789634f, 0.990788778789634f,
+ 0.990793653119573f, 0.990798527057315f, 0.990803400602923f,
+ 0.990808273756453f, 0.990808273756453f, 0.990813146517969f,
+ 0.990818018887528f, 0.990822890865190f, 0.990827762451014f,
+ 0.990827762451014f, 0.990832633645062f, 0.990837504447392f,
+ 0.990842374858063f, 0.990842374858063f, 0.990847244877137f,
+ 0.990852114504671f, 0.990856983740726f, 0.990856983740726f,
+ 0.990861852585361f, 0.990866721038637f, 0.990871589100612f,
+ 0.990876456771346f, 0.990876456771346f, 0.990881324050898f,
+ 0.990886190939329f, 0.990891057436697f, 0.990891057436697f,
+ 0.990895923543062f, 0.990900789258484f, 0.990905654583022f,
+ 0.990905654583022f, 0.990910519516736f, 0.990915384059684f,
+ 0.990920248211927f, 0.990920248211927f, 0.990925111973523f,
+ 0.990929975344533f, 0.990934838325015f, 0.990939700915029f,
+ 0.990939700915029f, 0.990944563114635f, 0.990949424923891f,
+ 0.990954286342857f, 0.990954286342857f, 0.990959147371593f,
+ 0.990964008010157f, 0.990968868258609f, 0.990968868258609f,
+ 0.990973728117009f, 0.990978587585414f, 0.990983446663886f,
+ 0.990988305352482f, 0.990988305352482f, 0.990993163651263f,
+ 0.990998021560288f, 0.991002879079614f, 0.991002879079614f,
+ 0.991007736209303f, 0.991012592949413f, 0.991017449300003f,
+ 0.991017449300003f, 0.991022305261132f, 0.991027160832860f,
+ 0.991032016015245f, 0.991032016015245f, 0.991036870808346f,
+ 0.991041725212224f, 0.991046579226937f, 0.991046579226937f,
+ 0.991051432852543f, 0.991056286089103f, 0.991061138936675f,
+ 0.991061138936675f, 0.991065991395318f, 0.991070843465090f,
+ 0.991075695146052f, 0.991075695146052f, 0.991080546438262f,
+ 0.991085397341780f, 0.991090247856663f, 0.991090247856663f,
+ 0.991095097982971f, 0.991099947720763f, 0.991104797070098f,
+ 0.991109646031035f, 0.991109646031035f, 0.991114494603633f,
+ 0.991119342787949f, 0.991119342787949f, 0.991124190584045f,
+ 0.991129037991978f, 0.991133885011807f, 0.991133885011807f,
+ 0.991138731643591f, 0.991143577887389f, 0.991148423743260f,
+ 0.991148423743260f, 0.991153269211262f, 0.991158114291454f,
+ 0.991158114291454f, 0.991162958983895f, 0.991167803288644f,
+ 0.991172647205759f, 0.991172647205759f, 0.991177490735300f,
+ 0.991182333877324f, 0.991187176631891f, 0.991187176631891f,
+ 0.991192040874586f, 0.991196904726712f, 0.991196904726712f,
+ 0.991201768188329f, 0.991206631259494f, 0.991211493940268f,
+ 0.991211493940268f, 0.991216356230710f, 0.991221218130880f,
+ 0.991226079640835f, 0.991230940760638f, 0.991230940760638f,
+ 0.991235801490345f, 0.991240661830017f, 0.991245521779712f,
+ 0.991245521779712f, 0.991250381339491f, 0.991255240509413f,
+ 0.991260099289535f, 0.991260099289535f, 0.991264957679918f,
+ 0.991269815680622f, 0.991274673291704f, 0.991274673291704f,
+ 0.991279530513224f, 0.991284387345243f, 0.991289243787817f,
+ 0.991289243787817f, 0.991294099841006f, 0.991298955504871f,
+ 0.991303810779470f, 0.991303810779470f, 0.991308665664861f,
+ 0.991313520161105f, 0.991318374268260f, 0.991318374268260f,
+ 0.991323227986385f, 0.991328081315538f, 0.991332934255781f,
+ 0.991332934255781f, 0.991337786807170f, 0.991342638969767f,
+ 0.991347490743627f, 0.991347490743627f, 0.991352342128812f,
+ 0.991357193125380f, 0.991357193125380f, 0.991362043733390f,
+ 0.991366893952901f, 0.991371743783973f, 0.991371743783973f,
+ 0.991376593226661f, 0.991381442281029f, 0.991386290947134f,
+ 0.991386290947134f, 0.991391139225033f, 0.991395987114787f,
+ 0.991400834616454f, 0.991400834616454f, 0.991405681730093f,
+ 0.991410528455763f, 0.991410528455763f, 0.991415374793522f,
+ 0.991420220743429f, 0.991425066305544f, 0.991425066305544f,
+ 0.991429911479924f, 0.991434756266630f, 0.991439600665718f,
+ 0.991439600665718f, 0.991444444677248f, 0.991449288301280f,
+ 0.991449288301280f};
+
+// Mapping between reverberation times at a given frequency and the
+// corresponding magnitude compensation value required in the specral reverb to
+// achieve 1.0 magnitude for any decay rate when combined with the convolution
+// compensator. This table covers reverberation times between
+// kMinReverbTimeForFeedback and kNumFeedbackValues * kTimeStepSizeForFeedback,
+// i.e. 0.15s and 25s @48kHz.
+static const float kMagnitudeCompensation[kNumFeedbackValues] = {
+ 17.3996715487709f, 17.4034178858004f, 16.6795907475848f, 16.3502965109599f,
+ 15.9879028507326f, 15.6718483944106f, 15.4042251167111f, 15.1677088170684f,
+ 14.9119678952536f, 14.6906933374371f, 14.2839187463763f, 14.0999475816121f,
+ 13.9338828421032f, 13.7652114598195f, 13.6133888181873f, 13.4638764218013f,
+ 13.3247278048121f, 13.1976499042192f, 13.0833841919256f, 12.7669049046296f,
+ 12.7761019806869f, 12.7855596748707f, 12.5979372062571f, 12.5244917829334f,
+ 12.4453918694998f, 12.3672592847373f, 12.3020244979211f, 12.2257529341565f,
+ 12.1547662449381f, 12.0845455398564f, 12.0231689748723f, 11.9586881174297f,
+ 11.8906434508319f, 11.8264759328308f, 11.7652619217413f, 11.7033346037227f,
+ 11.6379964417238f, 11.5862703081324f, 11.5299758955061f, 11.4741541469989f,
+ 11.4224679633351f, 11.4302928484179f, 11.3885962828649f, 11.3488611750486f,
+ 11.3151942251170f, 11.2687417305887f, 11.2651043092380f, 11.2873091969354f,
+ 11.1115917717737f, 11.1187139401529f, 11.0873969606168f, 11.0667794924678f,
+ 11.0355838523984f, 11.0107298415034f, 10.9796516432030f, 10.9630980289917f,
+ 10.9420391333514f, 10.9144740761416f, 10.8934689087169f, 10.8708578658852f,
+ 10.8533772904939f, 10.8325917807584f, 10.8084481007036f, 10.7910279996611f,
+ 10.7720483888874f, 10.7547079194992f, 10.7374196414357f, 10.7165315149605f,
+ 10.6992241376899f, 10.6906704210781f, 10.6696884648005f, 10.6557033369952f,
+ 10.6417418339839f, 10.6206945653294f, 10.6123225679390f, 10.5984100558311f,
+ 10.5804524022014f, 10.5664834165837f, 10.5556799009337f, 10.5448786038089f,
+ 10.5294926904862f, 10.5186888881000f, 10.5078877209760f, 10.4896735339343f,
+ 10.4818821922801f, 10.4710009885916f, 10.4601237996041f, 10.4509410528977f,
+ 10.4400494357799f, 10.4322036844072f, 10.4213123997993f, 10.4058408037523f,
+ 10.3978977400326f, 10.3899444363220f, 10.3853855448482f, 10.3697415198647f,
+ 10.3617555934757f, 10.3567416154720f, 10.3487308415134f, 10.3329739194789f,
+ 10.3248849674214f, 10.3167888305905f, 10.3116328550137f, 10.2935369581880f,
+ 10.2854263705648f, 10.2802322447455f, 10.2721029640593f, 10.2668785541679f,
+ 10.2421605382004f, 10.2455580945816f, 10.2402346533852f, 10.2417289351959f,
+ 10.2364188981879f, 10.2144782407169f, 10.2091021586862f, 10.2037115797613f,
+ 10.1983068112122f, 10.1849493802464f, 10.1794698792260f, 10.1739775209827f,
+ 10.1684725944209f, 10.1699972796703f, 10.1673363396048f, 10.1537525491825f,
+ 10.1481899931936f, 10.1454318458448f, 10.1398423821346f, 10.1370496372401f,
+ 10.1314343707374f, 10.1205181210259f, 10.1148376525328f, 10.1119389454472f,
+ 10.1062355079601f, 10.1105790660401f, 10.1076676719351f, 10.1019633798750f,
+ 10.0907918971796f, 10.0877973262721f, 10.0820222224882f, 10.0790001296191f,
+ 10.0759630305115f, 10.0729111643779f, 10.0698447671938f, 10.0585205719156f,
+ 10.0553915426054f, 10.0495078552707f, 10.0463551070209f, 10.0452327409897f,
+ 10.0420899577109f, 10.0416595363245f, 10.0301423062663f, 10.0269263405620f,
+ 10.0236983112226f, 10.0204584174658f, 10.0172068557167f, 10.0139438196431f,
+ 10.0106695001895f, 10.0100859757974f, 10.0144981569414f, 10.0027240655604f,
+ 9.99940079964503f, 9.99875825234152f, 9.99541139468150f, 9.99205454764473f,
+ 9.98868788138219f, 9.98799186037639f, 9.98460299286733f, 9.98120480749684f,
+ 9.97196520049622f, 9.96851944732945f, 9.96773313504258f, 9.97209005664967f,
+ 9.97130280431796f, 9.96784064615800f, 9.96702924221558f, 9.96354778825994f,
+ 9.96271284320390f, 9.95921268399594f, 9.94972407107315f, 9.94618172696550f,
+ 9.94527759238012f, 9.93907565972122f, 9.93815157408174f, 9.93457833610339f,
+ 9.94163812518103f, 9.93806970926284f, 9.93712514990341f, 9.93616969007899f,
+ 9.92384118906784f, 9.92284379437166f, 9.92183604073516f, 9.91819656036143f,
+ 9.91716993715598f, 9.91613331652545f, 9.91508681621950f, 9.91141646117235f,
+ 9.91035202464596f, 9.90927805124168f, 9.90819465265145f, 9.90449521562619f,
+ 9.89464259105904f, 9.90163545023412f, 9.90051546012025f, 9.89938659067352f,
+ 9.89565102186460f, 9.89450616426505f, 9.89335273158198f, 9.89219082303091f,
+ 9.89102053655261f, 9.88984196883182f, 9.88865521531579f, 9.88746037023253f,
+ 9.88367272054804f, 9.88246342154956f, 9.87235666909112f, 9.87111421978793f,
+ 9.86728668752777f, 9.86603072697377f, 9.86476741507816f, 9.87181651341944f,
+ 9.87055453050722f, 9.86928534086147f, 9.86800902634652f, 9.86672566780610f,
+ 9.86543534507815f, 9.86413813700939f, 9.86283412146973f, 9.85253909146675f,
+ 9.85120575616663f, 9.84730850541530f, 9.84340913473107f, 9.83695739915978f,
+ 9.83305682291249f, 9.82661110215619f, 9.82270954762832f, 9.81627003605364f,
+ 9.81236772402631f, 9.80593460975945f, 9.79446129569662f, 9.79056524322650f,
+ 9.79518744944566f, 9.78878964277671f, 9.78490541018618f, 9.76941213950054f,
+ 9.76551357605385f, 9.76161372925909f, 9.75771265131274f, 9.75131744052475f,
+ 9.74741680228965f, 9.74351508082574f, 9.73961232562214f, 9.73322796347969f,
+ 9.72932601448363f, 9.72542317182318f, 9.72151948243115f, 9.71761499262133f,
+ 9.71124407362971f, 9.70734082271271f, 9.70343690224853f, 9.69953235617044f,
+ 9.69562722783490f, 9.69172156002971f, 9.67869145869549f, 9.67477248299185f,
+ 9.67085316486435f, 9.66693354428727f, 9.67164058015198f, 9.66773182471560f,
+ 9.66382281819403f, 9.65991359906596f, 9.65600420530895f, 9.65209467440629f,
+ 9.64818504335383f, 9.64427534866659f, 9.64036562638542f, 9.63645591208342f,
+ 9.63254624087234f, 9.62863664740886f, 9.62713438108161f, 9.61841452486423f,
+ 9.61450796945654f, 9.61060162177350f, 9.59745989603663f, 9.59593610568275f,
+ 9.59201716961870f, 9.58809863249534f, 9.58418052465712f, 9.58026287605631f,
+ 9.57872704411307f, 9.57480783656106f, 9.57088917994674f, 9.56934602396844f,
+ 9.56542599874574f, 9.56150661276656f, 9.55758789336276f, 9.55603586291498f,
+ 9.56094674392723f, 9.55703713657417f, 9.55548790574812f, 9.55157724555479f,
+ 9.54766736708134f, 9.54611162685054f, 9.54220086685072f, 9.54064051939564f,
+ 9.53672899077399f, 9.53281837539013f, 9.51720800158667f, 9.51563186381870f,
+ 9.51171473845506f, 9.51013443950261f, 9.50621695380252f, 9.50696588424213f,
+ 9.50071486073663f, 9.49214537555389f, 9.48823490695630f, 9.48432562212793f,
+ 9.48041754157442f, 9.47651068553726f, 9.47260507399707f, 9.46870072667699f,
+ 9.46479766304589f, 9.45859100251033f, 9.45469300691147f, 9.45079634803183f,
+ 9.44690104436323f, 9.44300711415882f, 9.43911457543615f, 9.43522344598003f,
+ 9.43133374334554f, 9.42744548486084f, 9.42355868763001f, 9.42195400960424f,
+ 9.41806778052289f, 9.41374743291046f, 9.40986387622231f, 9.40598186864776f,
+ 9.40210142598266f, 9.39822256381766f, 9.39434529754074f, 9.39046964233973f,
+ 9.38659561320474f, 9.38498117319374f, 9.38110807138758f, 9.37723664318189f,
+ 9.37336690279205f, 9.36949886424754f, 9.36563254139424f, 9.36401291268887f,
+ 9.36014771343569f, 9.35628427401529f, 9.35242260755338f, 9.34856272700286f,
+ 9.34693907021185f, 9.34308046758153f, 9.33922369235477f, 9.33536875680650f,
+ 9.33374195178015f, 9.32988841242305f, 9.32381510855490f, 9.31996765124740f,
+ 9.31612209259297f, 9.31227844391980f, 9.31064913327039f, 9.30680704022146f,
+ 9.29348811817957f, 9.29184974751964f, 9.28800440783775f, 9.28636432840859f,
+ 9.28252074101649f, 9.27867919003329f, 9.27703723070236f, 9.27319750721753f,
+ 9.26935985268265f, 9.26771614205892f, 9.26388038770541f, 9.26223520182348f,
+ 9.25840139650094f, 9.25675479157226f, 9.25292298305829f, 9.24909331708271f,
+ 9.25658949582583f, 9.25276798764379f, 9.25112470249905f, 9.24730525026412f,
+ 9.24566068030482f, 9.24184332892114f, 9.24019752634543f, 9.23638231973555f,
+ 9.23690246959584f, 9.23308722316596f, 9.23143799550340f, 9.22762498397928f,
+ 9.22597467697943f, 9.22432329385452f, 9.22051260344843f, 9.21886021442665f,
+ 9.21505186252288f, 9.21339851457425f, 9.20959254004180f, 9.20578887012963f,
+ 9.20198751127673f, 9.19073957709422f, 9.18693490086577f, 9.18527430481475f,
+ 9.18361278881117f, 9.17981083017848f, 9.17814860221564f, 9.17648548821270f,
+ 9.17268633214397f, 9.17102256976036f, 9.16935795435656f, 9.16556168321056f,
+ 9.16389648126049f, 9.16223045844461f, 9.15843715205156f, 9.15677060283272f,
+ 9.15510326406306f, 9.15131299980263f, 9.14964519313344f, 9.14797662741422f,
+ 9.14630731290934f, 9.13195283996486f, 9.13028849958014f, 9.12862343577599f,
+ 9.12485055311793f, 9.12318514040497f, 9.12151903298212f, 9.11985224052471f,
+ 9.11608300035933f, 9.11441593072157f, 9.11274820378056f, 9.11107982889063f,
+ 9.10941081532760f, 9.10774117228957f, 9.10397656900970f, 9.10230675285809f,
+ 9.10063633356450f, 9.10830768064681f, 9.10664083749441f, 9.10288539850849f,
+ 9.10121842744103f, 9.09955087784672f, 9.09788275836467f, 9.08651657603331f,
+ 9.08068020302688f, 9.08108732540316f, 9.07941328828146f, 9.07773874094359f,
+ 9.07398723838858f, 9.07231273992199f, 9.07063775460245f, 9.06896229034989f,
+ 9.06521511911848f, 9.06353976158507f, 9.06186394770784f, 9.06018768515226f,
+ 9.05851098152150f, 9.05683384435697f, 9.05515628113895f, 9.05347829928709f,
+ 9.05179990616104f, 9.05012110906098f, 9.04844191522816f, 9.04676233184547f,
+ 9.04302460323958f, 9.04134529301830f, 9.03966561367638f, 9.03798557216934f,
+ 9.03630517539697f, 9.03462443020383f, 9.03294334337977f, 9.03126192166043f,
+ 9.02958017172773f, 9.02789810021035f, 9.02621571368424f, 9.02453301867307f,
+ 9.02285002164873f, 9.02116672903177f, 9.01948314719191f, 9.01779928244843f,
+ 9.01611514107069f, 9.01443072927854f, 9.01274605324280f, 9.01106111908564f,
+ 9.00937593288109f, 9.00769050065541f, 9.00600482838754f, 9.00431892200953f,
+ 9.00263278740694f, 9.00094643041926f, 8.99925985684034f, 8.99757307241874f,
+ 8.99588608285818f, 8.99419889381793f, 8.99251151091317f, 8.99082393971542f,
+ 8.99116133873637f, 8.98947240095708f, 8.97807476175783f, 8.97638164966389f,
+ 8.97468839063631f, 8.97299498992199f, 8.97130145272722f, 8.96960778421804f,
+ 8.96791398952060f, 8.96622007372152f, 8.96452604186820f, 8.96283189896917f,
+ 8.96113764999443f, 8.96145642376267f, 8.95976097981039f, 8.95806544520587f,
+ 8.95636982476753f, 8.95467412327692f, 8.95297834547896f, 8.95128249608231f,
+ 8.94958657975968f, 8.95734018337137f, 8.95765278435025f, 8.95595916486210f,
+ 8.95426548584055f, 8.95257175184105f, 8.95087796738447f, 8.94918413695742f,
+ 8.94949047792581f, 8.94779558087857f, 8.94610065184975f, 8.94440569519098f,
+ 8.94271071522106f, 8.93902043601862f, 8.93932070246017f, 8.93961997400109f,
+ 8.93792395970352f, 8.93622794393283f, 8.93453193081558f, 8.93283592444736f,
+ 8.93113992889301f, 8.92944394818690f, 8.92973639134932f, 8.92803947305345f,
+ 8.92634258225175f, 8.92464572285846f, 8.92493436813772f, 8.92323660608605f,
+ 8.92153888773201f, 8.91984121687354f, 8.91814359728028f, 8.91644603269380f,
+ 8.91474852682785f, 8.91305108336851f, 8.91135370597452f, 8.91163407099939f,
+ 8.90993586525176f, 8.90823773709895f, 8.90653969009158f, 8.90681648856557f,
+ 8.90511764518828f, 8.90341889416476f, 8.90172023894098f, 8.90199353826512f,
+ 8.90029411780736f, 8.89859480404659f, 8.89689560032744f, 8.89519650996990f,
+ 8.89546552758263f, 8.89376570958158f, 8.89206601546450f, 8.88053809639265f,
+ 8.88080074152891f, 8.87909746670552f, 8.87739433859366f, 8.87765453752411f,
+ 8.87595075137300f, 8.87424712182442f, 8.87254365189478f, 8.87280064185817f,
+ 8.87109654154655f, 8.86743422965210f, 8.86768885156784f, 8.86598526773548f,
+ 8.86428186186200f, 8.86453414821111f, 8.86478566315377f, 8.86112634248555f,
+ 8.86137632515970f, 8.85967194497569f, 8.85796776101988f, 8.85626377603165f,
+ 8.85651073575462f, 8.85480620525780f, 8.85310188252331f, 8.85334661076713f,
+ 8.85164176132608f, 8.84993712826266f, 8.85017965504828f, 8.84847451372278f,
+ 8.84676959721478f, 8.84700995202877f, 8.84530454535357f, 8.84359937176427f,
+ 8.84383758357059f, 8.84213193756712f, 8.84042653275027f, 8.84066263000176f,
+ 8.83895677018950f, 8.83725115950061f, 8.83748517015020f, 8.83577912155772f,
+ 8.83407332986507f, 8.83430528137662f, 8.83259906855242f, 8.83089312024770f,
+ 8.83112303960668f, 8.82941668662965f, 8.82771060563841f, 8.82793851936218f,
+ 8.82623204985186f, 8.82645864099079f, 8.82475179379158f, 8.82304523091814f,
+ 8.82326985960455f, 8.82156293469962f, 8.81985630119355f, 8.82007899274514f,
+ 8.81837201257628f, 8.81666533073787f, 8.81688611003994f, 8.81517909662492f,
+ 8.81539861470977f, 8.82335720989358f, 8.82165261003093f, 8.82187267469875f,
+ 8.82016775424161f, 8.82038657061920f, 8.81675884387406f, 8.81697642252574f,
+ 8.81527182158995f, 8.81548817296149f, 8.81378328023903f, 8.81207870996388f,
+ 8.81229323974577f, 8.81058839169725f, 8.81080171980556f, 8.80909660340947f,
+ 8.80739182006356f, 8.80760336445859f, 8.80589832635901f, 8.80610869398147f,
+ 8.80440341027862f, 8.80461261089678f, 8.80290709063344f, 8.80120191782847f,
+ 8.80140938602084f, 8.79970398968595f, 8.79991031482745f, 8.78825930793902f,
+ 8.78846219390559f, 8.78675407755697f, 8.78504632874136f, 8.78524755053805f,
+ 8.78353962067476f, 8.78373974434793f, 8.78203164176273f, 8.78223067625635f,
+ 8.78052240917839f, 8.77881452261966f, 8.77901193990290f, 8.77730390068432f,
+ 8.77559624718409f, 8.77579206684317f, 8.77408427230694f, 8.77427903760903f,
+ 8.77257110989291f, 8.77276482931164f, 8.77105677618141f, 8.76934912094798f,
+ 8.76954128723245f, 8.76783351773637f, 8.76802465892895f, 8.76631678271580f,
+ 8.76650690697914f, 8.76479893150834f, 8.76498804691885f, 8.76327997956479f,
+ 8.76346809411380f, 8.76175994216697f, 8.76194706376180f, 8.76023883442988f,
+ 8.76042497089499f, 8.75871667130397f, 8.75890183038202f, 8.75719346757723f,
+ 8.75737765693012f, 8.75566923787733f, 8.75585246508727f, 8.75414399667368f,
+ 8.75432626924423f, 8.75261775827954f, 8.75090968234295f, 8.75109053685399f,
+ 8.75127092234682f, 8.74956234640392f, 8.74785421147492f, 8.74803320078602f,
+ 8.74632504621761f, 8.74461733673038f, 8.74291007344532f, 8.74308722620506f,
+ 8.74326392423471f, 8.74155620054932f, 8.74173199434523f, 8.74002427303862f,
+ 8.74019916951030f, 8.73849145680757f, 8.73866546279447f, 8.73695776485170f,
+ 8.73713088712407f, 8.73542321002923f, 8.73559545528903f, 8.73388780506281f,
+ 8.73405917994453f, 8.73235156254122f, 8.73252207361275f, 8.73081449492107f,
+ 8.73098414868451f, 8.72927661452841f, 8.72944541742091f, 8.72773793356043f,
+ 8.72790589195497f, 8.72619846408700f, 8.72636558429323f, 8.72653228775183f,
+ 8.72482450631728f, 8.72499038093493f, 8.72328266978537f, 8.72344772171672f,
+ 8.72174008633388f, 8.72003293816951f, 8.72019676747887f, 8.71848970287163f,
+ 8.71865272461753f, 8.71881534481493f, 8.71710796902965f, 8.71726979056008f,
+ 8.71556251187906f, 8.71572354060119f, 8.71401636421506f, 8.71417660593049f,
+ 8.71246953697379f, 8.71262899742776f, 8.71278806938804f, 8.71108072586171f,
+ 8.71123902507422f, 8.70953180189128f, 8.70968933396333f, 8.70798223606704f,
+ 8.70813900655191f, 8.70829539830896f, 8.70658805323002f, 8.70674369162996f,
+ 8.70503648428256f, 8.70519137474645f, 8.70348430989052f, 8.70363845778761f,
+ 8.70379223639875f, 8.70208495078059f, 8.70223799478307f, 8.70053086365168f,
+ 8.70068317828796f, 8.69897620622755f, 8.69912779669041f, 8.68932884670592f,
+ 8.68762000302773f, 8.68776884448380f, 8.68606017937344f, 8.68620831732905f,
+ 8.68449983514613f, 8.68464727456896f, 8.68479436657368f, 8.68308572543799f,
+ 8.68323212627427f, 8.68152367907888f, 8.68166939359894f, 8.68181476676632f,
+ 8.68010617755452f, 8.67839813079483f, 8.67854248705877f, 8.67868650672127f,
+ 8.67697833094137f, 8.67712168091770f, 8.67541371794501f, 8.67555640288794f,
+ 8.67384865672637f, 8.67399068124574f, 8.68390062160107f, 8.68219432235443f,
+ 8.68233691470609f, 8.68247917838292f, 8.68077277138796f, 8.68091438110988f,
+ 8.67920820006687f, 8.67934916032569f, 8.67948979752457f, 8.67778352432750f,
+ 8.67792351872432f, 8.67621748133180f, 8.67635683731680f, 8.67649587572979f,
+ 8.67478976139895f, 8.67492816791096f, 8.67506626008425f, 8.67336007789188f,
+ 8.67349754458695f, 8.67179161357488f, 8.67192845902535f, 8.67206499542924f,
+ 8.67035901118758f, 8.67049493262608f, 8.67063054813644f, 8.66892451939922f,
+ 8.66905952613920f, 8.66735376335083f, 8.66748816540435f, 8.66762226663473f,
+ 8.66591647342483f, 8.66421125283483f, 8.66250660524407f, 8.66263951321076f,
+ 8.66093514316382f, 8.65923134784848f, 8.65752812762827f, 8.65582548286296f,
+ 8.65412341390855f, 8.65242192111739f, 8.65072100483809f, 8.64902066541564f,
+ 8.64732090319137f, 8.64745089666342f, 8.64575142580246f, 8.64405253375412f,
+ 8.64235422084484f, 8.64065648739748f, 8.63895933373144f, 8.63726276016262f,
+ 8.63556676700344f, 8.63387135456289f, 8.63399880607199f, 8.63230369681987f,
+ 8.63060916980038f, 8.62891522530837f, 8.62722186363537f, 8.62552908506957f,
+ 8.62383688989589f, 8.62214527839595f, 8.62045425084813f, 8.62057923413287f,
+ 8.61888852099306f, 8.61719839322406f, 8.61550885109083f, 8.61381989485520f,
+ 8.61213152477584f, 8.61225490214502f, 8.61056685383226f, 8.60887939303327f,
+ 8.60719251999378f, 8.60369847555976f, 8.60382053816116f, 8.60394234032854f,
+ 8.60225697240927f, 8.59876697957841f, 8.59708363808768f, 8.59720440679893f,
+ 8.59552139839225f, 8.59383898041714f, 8.59215715308918f, 8.59047591662109f,
+ 8.58879527122275f, 8.58891451420684f, 8.58903350571243f, 8.58735294942756f,
+ 8.58567298620775f, 8.58399361624780f, 8.58231483973975f, 8.58063665687294f,
+ 8.57895906783397f, 8.57728207280678f, 8.57560567197261f, 8.57392986551003f,
+ 8.57404638533917f, 8.57237093049576f, 8.57248696522609f, 8.56902180741298f,
+ 8.56734813950615f, 8.56746345224170f, 8.56579014113883f, 8.56411742691198f,
+ 8.56244530971544f, 8.56077378970094f, 8.56088791398092f, 8.55921675580945f,
+ 8.55933040984012f, 8.55765961583721f, 8.55598942090227f, 8.55431982517126f,
+ 8.55443254737061f, 8.55276331980773f, 8.55109469242306f, 8.54942666534252f,
+ 8.54775923868966f, 8.54076218161341f, 8.53909843766319f, 8.53743529276498f,
+ 8.53754618531528f, 8.53588341401169f, 8.53422124267654f, 8.53255967141845f,
+ 8.53266966578484f, 8.53100847186734f, 8.52934787891323f, 8.52768788702179f,
+ 8.52779699406003f, 8.52613738317202f, 8.52447837420358f, 8.52281996724478f,
+ 8.52292819763660f, 8.52127017526276f, 8.51961275572668f, 8.51795593910948f,
+ 8.51806330336606f, 8.51640687483569f, 8.51475105002415f, 8.51485777147656f,
+ 8.50313962983211f, 8.50324475785744f, 8.49982824152465f, 8.49993295139312f,
+ 8.49827795948697f, 8.49838225368573f, 8.49496979858900f, 8.49507367958556f,
+ 8.49342030501101f, 8.49176753881319f, 8.49187080459044f, 8.49021844295144f,
+ 8.48856669037454f, 8.48691554689607f, 8.48701800083704f, 8.48536726502665f,
+ 8.48371713897484f, 8.48206762271016f, 8.48216927434053f, 8.48052016878301f,
+ 8.47887167364807f, 8.47897272970743f, 8.47732464765384f, 8.47567717663991f,
+ 8.47577764234150f, 8.47413058674447f, 8.47248414278601f, 8.47083831047568f,
+ 8.47093799703846f, 8.46929258301410f, 8.46764778121340f, 8.46774688934850f,
+ 8.46610250808181f, 8.46445873959686f, 8.46455727431892f, 8.46291392857930f,
+ 8.46127119616231f, 8.46136916242822f, 8.45972685493173f, 8.45808516128181f,
+ 8.45818256399157f, 8.45654129740170f, 8.45490064516572f, 8.45499748916322f,
+ 8.45335726609174f, 8.45171765786506f, 8.45007866445932f, 8.45017477094663f,
+ 8.44853620927392f, 8.44863195015821f, 8.44699382175575f, 8.44535630913176f,
+ 8.44371941225248f, 8.44381442815541f, 8.44217796702569f, 8.44054212207913f,
+ 8.44063659963106f, 8.43900119239275f, 8.43909531357936f, 8.43746034551955f,
+ 8.43582599452207f, 8.43591958492567f, 8.43428567501132f, 8.43265238255709f,
+ 8.43274544665058f, 8.43111259715250f, 8.42948036549767f, 8.42957290770386f,
+ 8.42794112084706f, 8.42630995220218f, 8.42640197689452f, 8.42477125485887f,
+ 8.42486293694762f, 8.42323266289285f, 8.42160300781375f, 8.42169417956989f,
+ 8.42006497423241f, 8.41843638820147f, 8.41852705387014f, 8.41689891931203f,
+ 8.41527140437771f, 8.41536156815713f, 8.41373450639779f, 8.41210806456602f,
+ 8.41219773060816f, 8.41057174362487f, 8.40894637685971f, 8.40903554927091f,
+ 8.40741063899950f, 8.40749948458245f, 8.40587503206534f, 8.40425120040078f,
+ 8.40433955909700f, 8.40271618678552f, 8.40109343558253f, 8.40118131137970f,
+ 8.39955902110142f, 8.39964657715982f, 8.39802474901748f, 8.39640354256068f,
+ 8.39649062227007f, 8.39486987947612f, 8.39324975859040f, 8.39163025950670f,
+ 8.39171671010196f, 8.39180300448298f, 8.39018381508821f, 8.38856524812366f,
+ 8.38694730347691f, 8.38703297721227f, 8.38711849683149f, 8.38550086657194f,
+ 8.38388385923108f, 8.38226747469047f, 8.38235238198133f, 8.38073646806615f,
+ 8.38082107165390f, 8.37920562946549f, 8.37759081052727f, 8.37767496160383f,
+ 8.37606061575931f, 8.37614446717391f, 8.37453059549704f, 8.37461414884609f,
+ 8.37300075239995f, 8.37138797988615f, 8.37147108910541f, 8.36985879313347f,
+ 8.36994160822893f, 8.36832978983540f, 8.36671859574903f, 8.36680097257521f,
+ 8.36519025733977f, 8.36527234391476f, 8.36366210854089f, 8.36205249781910f,
+ 8.36213415188276f, 8.36052502225735f, 8.36060638987481f, 8.35899774233109f,
+ 8.36906328366319f, 8.36745599896731f, 8.36584933792354f, 8.36593087269790f,
+ 8.36432469282948f, 8.36440594331694f, 8.36280024559060f, 8.36288121327418f,
+ 8.36127599864676f, 8.35967140818186f, 8.35975195441777f, 8.35814784820884f,
+ 8.35822811530357f, 8.35662449428343f, 8.35670448368437f, 8.35510134877630f,
+ 8.35518106192085f, 8.35357841403867f, 8.35365785235434f, 8.35205569240248f,
+ 8.35045415745305f, 8.35053318618056f, 8.34893214024213f, 8.34901089766681f,
+ 8.34741034162095f, 8.34581041077519f, 8.34588876385553f, 8.34428932394983f,
+ 8.34269050924751f, 8.34276846108694f, 8.34284627986358f, 8.34124782440677f,
+ 8.34132537807708f, 8.33972741611141f, 8.33813007984327f, 8.33820723838498f,
+ 8.33828426588748f, 8.33668729339379f, 8.33676405981657f, 8.33349522624397f,
+ 8.33190013122409f, 8.33197650850716f, 8.33205275673382f, 8.33045802938747f,
+ 8.32886392796943f, 8.32893979098194f, 8.32734618754917f, 8.32742179537051f,
+ 8.32582869070157f, 8.32590404461620f, 8.32431143948147f, 8.32438654076529f,
+ 8.32279443592703f, 8.32286928584740f, 8.32127768205985f, 8.32135228187567f,
+ 8.31976117988511f, 8.31983553084688f, 8.31824493139170f, 8.31831903474163f,
+ 8.31672893855239f, 8.31680279552442f, 8.31521320332393f, 8.31362423794623f,
+ 8.31369772764720f, 8.31210926707506f, 8.31218251344738f, 8.31059455838471f,
+ 8.31066756263400f, 8.30908011377724f, 8.30915287710116f, 8.30756593513926f,
+ 8.30763845872760f, 8.30605202434212f, 8.30612430937688f, 8.30453838324206f,
+ 8.30461043089745f, 8.30302501368025f, 8.30309682512281f, 8.30151191748300f,
+ 8.30158349387164f, 8.29999909646184f, 8.30007043894792f, 8.29848655241367f,
+ 8.29855766214103f, 8.29697428712085f, 8.29704516522590f, 8.29546230235136f,
+ 8.29553294996313f, 8.29395059985889f, 8.29402101809908f, 8.29243918138296f,
+ 8.29250937136602f, 8.29092804864905f, 8.29099801148223f, 8.28941720336873f,
+ 8.28948694015212f, 8.28790664723974f, 8.28797615906635f, 8.28639638194612f,
+ 8.28646566990194f, 8.28488640915835f, 8.28495547432238f, 8.28337673053339f,
+ 8.28344557397773f, 8.28186734771489f, 8.28193597050476f, 8.27017819449824f,
+ 8.27024585417908f, 8.26866792233384f, 8.26873536584578f, 8.26715795557297f,
+ 8.26722518394961f, 8.26564829578422f, 8.26571531005260f, 8.26413894452349f,
+ 8.26420574570412f, 8.26262990333412f, 8.26269649244104f, 8.26276297588936f,
+ 8.26118755178779f, 8.25961275728051f, 8.25967892525651f, 8.25810465544098f,
+ 8.25817061434708f, 8.25823646908936f, 8.25666262054728f, 8.25672826770033f,
+ 8.25515494533288f, 8.25522038587603f, 8.25364759016769f, 8.25371282507417f,
+ 8.25214055650372f, 8.25220558674063f, 8.25063384578123f, 8.25069867230963f,
+ 8.24912745942885f, 8.24919208320380f, 8.24925660595830f, 8.24768582083426f,
+ 8.24775014225673f, 8.24617988660073f, 8.24624400763143f, 8.24467428189162f,
+ 8.24473820346495f, 8.24316900808411f, 8.24323273112868f, 8.24166406654423f,
+ 8.24172759198290f, 8.24179101896291f, 8.24022278737693f, 8.24028601812464f,
+ 8.23871831864912f, 8.23878135407322f, 8.23721418712705f, 8.23727702813061f,
+ 8.23733977226080f, 8.23577304160806f, 8.23583559266193f, 8.23426939580623f,
+ 8.23433175467303f, 8.23276609201523f, 8.23282825957879f, 8.23126313151474f,
+ 8.23132510865348f, 8.22976051557408f, 8.22982230316103f, 8.22988399629783f,
+ 8.22831984435517f, 8.22838134923978f, 8.22681773347944f, 8.22687905097167f,
+ 8.22694027508851f, 8.22537710272175f, 8.22543814072614f, 8.22387550570830f,
+ 8.22393635844707f, 8.22237426113974f, 8.22243492945458f, 8.22087337021466f,
+ 8.22093385494218f, 8.22099424818818f, 8.21943313609370f, 8.21787265404081f,
+ 8.21793277408334f, 8.21799280346968f, 8.21643277007580f, 8.21649261876342f,
+ 8.21493312522628f, 8.21499279402937f, 8.21505237319400f, 8.21349333040847f,
+ 8.21355273090070f, 8.21199422903237f, 8.21205345165425f, 8.21211258564023f,
+ 8.21055453657232f, 8.20899711749525f, 8.20905598676346f, 8.20911476818710f,
+ 8.20755780332721f, 8.20761640965019f, 8.20767492871591f, 8.20611841935722f,
+ 8.20456253992591f, 8.20462079838524f, 8.20467897036231f, 8.20473705604943f,
+ 8.20318154739142f, 8.20323946107331f, 8.20168449663402f, 8.20174223907312f,
+ 8.20018781913647f, 8.20024539109073f, 8.19869151593658f, 8.19874891815950f,
+ 8.19719558806369f, 8.19725282130430f, 8.19730997033329f, 8.19736703533627f,
+ 8.19581408270369f, 8.19265325254094f, 8.19271006657415f, 8.19115900249672f,
+ 8.19121565013490f, 8.18966513207149f, 8.18650958272842f, 8.18656598214056f,
+ 8.18501734969057f, 8.18346934519575f, 8.18192196836458f, 8.17717170379845f,
+ 8.17562699749445f, 8.17408291704024f, 8.17253946214471f, 8.17099663251667f,
+ 8.16945442786483f, 8.16791284789780f, 8.16796835096100f, 8.16642731496180f,
+ 8.16488690323892f, 8.16334711550016f, 8.16180795145325f, 8.16026941080583f,
+ 8.15873149326542f, 8.15878643699898f, 8.15724906305914f, 8.15571231181225f,
+ 8.15417618296508f, 8.15264067622436f, 8.15269522559432f, 8.15116026230331f,
+ 8.14962592070057f, 8.14809220049210f, 8.14655910138382f, 8.14502662308157f,
+ 8.14508070479135f, 8.14354876964219f, 8.14201745487630f, 8.14048676019884f,
+ 8.14054053346655f, 8.13901038188417f, 8.13748084996421f, 8.13595193741119f,
+ 8.13442364392953f, 8.13289596922361f, 8.13294928474971f, 8.13142215280177f,
+ 8.12989563919925f, 8.12836974364590f, 8.12842275724862f, 8.12689740436780f,
+ 8.12537266910277f, 8.12384855115666f, 8.12390126539209f, 8.12237769002218f,
+ 8.12085473153483f, 8.11933238963259f, 8.11938480702804f, 8.11786300759498f,
+ 8.11634182430774f, 8.11482125686830f, 8.11487337992274f, 8.11335335483497f,
+ 8.11183394515285f, 8.11031515057783f, 8.10879697081129f, 8.10727940555463f,
+ 8.10733109172207f, 8.10581406839611f, 8.10429765913410f, 8.10278186363689f,
+ 8.10283326158928f, 8.10131800788042f, 8.09980336748775f, 8.09828934011163f,
+ 8.09834045225178f, 8.09682696651178f, 8.09531409333708f, 8.09380183242756f,
+ 8.09229018348308f, 8.09234094169636f, 8.09082983407241f, 8.09088045174303f,
+ 8.08780945305625f, 8.08630017906285f, 8.08635058701088f, 8.08484185400836f,
+ 8.08489212286426f, 8.08338393098375f, 8.08187634939786f, 8.08192641069197f,
+ 8.08041937019749f, 8.07891293953684f, 8.07740711840818f, 8.07745690494605f,
+ 8.07595162471175f, 8.07444695354630f, 8.07294289114748f, 8.07143943721306f,
+ 8.07148888347334f, 8.06998597006411f, 8.06848366465346f, 8.06698196693880f,
+ 8.06548087661756f, 8.06552998607523f, 8.06402943589713f, 8.06252949264403f,
+ 8.06103015601301f, 8.06107899849888f, 8.05958020178404f, 8.05808201122071f,
+ 8.05658442650561f, 8.05663300418972f, 8.05513595915549f, 8.05363951949682f,
+ 8.05368789998958f, 8.05219199993907f, 8.05069670478969f, 8.04920201423756f,
+ 8.04925013366991f, 8.04775598247604f, 8.04626243540297f, 8.04476949214651f,
+ 8.04481735262478f, 8.04332494846872f, 8.04183314765086f, 8.04188081528225f,
+ 8.04038955347468f, 8.03889889452528f, 8.03740883812929f, 8.03745625043850f,
+ 8.03596673278105f, 8.03447781719510f, 8.03452503935586f, 8.03303666240761f,
+ 8.03154888704735f, 8.03006171296983f, 8.03010868337276f, 8.02862204764774f,
+ 8.02713601272015f, 8.02565057828453f, 8.02569729893665f, 8.02421240256096f,
+ 8.02272810619019f, 8.02124440951864f, 8.02129088240572f, 8.01980772349432f,
+ 8.01985407323696f, 8.01837145214721f, 8.01688943008388f, 8.01540800674083f,
+ 8.01392718181196f, 8.01397322582548f, 8.01401920905608f, 8.01253886078973f,
+ 8.01105911056564f, 8.00957995807722f, 8.00810140301796f, 8.00814708413104f,
+ 8.00819270517784f, 8.00519164505985f, 8.00523714632877f, 8.00376026217617f,
+ 8.00380564413810f, 8.00232929694652f, 8.00085354600357f, 7.99937839100213f,
+ 7.99942353575290f, 7.99946862149134f, 7.99647489443730f, 7.99651986249756f,
+ 7.99504637603060f, 7.99357348450695f, 7.99210118761919f, 7.99214592170044f,
+ 7.99067416093170f, 7.98920299429772f, 7.98924755409054f, 7.98777692341388f,
+ 7.98782136757941f, 7.98635127289017f, 7.98488177163773f, 7.98492604320402f,
+ 7.98345707777071f, 7.98198870526966f, 7.98052092539286f, 7.98056496838578f,
+ 7.97909772395840f, 7.97914165332759f, 7.97767494437153f, 7.97620882733528f,
+ 7.97625258709119f, 7.97478700534684f, 7.97332201501439f, 7.97185761578544f,
+ 7.97039380735165f, 7.97043728659283f, 7.96897401286660f, 7.96751132942632f,
+ 7.96755464165088f, 7.96609249272969f, 7.96463093358420f, 7.96467407975223f,
+ 7.96321305493386f, 7.96325609092729f, 7.96179560044416f, 7.96033569902192f,
+ 7.96037857054268f, 7.95891920325775f, 7.95746042452055f, 7.95750313250930f,
+ 7.95604488770810f, 7.95458723094048f, 7.95462977633074f, 7.95317265329447f,
+ 7.95171611777669f, 7.95175850149487f, 7.95030249950052f, 7.95034477594852f,
+ 7.94888930747411f, 7.94743442579445f, 7.94747654209885f, 7.94602219372557f,
+ 7.94456843162926f, 7.94461038869789f, 7.94315715969122f, 7.94319901110364f,
+ 7.94174631517672f, 7.94029420479812f, 7.94033589847266f, 7.93888432095161f,
+ 7.93743332845856f, 7.93747486528495f, 7.93602440542419f, 7.93457453007028f,
+ 7.93461591093157f, 7.93316656798164f, 7.93320784535352f, 7.93175903478923f,
+ 7.93031080799686f, 7.93035193086073f, 7.92890423622049f, 7.92745712482865f,
+ 7.92749809405007f, 7.92605151457391f, 7.92609238184470f, 7.92464633425995f,
+ 7.92320086918425f, 7.92324158424087f, 7.92179665081508f, 7.92035229937259f,
+ 7.92039286306353f, 7.91894904302663f, 7.91898950627539f, 7.91754621761402f,
+ 7.91610351019241f, 7.91614382347568f, 7.91470164718040f, 7.91474186094918f,
+ 7.91330021574662f, 7.91185915103762f, 7.91041866651197f, 7.91045868235363f,
+ 7.91049864894173f, 7.90905864587311f, 7.90761922254927f, 7.90618037865992f,
+ 7.90622014914004f, 7.90625987081860f, 7.90334410091085f, 7.90338372525529f,
+ 7.90194652065353f, 7.90198604802103f, 7.90054937343709f, 7.89911327700454f,
+ 7.89915265957254f, 7.89771709289137f, 7.89775637936779f, 7.89632134239166f,
+ 7.89488688281196f, 7.89492602580883f, 7.89349209566332f, 7.89353114344322f,
+ 7.89209774268232f, 7.89213669559212f, 7.89070382416489f, 7.88927152915260f,
+ 7.88931034040396f, 7.88787857444832f, 7.88791729168944f, 7.88648605473636f,
+ 7.88652467830826f, 7.88509397030232f, 7.87347922114931f, 7.87351729104996f,
+ 7.87208727383296f, 7.87212525190789f, 7.87069576429284f, 7.86926685183945f,
+ 7.86930469279725f, 7.86787630965334f, 7.86791405961169f, 7.86648620571214f,
+ 7.86505892620407f, 7.86509654027728f, 7.86366978971717f, 7.86370731360733f,
+ 7.86228109192717f, 7.86231852595811f, 7.86089283308866f, 7.86093017758265f,
+ 7.85950501345345f, 7.85808042248116f, 7.85811763327064f, 7.85669357073507f,
+ 7.85673069278658f, 7.85530715861446f, 7.85388419682346f, 7.85392118636180f,
+ 7.85249875262700f, 7.85253565421687f, 7.85111374846223f, 7.84969241431120f,
+ 7.84972918456513f, 7.84830837808339f, 7.84834506116903f, 7.84692478227781f,
+ 7.84696137850464f, 7.84554162712403f, 7.84412244633333f, 7.84415891284905f,
+ 7.84274025925302f, 7.85141288690937f, 7.85144961512784f, 7.85003244871711f,
+ 7.85006909037938f, 7.84865244984218f, 7.84868900525374f, 7.84727289050818f,
+ 7.84730935997309f, 7.84589377093614f, 7.84593015475706f, 7.84451509134460f,
+ 7.84310059603029f, 7.84313685194958f, 7.84172288193889f, 7.84175905296461f,
+ 7.84179518165529f, 7.84038169460074f, 7.83896877493622f, 7.83900477706657f,
+ 7.83759238244247f, 7.83618055466107f, 7.83621643089471f, 7.83480512782671f,
+ 7.83484092049560f, 7.83343014204975f, 7.83346585144543f, 7.83205559752943f,
+ 7.83209122394205f, 7.83068149446259f, 7.83071703818099f, 7.82930783304372f,
+ 7.82789919324926f, 7.82793461346494f, 7.82652649767837f, 7.82656183591605f,
+ 7.82515424404068f, 7.82518950058452f, 7.82378243252265f, 7.82381760765553f,
+ 7.82241106330847f, 7.82244615731193f, 7.81815980032046f, 7.81963467968251f,
+ 7.81822978630920f, 7.81682545615005f, 7.81542168889519f, 7.81545654118684f,
+ 7.81405329648404f, 7.81408806875962f, 7.81268534650601f, 7.81272003904066f,
+ 7.81131783913241f, 7.81135245220004f, 7.80995077453237f, 7.80998530840561f,
+ 7.80858415287283f, 7.80718355872843f, 7.80721797431856f, 7.80581790196027f,
+ 7.80585223903216f, 7.80445268835214f, 7.80448694717412f, 7.80308791806361f,
+ 7.80312209890276f, 7.80172359125211f, 7.80175769437431f, 7.80035970807297f,
+ 7.80039373374288f, 7.79899626867941f, 7.79903021716051f, 7.79763327322259f,
+ 7.79766714477715f, 7.79627072185158f, 7.79630451674069f, 7.79490861471341f,
+ 7.79494233319698f, 7.79354695195305f, 7.79215212935342f, 7.79218573371345f,
+ 7.79221930016098f, 7.79082496013553f, 7.78943117826415f, 7.78946463135824f,
+ 7.78807136967477f, 7.78810474751849f, 7.78671200590435f, 7.78674530875124f,
+ 7.78677857419443f, 7.78538631518947f, 7.78399461335494f, 7.78402776696423f,
+ 7.78263658483832f, 7.78266966420460f, 7.78270270654283f, 7.78131200703774f,
+ 7.77992186396267f, 7.77995479558889f, 7.77856517185444f, 7.77859802998139f,
+ 7.77863085145093f, 7.77724171033669f, 7.77585312490987f, 7.77588583677433f,
+ 7.77449777031297f, 7.77453040941203f, 7.77456301221936f, 7.77317542836560f,
+ 7.77178839945454f, 7.77182089374904f, 7.77043438342129f, 7.77046680567449f,
+ 7.77049919199683f, 7.76911316425229f, 7.76772769070368f, 7.76775996959096f,
+ 7.76637501423668f, 7.76640722179721f, 7.76502278450610f, 7.76505492097597f,
+ 7.76367100161614f, 7.76370306723039f, 7.76373509750465f, 7.76235166066185f,
+ 7.76096877676598f, 7.76100070136998f, 7.75961833500535f, 7.75965018945267f,
+ 7.75968200890831f, 7.75830012500606f, 7.75691879329882f, 7.75695050812457f,
+ 7.75698218818832f, 7.75560133890093f, 7.75563294961187f, 7.75425261742615f,
+ 7.75287283668173f, 7.75290434378954f, 7.75152507975708f, 7.75155651807875f,
+ 7.75158792209177f, 7.75020914037975f, 7.74883090935359f, 7.74745322870535f,
+ 7.74748449604369f, 7.74610733143631f, 7.74613853077156f, 7.74476188206386f,
+ 7.74479301361756f, 7.74341688066770f, 7.74344794466043f, 7.74347897495509f,
+ 7.74210332397728f, 7.74072822211493f, 7.74075915164364f, 7.74079004769272f,
+ 7.73941542773344f, 7.73944625698609f, 7.73807215231902f, 7.73810291499155f,
+ 7.73672932547114f, 7.73676002177895f, 7.73538694725902f, 7.73541757741657f,
+ 7.73404501775030f, 7.73407558197112f, 7.73270353701110f, 7.73273403550780f,
+ 7.73136250510597f, 7.73139293809028f, 7.73142333839748f, 7.73005228978068f,
+ 7.72868178804865f, 7.72871209063963f, 7.72874236076320f, 7.72737234072627f,
+ 7.72740254607128f, 7.72603304009822f, 7.72606318087234f, 7.72469418881164f,
+ 7.72472426522164f, 7.72335578692120f, 7.72338579917296f, 7.72341577942255f,
+ 7.72204778277866f, 7.72068033154013f, 7.72071021608960f, 7.72074006884081f,
+ 7.71937309915523f, 7.71940288846195f, 7.71803643202356f, 7.71806615808777f,
+ 7.71670021474117f, 7.71672987776400f, 7.71536444735324f, 7.71399956106752f,
+ 7.71402912990351f, 7.71405866744353f, 7.71269426243436f, 7.71272373753127f,
+ 7.71135984498676f, 7.71138925783831f, 7.71002587760030f, 7.71005522840340f,
+ 7.70869236031320f, 7.70872164926393f, 7.70875090736174f, 7.70738852045593f,
+ 7.70602667618316f, 7.70605584201409f, 7.70608497718635f, 7.70472361397175f,
+ 7.70475268797144f, 7.70339183636089f, 7.70342084938044f, 7.70344983198180f,
+ 7.70208946144320f, 7.70211838335150f, 7.70075852418828f, 7.70078738559392f,
+ 7.69942803764294f, 7.69945683873552f, 7.69809800183312f, 7.69812674280145f,
+ 7.69815545377820f, 7.69679709781559f, 7.69682574894983f, 7.69546790380054f,
+ 7.69549649527912f, 7.69552505699968f, 7.69416769278661f, 7.69419619513033f,
+ 7.69283934149161f, 7.69286778464339f, 7.69151144141215f, 7.69015563785611f,
+ 7.69018399256504f, 7.69021231788464f, 7.68885699496583f, 7.68888526164366f,
+ 7.68891349906885f, 7.68755865684680f, 7.68620435356727f, 7.68623250350642f,
+ 7.68626062437385f, 7.68490680163368f, 7.68493486449261f, 7.68358155123853f,
+ 7.68360955626822f, 7.68225675232974f, 7.68228469970871f, 7.68231261832904f,
+ 7.68096029482092f, 7.68098815605693f, 7.67963634161052f, 7.67966414563891f,
+ 7.67831284008200f, 7.67834058707873f, 7.67836830562541f, 7.67701748037903f,
+ 7.67704514215666f, 7.67569482554126f, 7.67572243072402f, 7.67437262256575f,
+ 7.67440017132709f, 7.67442769194256f, 7.67307836396437f, 7.67172957219709f,
+ 7.67175700863318f, 7.67040872479895f, 7.67043610532978f, 7.67046345797203f,
+ 7.66911565404928f, 7.66914295104199f, 7.66917022027359f, 7.66782289629849f,
+ 7.66785011013471f, 7.66650329373374f, 7.66653045234317f, 7.66518414333889f,
+ 7.66521124688943f, 7.66523832297326f, 7.66389249376287f, 7.66254719901806f,
+ 7.66257419295179f, 7.66260115958519f, 7.66262809895966f, 7.66128325660027f,
+ 7.66131014158067f, 7.65996580606912f, 7.65999263682000f, 7.65864880797621f,
+ 7.65867558466144f, 7.65870233437509f, 7.65735898508777f, 7.65738568098033f,
+ 7.65604283808071f, 7.65606948031442f, 7.65472714362097f, 7.65475373235741f,
+ 7.65478029440560f, 7.65343843708835f, 7.65346494588041f, 7.65349142810445f,
+ 7.65215005018116f, 7.65080920452514f, 7.65083560728422f, 7.65086198363444f,
+ 7.64952161716499f, 7.64954794081617f, 7.64957423817702f, 7.64823435090775f,
+ 7.64826059580594f, 7.64692121388197f, 7.64694740647428f, 7.64560852971063f,
+ 7.64563467015326f, 7.64566078457940f, 7.64432238681307f, 7.64434844932262f,
+ 7.64301055642301f, 7.64303656717059f, 7.64306255209497f, 7.64172513809099f,
+ 7.64175107148420f, 7.64041416205060f, 7.64044004406572f, 7.64046590044903f,
+ 7.63912946980476f, 7.63915527503849f, 7.63918105475433f, 7.63784510290134f,
+ 7.63787083169477f, 7.63653538400034f, 7.63656106202205f, 7.63522611829733f,
+ 7.63525174569743f, 7.63527734784282f, 7.63394288268123f, 7.63396843442906f,
+ 7.63263447293292f, 7.63265997443180f, 7.63268545086176f, 7.63135196780901f,
+ 7.63137739421188f, 7.63004441451777f, 7.63006979104072f, 7.63009514267872f,
+ 7.62876264130389f, 7.62878794328165f, 7.62745594495614f, 7.62748119741940f,
+ 7.62750642517989f, 7.62617490504528f, 7.62620008350880f, 7.62622523737801f,
+ 7.62489419542260f, 7.62491930021111f, 7.62358876087232f, 7.62361381672361f,
+ 7.62228377980812f, 7.62230878686512f, 7.62233376957839f, 7.62100421058442f,
+ 7.62102914471672f, 7.61970008782946f, 7.61972497352234f, 7.61974983504840f,
+ 7.61842125594179f, 7.61844606923969f, 7.61847085847612f, 7.61714275713074f,
+ 7.61716749834905f, 7.61583989866594f, 7.61586459200549f, 7.61588926145774f,
+ 7.61456213938845f, 7.61458676116997f, 7.61326014044020f, 7.61328471468899f,
+ 7.61330926522300f, 7.61198312195571f, 7.61200762516301f, 7.61203210475825f,
+ 7.61070643892753f, 7.61073087140089f, 7.60940570645537f, 7.60943009194275f,
+ 7.60945445398794f, 7.60812976632134f, 7.60815408158342f, 7.60817837350445f,
+ 7.60685416308698f, 7.60687840842660f, 7.60555469843407f, 7.60557889732612f,
+ 7.60560307304439f, 7.60427984013719f, 7.60430396960766f, 7.60432807600398f,
+ 7.60300532014849f, 7.60302938049563f, 7.60170712459905f, 7.60173113902882f,
+ 7.60175513054917f, 7.60043335153450f, 7.60045729733421f, 7.60048122032260f,
+ 7.59915991815251f, 7.59918379561589f, 7.59786299293304f, 7.59788682500121f,
+ 7.59791063442030f, 7.59659030840635f, 7.59661407262401f, 7.59663781428919f,
+ 7.59531796490316f, 7.59534166155958f, 7.59402231118364f, 7.59404596295913f,
+ 7.59406959234195f, 7.59275071841253f, 7.59277430310528f, 7.59279786550050f,
+ 7.59147946797307f, 7.59150298586800f, 7.59018508686814f, 7.59020856038869f,
+ 7.59023201176912f, 7.58891458898425f, 7.58893799617829f, 7.58896138132594f,
+ 7.58764443470803f, 7.58766777585621f, 7.58635132727817f, 7.58637462455093f,
+ 7.58639789993235f, 7.58508192732879f, 7.58510515901996f, 7.58512836891210f,
+ 7.58515155703590f, 7.58383603861766f, 7.58385918329649f, 7.58254416248636f,
+ 7.58256726384219f, 7.58259034358211f, 7.58127579858836f, 7.58129883518735f,
+ 7.58132185026121f, 7.58000778102858f, 7.58003075314258f, 7.57871718101943f,
+ 7.57874011029371f, 7.57876301819304f, 7.57744992162713f, 7.57747278686613f,
+ 7.57749563081961f, 7.57618300975245f, 7.57620581122397f, 7.57622859149893f,
+ 7.57491644587078f, 7.57493918384116f, 7.57496190070343f, 7.57365023045334f,
+ 7.57367290518743f, 7.57369555890135f, 7.57238436396716f, 7.57240697572833f,
+ 7.57109627695034f, 7.57111884687516f, 7.57114139592532f, 7.56983117224850f,
+ 7.56985367963612f, 7.56987616623575f, 7.56856641759483f, 7.56858886270476f,
+ 7.56861128711287f, 7.56730201344145f, 7.56732439653175f, 7.56734675900595f,
+ 7.56603796023641f, 7.56606028156374f, 7.56608258236019f, 7.56477425842381f,
+ 7.56479651824339f, 7.56348868948821f, 7.56351090844373f, 7.56353310700940f,
+ 7.56355528521325f, 7.56224791073235f, 7.56227004829653f, 7.56229216558275f,
+ 7.56098526572191f, 7.56100734253566f, 7.55970093738852f, 7.55972297384077f,
+ 7.55974499015377f, 7.55843905939626f, 7.55846103551346f, 7.55848299157400f,
+ 7.55850492760535f, 7.55719945116063f, 7.55722134721568f, 7.55724322332355f,
+ 7.55593822119919f, 7.55596005749435f, 7.55465554945120f, 7.55467734604222f,
+ 7.55469912282176f, 7.55339508886160f, 7.54282966651951f, 7.55343854360594f,
+ 7.55346024140898f, 7.55215666177545f, 7.55217832025123f, 7.55087523421793f,
+ 7.54031819934936f, 7.54033979903436f, 7.53904143572760f, 7.53906299635162f,
+ 7.53908453748486f, 7.54306747979114f, 7.54837830652259f, 7.54839978934178f,
+ 7.54709812458524f, 7.53655365351434f, 7.53657507825590f, 7.53527812830351f,
+ 7.53529951445738f, 7.54588255528895f, 7.54590390295925f, 7.54460316609662f,
+ 7.54462447538711f, 7.53408833025382f, 7.53279230299647f, 7.53281355491099f,
+ 7.53151801756132f, 7.53153923135380f, 7.53156042612371f, 7.53158160189662f,
+ 7.53028651627133f, 7.53030765412677f, 7.53032877306164f, 7.52903415812099f,
+ 7.52905523929085f, 7.52776111376522f, 7.52778215727126f, 7.52780318198321f,
+ 7.52782418792625f, 7.52653051408721f, 7.52655148256772f, 7.52657243235452f,
+ 7.52527922893053f, 7.52530014140483f, 7.52532103526024f, 7.52402830215954f,
+ 7.52404915885167f, 7.52406999699933f, 7.52277773412920f, 7.52279853526202f,
+ 7.52281931792438f, 7.52284008214087f, 7.52154827098634f, 7.52156899838468f,
+ 7.52027767569381f, 7.52029836637182f, 7.52031903872623f, 7.52033969278137f,
+ 7.51904882176237f, 7.51906943929178f, 7.51909003859465f, 7.51779963749868f,
+ 7.51782020042086f, 7.51784074518883f, 7.51655081391820f, 7.51657132244977f,
+ 7.51659181289909f, 7.51530235135519f, 7.51532280571164f, 7.51534324205739f,
+ 7.51536366041622f, 7.51407465053640f, 7.51409503299255f, 7.51280651060282f,
+ 7.51282685725088f, 7.51284718603027f, 7.51286749696450f, 7.51157942617877f,
+ 7.51159970149310f, 7.51161995903261f, 7.51033235764065f, 7.51035257970054f,
+ 7.51037278405555f, 7.50908565195404f, 7.50910582096898f, 7.50912597234864f,
+ 7.50783930943340f, 7.50785942561180f, 7.50787952422413f, 7.50789960529340f,
+ 7.50661339393931f, 7.50663343999128f, 7.50534771513269f, 7.50536772625885f,
+ 7.50538771995631f, 7.50540769624784f, 7.50412242287476f, 7.50414236442252f,
+ 7.50416228863237f, 7.50418219552691f, 7.50289737369020f, 7.50291724602175f,
+ 7.50163291019824f, 7.50165274805672f, 7.50167256871229f, 7.50169237218731f,
+ 7.50040848781655f, 7.50042825699740f, 7.50044800906457f, 7.49916459326124f,
+ 7.49918431116761f, 7.49920401202679f, 7.49792106467948f, 7.49794073151054f,
+ 7.49796038136060f, 7.49667790235710f, 7.49669751831102f, 7.49671711734975f,
+ 7.49673669949516f, 7.49545467185099f, 7.49547422027520f, 7.49549375187145f,
+ 7.49421219240963f, 7.49423169041509f, 7.49425117165763f, 7.49297008026330f,
+ 7.49298952804478f, 7.49300895912805f, 7.49302837353459f, 7.49174773343687f,
+ 7.49176711455430f, 7.49178647905926f, 7.49050630686120f, 7.49052563820524f,
+ 7.49054495300073f, 7.48926524858490f, 7.48928453034700f, 7.48930379562416f,
+ 7.48932304443751f, 7.48804379124302f, 7.48806300719200f, 7.48808220674033f,
+ 7.48680342115406f, 7.48682258796403f, 7.48684173843620f, 7.48556342033819f,
+ 7.48558253819737f, 7.48560163978127f, 7.48562072511066f, 7.48434285814650f,
+ 7.48436191102906f, 7.48438094771922f, 7.48310354806328f, 7.48312255243049f,
+ 7.48314154066708f, 7.48316051279358f, 7.48188356423386f, 7.48190250420160f,
+ 7.48192142812062f, 7.48064494668490f, 7.48066383856759f, 7.48068271446260f,
+ 7.47940670002678f, 7.47942554400726f, 7.47944437206081f, 7.47946318420761f,
+ 7.47818762076032f, 7.47820640115404f, 7.47822516570133f, 7.47824391442227f,
+ 7.47696880197852f, 7.47698751910672f, 7.47571288915281f, 7.47573157476804f,
+ 7.47575024465661f, 7.47576889883838f, 7.47449471975386f, 7.47451334258139f,
+ 7.47453194976145f, 7.47455054131374f, 7.47327681310897f, 7.47329537346471f,
+ 7.47202212722719f, 7.47204065646482f, 7.47205917017273f, 7.47207766837044f,
+ 7.47080487287198f, 7.47082334010767f, 7.47084179189150f, 7.47086022824284f,
+ 7.46958788348929f, 7.46960628903368f, 7.46962467920354f, 7.46835280053496f,
+ 7.46837116001347f, 7.46838950417511f, 7.46840783303904f, 7.46713640504500f,
+ 7.46715470337084f, 7.46717298645624f, 7.46719125432022f, 7.46592027700163f,
+ 7.46593851447977f, 7.46595673679339f, 7.46597494396140f, 7.46470441731737f,
+ 7.46472259425075f, 7.46345254840048f, 7.46347069517447f, 7.46348882689689f,
+ 7.46350694358646f, 7.46223734825421f, 7.46225543493411f, 7.46227350663713f,
+ 7.46229156338184f, 7.46102241856245f, 7.46104044544623f, 7.46105845742730f,
+ 7.45978977797966f, 7.45980776021071f, 7.45982572759437f, 7.45855751338129f,
+ 7.45857545112528f, 7.45859337407692f, 7.45861128225450f, 7.45734351838243f,
+ 7.45736139706664f, 7.45737926103147f, 7.45611196217244f, 7.45612979675298f,
+ 7.45614761666857f, 7.45616542193726f, 7.45489857332325f, 7.45491634935229f,
+ 7.45493411078849f, 7.45495185764981f, 7.45368545926693f, 7.45370317703227f,
+ 7.45372088027644f, 7.45245494659468f, 7.45247262084999f, 7.45249028063757f,
+ 7.45250792597517f, 7.45124244242089f, 7.45126005891167f, 7.45127766100555f,
+ 7.45129524872019f, 7.45003021527506f, 7.45004777428398f, 7.44878321952857f,
+ 7.44880074990199f, 7.44881826598392f, 7.44883576779183f, 7.43727548639956f,
+ 7.43729277382540f, 7.43731004718073f, 7.43732730648274f, 7.43606346612857f,
+ 7.43608069737530f, 7.43609791462004f, 7.43483453871790f, 7.43485172800980f,
+ 7.43486890335078f, 7.43488606475783f, 7.43362313924374f, 7.43364027283374f,
+ 7.43365739254057f, 7.43367449838109f, 7.43241202322571f, 7.43242910138409f,
+ 7.43244616572660f, 7.43118415468149f, 7.43120119144248f, 7.43121821443777f,
+ 7.43123522368404f, 7.42997366286784f, 7.42999064466599f, 7.43000761276498f,
+ 7.43002456718139f, 7.42876345656070f, 7.42878038366152f, 7.42879729712929f,
+ 7.42753665026940f, 7.42755353652043f, 7.42757040918772f, 7.42758726828765f,
+ 7.42632707148660f, 7.42634390350084f, 7.42636072199670f, 7.42637752699047f,
+ 7.42511778021092f, 7.42513455824921f, 7.42387528831073f, 7.42389203945833f,
+ 7.42390877718483f, 7.42392550150634f, 7.42266668138160f, 7.42268337894139f,
+ 7.42270006314441f, 7.42271673400666f, 7.42145836365485f, 7.42147500788352f,
+ 7.42149163881933f, 7.42023373145931f, 7.42025033585711f, 7.42026692700973f,
+ 7.42028350493299f, 7.41902604719678f, 7.41904259870875f, 7.41905913703874f,
+ 7.41907566220248f, 7.41781865404551f, 7.41783515292387f, 7.41785163868306f,
+ 7.41659509314730f, 7.41661155271502f, 7.41662799921040f, 7.41664443264901f,
+ 7.41538833653703f, 7.41540474390869f, 7.41542113827012f, 7.41543751963679f,
+ 7.41418187290033f, 7.41419822832378f, 7.41421457079872f, 7.41295938630599f,
+ 7.41297570292999f, 7.41299200665152f, 7.41300829748587f, 7.41175356220701f,
+ 7.41176982731279f, 7.41178607957713f, 7.41180231901523f, 7.41054803289840f,
+ 7.41056424672953f, 7.40931043523787f, 7.40932662352253f, 7.40934279905659f,
+ 7.40935896185510f, 7.40937511193310f, 7.40812173667013f, 7.40813786135211f,
+ 7.40815397335853f, 7.40817007270433f, 7.40691714638039f, 7.40693322044968f,
+ 7.40568076819554f, 7.40569681704778f, 7.40571285331374f, 7.40572887700822f,
+ 7.40574488814600f};
+
+// Set of scalers, one per reverberation time, by which the
+// |kHighCorrectionCurve| and |kLowCorrectionCurve| curves are multiplied in
+// order to generate the correct onset compensation curves.
+static const float kCurveCorrectionMultipliers[kNumFeedbackValues] = {
+ 0.000249027612622225f, 0.0475827382701585f, 0.0982856973675534f,
+ 0.144077780284872f, 0.187710331328442f, 0.229214552437128f,
+ 0.268761703298196f, 0.306384317687497f, 0.342116205684862f,
+ 0.376122154314109f, 0.408019265118341f, 0.438826303187204f,
+ 0.468213758592444f, 0.496269855121223f, 0.523103160344453f,
+ 0.548734562484123f, 0.573191744554386f, 0.596776538466997f,
+ 0.619556090502133f, 0.646894092669191f, 0.661870435136273f,
+ 0.675494733319630f, 0.701821797958321f, 0.713358543560036f,
+ 0.731743894313465f, 0.749694785147017f, 0.758967096040535f,
+ 0.775801399270244f, 0.792218438223182f, 0.808206900102085f,
+ 0.815142557606016f, 0.830420164791760f, 0.845380788487056f,
+ 0.859865023229817f, 0.865296767216337f, 0.879331838452952f,
+ 0.893145291831122f, 0.897272345317607f, 0.910373690471610f,
+ 0.923470208927849f, 0.936268613920194f, 0.940724814169454f,
+ 0.943721434222406f, 0.956465808252697f, 0.959628673835810f,
+ 0.971718784707243f, 0.974554123797995f, 0.977943937294408f,
+ 0.997154666216203f, 0.999999999999996f, 0.00847079214371323f,
+ 0.0104396896429595f, 0.0383165227008017f, 0.0405943838116957f,
+ 0.0685361482242258f, 0.0701917817593536f, 0.0713216738657547f,
+ 0.0986119509733789f, 0.0995868953874107f, 0.127034424762083f,
+ 0.127777689282131f, 0.128246886910136f, 0.155790763336654f,
+ 0.156203946276228f, 0.183007628720406f, 0.182664049029335f,
+ 0.182636700276451f, 0.209980982953741f, 0.209930104556972f,
+ 0.209208261790760f, 0.236016550641923f, 0.235475840865507f,
+ 0.234825009787230f, 0.262017587951252f, 0.260795540834379f,
+ 0.259744215394585f, 0.286998748503651f, 0.286344090244485f,
+ 0.285483640041615f, 0.284539872612409f, 0.311891500333310f,
+ 0.310660815240075f, 0.309428379806926f, 0.336752370009492f,
+ 0.335612040225831f, 0.334119868753728f, 0.332881268861172f,
+ 0.360256878080152f, 0.358861080534800f, 0.357458025447185f,
+ 0.356097715473274f, 0.383869923377680f, 0.382590514632971f,
+ 0.381266229326427f, 0.379680871429225f, 0.407164010760711f,
+ 0.405597486292448f, 0.404000115485411f, 0.402266215035868f,
+ 0.429891973669762f, 0.428453570844335f, 0.426999237668900f,
+ 0.425435248817597f, 0.450978617489268f, 0.449350785137532f,
+ 0.447825393407652f, 0.446242285152424f, 0.444699143006987f,
+ 0.471928084570479f, 0.471460865818911f, 0.469975899073161f,
+ 0.468110718285786f, 0.466267910972978f, 0.492953377248067f,
+ 0.491503646910692f, 0.490004860015654f, 0.488499316392171f,
+ 0.516893492513121f, 0.515545795838069f, 0.514220820105042f,
+ 0.512893659480599f, 0.511085398658644f, 0.509264930798830f,
+ 0.537621048957386f, 0.536165057418577f, 0.534661930692635f,
+ 0.533210489850429f, 0.531756722686728f, 0.530384047322116f,
+ 0.559038131029643f, 0.557822105594252f, 0.556569707547800f,
+ 0.555271214486959f, 0.553432792863170f, 0.551315743703168f,
+ 0.549392825464220f, 0.577982280854402f, 0.576562976619477f,
+ 0.575372635620591f, 0.574072847452013f, 0.572872688648164f,
+ 0.571573384448578f, 0.570329067593286f, 0.599518572118246f,
+ 0.598348121604011f, 0.597160846836955f, 0.595942913014291f,
+ 0.592262360278945f, 0.590949984551833f, 0.589596537782439f,
+ 0.618986568059361f, 0.617798845452536f, 0.616549708449253f,
+ 0.615384701753942f, 0.614192476100642f, 0.612892560298438f,
+ 0.611692826552139f, 0.610490163023757f, 0.609174362863638f,
+ 0.638463194928117f, 0.637028017578980f, 0.635559187798163f,
+ 0.633766413298784f, 0.632208978019593f, 0.630423678935153f,
+ 0.629100361498165f, 0.627683147303628f, 0.626307825147069f,
+ 0.656136973617732f, 0.655009501850360f, 0.653877493459184f,
+ 0.652665127999537f, 0.651558827307289f, 0.650386889118506f,
+ 0.649111048361781f, 0.648028207288007f, 0.646759703278198f,
+ 0.645519124438692f, 0.675436924850795f, 0.674280803735362f,
+ 0.673132340969860f, 0.671373346843144f, 0.670134946644868f,
+ 0.668731343028307f, 0.667428990512785f, 0.666412032897529f,
+ 0.665280139756495f, 0.664193037044164f, 0.694188573172643f,
+ 0.693133764391982f, 0.692048704636038f, 0.690982103678122f,
+ 0.689946461409433f, 0.688925040506954f, 0.687918657972205f,
+ 0.686814035317773f, 0.685752672736224f, 0.684629857976692f,
+ 0.683538212068733f, 0.682502742914018f, 0.712650390189218f,
+ 0.711488074001633f, 0.710454507990280f, 0.709369576447197f,
+ 0.708293103236850f, 0.707161341261432f, 0.706142174509451f,
+ 0.705109933856033f, 0.703938122923563f, 0.702882741466471f,
+ 0.701795805729764f, 0.700970588654440f, 0.699907893809526f,
+ 0.698848748944422f, 0.729459722297675f, 0.728559828909388f,
+ 0.727267491809098f, 0.726368916168934f, 0.725549425012621f,
+ 0.724701162719866f, 0.723729724234480f, 0.722805513390802f,
+ 0.721877511846754f, 0.720923464905931f, 0.720063488137338f,
+ 0.719192489800377f, 0.718234018896418f, 0.748886813504199f,
+ 0.748055970602122f, 0.747192096825651f, 0.746255128515039f,
+ 0.745432542499951f, 0.744527975028176f, 0.743657128539979f,
+ 0.742815568594195f, 0.742016966380996f, 0.741081662145480f,
+ 0.740212516669136f, 0.738752388065193f, 0.737838959848034f,
+ 0.737079784380805f, 0.736207833150401f, 0.735328920674480f,
+ 0.766147300368082f, 0.765383964612946f, 0.764598877501888f,
+ 0.763830319512426f, 0.763071341941492f, 0.762261754967287f,
+ 0.761421505873705f, 0.760683170206014f, 0.759876633831137f,
+ 0.759111799483625f, 0.758326333824362f, 0.757541464414185f,
+ 0.756805357349899f, 0.755956399690542f, 0.755084876598806f,
+ 0.754190942656102f, 0.753440741268157f, 0.752571082952576f,
+ 0.751825304285737f, 0.782969421663051f, 0.782195656629464f,
+ 0.781491207489953f, 0.780796925868243f, 0.780038270784649f,
+ 0.779327959705502f, 0.778571499532576f, 0.777927257784694f,
+ 0.777170483964829f, 0.776461606951210f, 0.775702452890459f,
+ 0.774964094372046f, 0.774261007484278f, 0.773530800147826f,
+ 0.772829253854413f, 0.772012428008542f, 0.771256939594450f,
+ 0.770263007078531f, 0.769608778166788f, 0.768868370726016f,
+ 0.800132675567284f, 0.799414457498671f, 0.798801140955651f,
+ 0.798201875508813f, 0.797562927111111f, 0.796973690523352f,
+ 0.796325305652599f, 0.795751914017312f, 0.795062104562951f,
+ 0.794392825660217f, 0.793833580415710f, 0.793228467372150f,
+ 0.792632556794021f, 0.791987407625815f, 0.791349198936522f,
+ 0.790758300136560f, 0.790155406100658f, 0.789502309522568f,
+ 0.788850663559873f, 0.788244230667833f, 0.787690453856242f,
+ 0.787119319515071f, 0.786556345654314f, 0.785871663518153f,
+ 0.817012925306181f, 0.816430511458010f, 0.815893193830343f,
+ 0.815310874396162f, 0.814695628192059f, 0.814349340761576f,
+ 0.813807195954924f, 0.812960708290203f, 0.812353982324263f,
+ 0.811760341393159f, 0.811129283770446f, 0.810570029805507f,
+ 0.810041997821215f, 0.809539025954020f, 0.808890057582255f,
+ 0.808376532093617f, 0.807788126711313f, 0.807229044495192f,
+ 0.806691599106826f, 0.806133405950557f, 0.805529337345578f,
+ 0.804943724604441f, 0.804359069634119f, 0.803604313367394f,
+ 0.803039009317611f, 0.802427016830483f, 0.801897188869670f,
+ 0.833460875990872f, 0.832877329385786f, 0.832411116216460f,
+ 0.831855344205032f, 0.831306648690096f, 0.830826737217004f,
+ 0.830306337776770f, 0.829837395963940f, 0.829322442611281f,
+ 0.828828529233458f, 0.828227895618978f, 0.827688095121686f,
+ 0.827224825179024f, 0.826744576227558f, 0.826190089131040f,
+ 0.825644348776801f, 0.825111095394709f, 0.824576204498035f,
+ 0.824095489895994f, 0.823551761115446f, 0.823018027544573f,
+ 0.822476448619841f, 0.821809962673621f, 0.821291765423256f,
+ 0.820650961031543f, 0.819911412926238f, 0.819376250438516f,
+ 0.818827507074517f, 0.818298634930539f, 0.817837741058990f,
+ 0.817377454006327f, 0.849050771870725f, 0.848598343034601f,
+ 0.848137761126842f, 0.847720880735919f, 0.847272612414349f,
+ 0.846839043139818f, 0.846310781171764f, 0.845917411128721f,
+ 0.845432276174650f, 0.845016330807497f, 0.844560412891491f,
+ 0.844113268000604f, 0.843717326097535f, 0.843254715180455f,
+ 0.842771923047896f, 0.842317456952523f, 0.841894102789524f,
+ 0.841478900234186f, 0.841073526044248f, 0.840678089199553f,
+ 0.840282881990563f, 0.839851728795932f, 0.839437044180938f,
+ 0.839032992924171f, 0.838674041436160f, 0.838208899506653f,
+ 0.837815592964711f, 0.837413586062667f, 0.836945051441415f,
+ 0.836579931119682f, 0.836103168868015f, 0.835691507888582f,
+ 0.835190824315056f, 0.834796921260984f, 0.834397547217396f,
+ 0.833770693471246f, 0.833382919978899f, 0.865249714600488f,
+ 0.864778611194893f, 0.864359035626832f, 0.863974109667504f,
+ 0.863599275330096f, 0.863175308582704f, 0.862750738409356f,
+ 0.862410936451282f, 0.862060660613492f, 0.861703069468698f,
+ 0.861338903148747f, 0.860962071126888f, 0.860613235617068f,
+ 0.860255115049830f, 0.859941728589139f, 0.859556867553160f,
+ 0.859129368056343f, 0.858719511777174f, 0.858375007251166f,
+ 0.857991574276656f, 0.857077372527853f, 0.856709203488606f,
+ 0.856338576783691f, 0.855985971077822f, 0.855629124525839f,
+ 0.855290191537388f, 0.854966165735379f, 0.854612291162095f,
+ 0.854327456524765f, 0.854015841793238f, 0.853649356402945f,
+ 0.853317984411307f, 0.852952982901721f, 0.852579010983093f,
+ 0.852262745823692f, 0.851907995774951f, 0.851574167335807f,
+ 0.851264191083115f, 0.850902019876735f, 0.850580011598579f,
+ 0.850225307767087f, 0.849838013922043f, 0.881877389068535f,
+ 0.881425634756149f, 0.881222935599140f, 0.880862887645329f,
+ 0.880558209099982f, 0.880114949740768f, 0.879806159434422f,
+ 0.879538932468558f, 0.879173454249472f, 0.878803642529878f,
+ 0.878488774313953f, 0.878152689099870f, 0.877787934867571f,
+ 0.877472544637065f, 0.877200689447213f, 0.876929820734263f,
+ 0.876616544942982f, 0.876339352367848f, 0.876037900545338f,
+ 0.875737826612020f, 0.875450927592556f, 0.875146075620635f,
+ 0.874847876858510f, 0.874531696275083f, 0.874232625014850f,
+ 0.873956654705092f, 0.873657406655665f, 0.873376193316924f,
+ 0.873085430435485f, 0.872758743496618f, 0.872421095892080f,
+ 0.872098708018361f, 0.871788288227254f, 0.871530138982730f,
+ 0.871236056832414f, 0.870910572694844f, 0.870635091498100f,
+ 0.870312602324108f, 0.870054759273025f, 0.869714810151487f,
+ 0.869366798437594f, 0.869122644903574f, 0.868812734247120f,
+ 0.868513864159224f, 0.868223756623966f, 0.867982715181480f,
+ 0.867671997828998f, 0.867407549259445f, 0.867121915940559f,
+ 0.866777786882641f, 0.866494871692684f, 0.866169558022502f,
+ 0.865824390903947f, 0.865583948922912f, 0.865260577587179f,
+ 0.897442560230671f, 0.897197975041446f, 0.896936601622887f,
+ 0.896639930442358f, 0.896358496055820f, 0.896149517619677f,
+ 0.895865666553406f, 0.895617470802557f, 0.895302338320577f,
+ 0.895008903956437f, 0.894766131546458f, 0.894431674742994f,
+ 0.894121880084823f, 0.893833250070113f, 0.893486559142867f,
+ 0.893208534201067f, 0.892971102121326f, 0.892730883606376f,
+ 0.892486495783999f, 0.892266606766457f, 0.891912616713162f,
+ 0.891668012816153f, 0.891389344583694f, 0.891127210236977f,
+ 0.890877014364885f, 0.890647919776319f, 0.890418186238544f,
+ 0.890123493822626f, 0.889882930209191f, 0.889638857058379f,
+ 0.889365113843686f, 0.889063011963076f, 0.888904806210479f,
+ 0.888595224427484f, 0.888348740136566f, 0.888084256999739f,
+ 0.887840636004391f, 0.887568183405399f, 0.887244627634201f,
+ 0.886968902204163f, 0.886813404979471f, 0.886525300589349f,
+ 0.886292070226496f, 0.886001016727455f, 0.885775223543453f,
+ 0.885522319161176f, 0.885169241885031f, 0.884891814369998f,
+ 0.884613124661923f, 0.884274453041876f, 0.884000697538040f,
+ 0.883779270240537f, 0.883552044352765f, 0.883321450052730f,
+ 0.883113458915741f, 0.882876096712848f, 0.882646681017793f,
+ 0.882443778020906f, 0.882173341068617f, 0.881932486077329f,
+ 0.881703097548580f, 0.881512430676287f, 0.881324988614956f,
+ 0.881078415830145f, 0.880836019375906f, 0.880592380876914f,
+ 0.880399045447751f, 0.880144055756519f, 0.879916264512867f,
+ 0.912262761203549f, 0.912050025877916f, 0.911837032776423f,
+ 0.911637002893336f, 0.911410664361064f, 0.911128179710946f,
+ 0.910915857381980f, 0.910713468187444f, 0.910514321228273f,
+ 0.910306658353875f, 0.910045217950504f, 0.909852480850540f,
+ 0.909661981811356f, 0.909400554409887f, 0.909267222071404f,
+ 0.909082328173087f, 0.908854885017709f, 0.908645654905872f,
+ 0.908429066044714f, 0.908203229057788f, 0.907975524348559f,
+ 0.907844076324775f, 0.907589558754535f, 0.907386017765108f,
+ 0.907146503676051f, 0.906950671939951f, 0.906734850193100f,
+ 0.906517924929938f, 0.906308309667034f, 0.906112492983346f,
+ 0.905983410335819f, 0.905742671195132f, 0.905504049165944f,
+ 0.905260974598286f, 0.905102165719060f, 0.904889117286156f,
+ 0.904704031490403f, 0.904530549352657f, 0.904335967396413f,
+ 0.904167284044056f, 0.903943525425265f, 0.903781177741016f,
+ 0.903561239085713f, 0.903395399945175f, 0.903235371390208f,
+ 0.903016653613898f, 0.902845104975041f, 0.902610542350492f,
+ 0.902398193708679f, 0.902225960341570f, 0.902021465390646f,
+ 0.901838424668229f, 0.901655758835068f, 0.901444820937381f,
+ 0.901245949833824f, 0.901069151471710f, 0.900915074109771f,
+ 0.900760359450201f, 0.900578847724700f, 0.900395965994494f,
+ 0.900228168805928f, 0.900066616036273f, 0.899902868316372f,
+ 0.899691672138911f, 0.899502275011599f, 0.899326630297481f,
+ 0.899147517398502f, 0.898891311929557f, 0.898737589588815f,
+ 0.898516468975794f, 0.898279435824810f, 0.898133379766176f,
+ 0.897940625694500f, 0.897769012071762f, 0.897584094034260f,
+ 0.897386399090749f, 0.897204596940482f, 0.897029990887014f,
+ 0.896843984043430f, 0.896659433456950f, 0.896499592362130f,
+ 0.896326359805770f, 0.896161841889783f, 0.896002962811382f,
+ 0.895858985647756f, 0.895679053342135f, 0.895472630838065f,
+ 0.895298117862088f, 0.927754563825053f, 0.927595758579098f,
+ 0.927423098901489f, 0.927274066423043f, 0.927064318376423f,
+ 0.926889227267958f, 0.926684097147831f, 0.926538641708822f,
+ 0.926383924569828f, 0.926221023888201f, 0.926069942094448f,
+ 0.925936867669842f, 0.925764476234829f, 0.925474258843906f,
+ 0.925341480568837f, 0.925184458584946f, 0.924997556397188f,
+ 0.924807778651479f, 0.924662367744565f, 0.924518831561345f,
+ 0.924374320422518f, 0.924239452756272f, 0.924035083031803f,
+ 0.923866605961103f, 0.923708308696909f, 0.923561861399422f,
+ 0.923342111379313f, 0.923181861833928f, 0.923022655494797f,
+ 0.922830553518427f, 0.922680580975269f, 0.922530343438704f,
+ 0.922379253084900f, 0.922203220902534f, 0.922017473475193f,
+ 0.921852509706996f, 0.921632506820494f, 0.921478764462909f,
+ 0.921328663166398f, 0.921188703978730f, 0.920977965994368f,
+ 0.920847607574230f, 0.920703577888137f, 0.920525889242200f,
+ 0.920380588508078f, 0.920253615844872f, 0.920139601102595f,
+ 0.920016687689548f, 0.919852868556918f, 0.919622503761834f,
+ 0.919458278730055f, 0.919295852055926f, 0.919134591570401f,
+ 0.918979062183651f, 0.918841010886602f, 0.918716051757939f,
+ 0.918569984788460f, 0.918369062451782f, 0.918215421152890f,
+ 0.918088828939449f, 0.917919780942217f, 0.917733211893808f,
+ 0.917574574952106f, 0.917438548196200f, 0.917323625618981f,
+ 0.917182002664427f, 0.917002289446623f, 0.916827583253820f,
+ 0.916714472811547f, 0.916594418971258f, 0.916460972969900f,
+ 0.916335756364354f, 0.916196649287878f, 0.916065572511741f,
+ 0.915947599776994f, 0.915818586940659f, 0.915632981571024f,
+ 0.915459212086808f, 0.915265459093725f, 0.915128178161501f,
+ 0.914998176624468f, 0.914860825831538f, 0.914625484964234f,
+ 0.914493843678021f, 0.914371779571637f, 0.914248620563387f,
+ 0.914100275148066f, 0.913965738470841f, 0.913826791405259f,
+ 0.913703250466401f, 0.913546026990571f, 0.913439303707573f,
+ 0.913292218567478f, 0.913182541953684f, 0.913036917749445f,
+ 0.912916054634268f, 0.912755642259817f, 0.912614205240910f,
+ 0.912468319200128f, 0.912352343404882f, 0.912236216864747f,
+ 0.912140734213469f, 0.911996828465493f, 0.911863354404087f,
+ 0.911709187715872f, 0.911569284462607f, 0.911451616353229f,
+ 0.911303512456831f, 0.911177218626202f, 0.911072557873086f,
+ 0.910948265059310f, 0.910791791835805f, 0.910670891976214f,
+ 0.910537919420196f, 0.910401546439265f, 0.910274713004068f,
+ 0.942972886116868f, 0.942873255216079f, 0.942736199328047f,
+ 0.942643396902211f, 0.942519604430019f, 0.942405794729767f,
+ 0.942276761652276f, 0.942183483677777f, 0.942062466352059f,
+ 0.941936635001252f, 0.941827790721750f, 0.941701752257506f,
+ 0.941569181480861f, 0.941428171184760f, 0.941266420007758f,
+ 0.941177983355581f, 0.941068131173925f, 0.940966459861221f,
+ 0.940802771917656f, 0.940699319454695f, 0.940546307666082f,
+ 0.940436629963075f, 0.940335105964667f, 0.940233168267479f,
+ 0.940070294791264f, 0.939946864568674f, 0.939806207003117f,
+ 0.939699917303889f, 0.939575570410864f, 0.939449267180147f,
+ 0.939335059915008f, 0.939221634003170f, 0.939100130210696f,
+ 0.938992079038466f, 0.938888993643325f, 0.938794272868458f,
+ 0.938679778519673f, 0.938565198624842f, 0.938447781743272f,
+ 0.938348887694662f, 0.938231674973109f, 0.938100618555889f,
+ 0.937929151200885f, 0.937821433643558f, 0.937688230231913f,
+ 0.937566420334848f, 0.937429830160073f, 0.937311443695009f,
+ 0.937190423215931f, 0.937063169325198f, 0.936979087920893f,
+ 0.936811325718775f, 0.936731671218813f, 0.936634805076269f,
+ 0.936532298789202f, 0.936416186739817f, 0.936286492122839f,
+ 0.936179817873514f, 0.936053037111203f, 0.935955439730240f,
+ 0.935819042190242f, 0.935683118325825f, 0.935582469083303f,
+ 0.935446994096024f, 0.935322108430966f, 0.935220912945627f,
+ 0.935131513738277f, 0.934997955601702f, 0.934903645484091f,
+ 0.934768972260724f, 0.934650110711938f, 0.934559208948897f,
+ 0.934458767641773f, 0.934334472974010f, 0.934235823798025f,
+ 0.934104732808613f, 0.934019917031945f, 0.933918957071512f,
+ 0.933828574280968f, 0.933739424110845f, 0.933624421615613f,
+ 0.933536789816350f, 0.933419733757098f, 0.933286841283564f,
+ 0.933217203121627f, 0.933126139166788f, 0.933030450650661f,
+ 0.932884936176351f, 0.932779450273416f, 0.932651017715157f,
+ 0.932545945362592f, 0.932455958346926f, 0.932337686419334f,
+ 0.932227058302166f, 0.932025539224666f, 0.931920795784066f,
+ 0.931852577738301f, 0.931756049103193f, 0.931621152063621f,
+ 0.931541102847978f, 0.931464901339151f, 0.931333487828105f,
+ 0.931222317831001f, 0.931145274335559f, 0.931048712065999f,
+ 0.930944549228741f, 0.930833966893561f, 0.930767031403833f,
+ 0.930661219955564f, 0.930573682517608f, 0.930490587789090f,
+ 0.930399861447101f, 0.930260043354527f, 0.930174757179721f,
+ 0.930082735120729f, 0.929980062024817f, 0.929881070592492f,
+ 0.929815269359710f, 0.929685646724316f, 0.929601626627662f,
+ 0.929475080077155f, 0.929384083536765f, 0.929293843820907f,
+ 0.929197517689133f, 0.929071496407846f, 0.928928767504129f,
+ 0.928850812213896f, 0.928755858560461f, 0.928683595051250f,
+ 0.928619097034910f, 0.928515473276713f, 0.928415904127483f,
+ 0.928296589209896f, 0.928156514230767f, 0.928075740622194f,
+ 0.927976281024834f, 0.927893758602397f, 0.927793223751259f,
+ 0.927533278933111f, 0.927443413613116f, 0.927330462282895f,
+ 0.927224518557079f, 0.927121263013897f, 0.927032513483020f,
+ 0.926946653005310f, 0.926852163943676f, 0.926782799964173f,
+ 0.926704377754193f, 0.926593212214682f, 0.926514271256028f,
+ 0.926434387396852f, 0.926338455745648f, 0.926241793129996f,
+ 0.926151243722815f, 0.926062446007553f, 0.925975437993879f,
+ 0.925900994878278f, 0.925769198338033f, 0.925673561554096f,
+ 0.925575663106531f, 0.925500150132689f, 0.958275427245807f,
+ 0.958217720942176f, 0.958088351980738f, 0.958006794628661f,
+ 0.957895379103873f, 0.957838037581917f, 0.957720979374284f,
+ 0.957627497340097f, 0.957500947647853f, 0.957418120264750f,
+ 0.957316257911455f, 0.957217318469398f, 0.957134253993810f,
+ 0.957060018503037f, 0.956986278198766f, 0.956895348950843f,
+ 0.956777435571824f, 0.956705582605570f, 0.956594026023218f,
+ 0.956516152335888f, 0.956357855496398f, 0.956298018292689f,
+ 0.956199349632202f, 0.956126323885033f, 0.956032137481781f,
+ 0.955946413187796f, 0.955847188872627f, 0.955754430213996f,
+ 0.955687353291728f, 0.955609174338888f, 0.955480819432032f,
+ 0.955422957826911f, 0.955337878180787f, 0.955277719747449f,
+ 0.955209090297961f, 0.955115833029069f, 0.955013029263148f,
+ 0.954929052626773f, 0.954858410226777f, 0.954780241057584f,
+ 0.954681910461255f, 0.954620468882399f, 0.954550468034354f,
+ 0.954461164292287f, 0.954381926562481f, 0.954303937678699f,
+ 0.954176727439442f, 0.954102874300900f, 0.953998321230092f,
+ 0.953890423951280f, 0.953784655913618f, 0.953722369794478f,
+ 0.953623105806388f, 0.953565738290012f, 0.953492683988373f,
+ 0.953413141506086f, 0.953326969854586f, 0.953262954351555f,
+ 0.953184013301923f, 0.953101894037007f, 0.953041902190311f,
+ 0.952969534053552f, 0.952869749867014f, 0.952789835366490f,
+ 0.952716435862726f, 0.952645254649320f, 0.952542492593948f,
+ 0.952463994814448f, 0.952349666425407f, 0.952275493737817f,
+ 0.952196002674587f, 0.952116683488068f, 0.952054020589248f,
+ 0.951971212262823f, 0.951862394841917f, 0.951768820782406f,
+ 0.951688868658540f, 0.951600614570797f, 0.951492713205816f,
+ 0.951425735970941f, 0.951361524070373f, 0.951238590334566f,
+ 0.951128486638037f, 0.951065909028615f, 0.951010270116159f,
+ 0.950902781595030f, 0.950831870621427f, 0.950771893006073f,
+ 0.950692220598345f, 0.950620597115025f, 0.950534510887917f,
+ 0.950403380584764f, 0.950334478890738f, 0.950272098021135f,
+ 0.950196260665432f, 0.950113227599961f, 0.950054926159529f,
+ 0.949969340742870f, 0.949881762321030f, 0.949768876353368f,
+ 0.949694926630816f, 0.949573394239407f, 0.949506256443609f,
+ 0.949443701324821f, 0.949394489146048f, 0.949300730229581f,
+ 0.949198332808624f, 0.949133287613908f, 0.949025351436334f,
+ 0.948942100841359f, 0.948893326812499f, 0.948797799093608f,
+ 0.948739418726981f, 0.948668659827771f, 0.948605293473731f,
+ 0.948508263335470f, 0.948422283102023f, 0.948334646705041f,
+ 0.948229838411611f, 0.948135977369838f, 0.948064621226811f,
+ 0.947975836227614f, 0.947869887504853f, 0.947787963676601f,
+ 0.947725593707634f, 0.947662889604619f, 0.947606582024487f,
+ 0.947529016389140f, 0.947467718279620f, 0.947393788461035f,
+ 0.947318309358938f, 0.947265244768804f, 0.947190494072727f,
+ 0.947120952789888f, 0.947051021658373f, 0.946947790362777f,
+ 0.946867304156026f, 0.946794285196121f, 0.946721140964183f,
+ 0.946641154869108f, 0.946546018566680f, 0.946455030201867f,
+ 0.946381856581210f, 0.946325274031613f, 0.946255337478520f,
+ 0.946177270118115f, 0.946111078958173f, 0.946023514156008f,
+ 0.945959212905784f, 0.945849080079382f, 0.945786239425211f,
+ 0.945714124061532f, 0.945658743681213f, 0.945601619947753f,
+ 0.945519484258529f, 0.945455400385708f, 0.945357119411477f,
+ 0.945264635751721f, 0.945201727920235f, 0.945141917936100f,
+ 0.945079684332379f, 0.945015928228411f, 0.944946280980823f,
+ 0.944877156498061f, 0.944804451411428f, 0.944739649858619f,
+ 0.944673589496270f, 0.944611136357352f, 0.944511223725923f,
+ 0.944424321938049f, 0.944379812730986f, 0.944289367399254f,
+ 0.944211374628178f, 0.944134509881182f, 0.944064570023833f,
+ 0.944020390576029f, 0.943935471478227f, 0.943859733313642f,
+ 0.943783658792479f, 0.943611497969536f, 0.943543421957604f,
+ 0.943499628090061f, 0.943413995369032f, 0.943307061234208f,
+ 0.943209211047420f, 0.943139391056753f, 0.943036473656225f,
+ 0.942967493742562f, 0.942890220197419f, 0.942826382100022f,
+ 0.942762325628337f, 0.942694266534490f, 0.942624647563169f,
+ 0.942553430443271f, 0.942488591032619f, 0.942370472457731f,
+ 0.942305639341609f, 0.942234519293505f, 0.942182960322720f,
+ 0.942120517409552f, 0.942027480395684f, 0.941952932339245f,
+ 0.941891750910681f, 0.941835281530227f, 0.941772951246597f,
+ 0.941725887717769f, 0.941683506530303f, 0.941593394184890f,
+ 0.941515512558213f, 0.941473311245300f, 0.941404233632176f,
+ 0.941325255534943f, 0.941251396941407f, 0.941202439199741f,
+ 0.941105194862420f, 0.941038074691643f, 0.940974096891109f,
+ 0.940919561213800f, 0.940827436688845f, 0.940746282858963f,
+ 0.940672810072720f, 0.940626178969142f, 0.940581240903949f,
+ 0.940525846543147f, 0.940475217956959f, 0.940413763147880f,
+ 0.940361426762319f, 0.940301758975821f, 0.940257761802955f,
+ 0.940172669790372f, 0.940105121519804f, 0.940055180311003f,
+ 0.940010615028732f, 0.939935007349598f, 0.939881036050097f,
+ 0.939814506904525f, 0.939749695798634f, 0.939696784747100f,
+ 0.939625052341671f, 0.939556008686751f, 0.939512250891858f,
+ 0.939433258280986f, 0.939307887545587f, 0.939263971661818f,
+ 0.939215889164479f, 0.939167376618607f, 0.939121752120582f,
+ 0.971967121890630f, 0.971911994887031f, 0.971870201384295f,
+ 0.971825989837210f, 0.971772117969243f, 0.971732534447172f,
+ 0.971671680117055f, 0.971613103848265f, 0.971568277310130f,
+ 0.971497005410360f, 0.971437965611784f, 0.971374177119527f,
+ 0.971311337696788f, 0.971265675503488f, 0.971211728334830f,
+ 0.971173654659489f, 0.971097115191358f, 0.971055546830150f,
+ 0.970984778394942f, 0.970938216168554f, 0.970854927036070f,
+ 0.970811611972044f, 0.970768836659056f, 0.970693057804116f,
+ 0.970619816488717f, 0.970549808560914f, 0.970503576286217f,
+ 0.970432624714843f, 0.970388770439317f, 0.970349084551768f,
+ 0.970298841533354f, 0.970239776951519f, 0.970194884492194f,
+ 0.970152831029440f, 0.970090756146688f, 0.970000755530803f,
+ 0.969956773446540f, 0.969911684742526f, 0.969822649120895f,
+ 0.969768380795225f, 0.969704307081910f, 0.969664862298864f,
+ 0.969522047689548f, 0.969481769085865f, 0.969436588593615f,
+ 0.969381675858440f, 0.969343203466823f, 0.969266922648030f,
+ 0.969211596747585f, 0.969146961317084f, 0.969103250668287f,
+ 0.969062089905576f, 0.968999137424737f, 0.968951901823687f,
+ 0.968911916700945f, 0.968839412499583f, 0.968786327157115f,
+ 0.968751440609257f, 0.968695995382998f, 0.968656550332251f,
+ 0.968616734717366f, 0.968567111115919f, 0.968522709698953f,
+ 0.968475903432075f, 0.968425439658996f, 0.968384501083956f,
+ 0.968344193764876f, 0.968302257085111f, 0.968251151994677f,
+ 0.968211894760266f, 0.968149794153557f, 0.968115330462226f,
+ 0.968048914429886f, 0.967991768898303f, 0.967957662757341f,
+ 0.967901867217052f, 0.967842068702658f, 0.967808096936220f,
+ 0.967746019972052f, 0.967668943461478f, 0.967635103661283f,
+ 0.967593146864882f, 0.967519676893017f, 0.967485969937633f,
+ 0.967444577221587f, 0.967394666397157f, 0.967359767192470f,
+ 0.967326238566736f, 0.967269924014565f, 0.967205488183220f,
+ 0.967150317626980f, 0.967112403335753f, 0.967079092591829f,
+ 0.967028848415111f, 0.966995625287745f, 0.966944244501088f,
+ 0.966911108413841f, 0.966837881450369f, 0.966804830861701f,
+ 0.966754806420632f, 0.966711974994014f, 0.966667104208814f,
+ 0.966634226055970f, 0.966584175955802f, 0.966508735173705f,
+ 0.966412422599927f, 0.966337465181747f, 0.966304793467667f,
+ 0.966245985853935f, 0.966195744077399f, 0.966162098544657f,
+ 0.966129595993283f, 0.966065142768099f, 0.966008381118610f,
+ 0.965958352967421f, 0.965907874942570f, 0.965860700300948f,
+ 0.965828446705695f, 0.965778827683499f, 0.965702907650509f,
+ 0.965666139199334f, 0.965634050335282f, 0.965562124832480f,
+ 0.965517675557660f, 0.965436730013674f, 0.965359948253595f,
+ 0.965303173457664f, 0.965250492014372f, 0.965212160348909f,
+ 0.965170781733245f, 0.965118350471165f, 0.965086664224710f,
+ 0.965045725545761f, 0.965008421977835f, 0.964951359793605f,
+ 0.964899196027095f, 0.964824463593891f, 0.964772498398080f,
+ 0.964741091666004f, 0.964670443460613f, 0.964607787072424f,
+ 0.964551286041044f, 0.964496863047423f, 0.964465651940309f,
+ 0.964429879490986f, 0.964398594355012f, 0.964351600338458f,
+ 0.964307369250915f, 0.964271608387851f, 0.964227417766565f,
+ 0.964171256711235f, 0.964081060807346f, 0.964045880993023f,
+ 0.963985242031280f, 0.963945647749731f, 0.963907826366924f,
+ 0.963868197486238f, 0.963799938452283f, 0.963765207634998f,
+ 0.963720978726527f, 0.963684133853149f, 0.963649040588658f,
+ 0.963607534831113f, 0.963551321580042f, 0.963497204430698f,
+ 0.963424714887615f, 0.963374725409701f, 0.963344439312775f,
+ 0.963310870684628f, 0.963276090213696f, 0.963222993617856f,
+ 0.963129610624736f, 0.963090851003960f, 0.963060656182045f,
+ 0.963002655525515f, 0.962963164663654f, 0.962922597291628f,
+ 0.962882895853952f, 0.962848459982529f, 0.962771647590578f,
+ 0.962711992610457f, 0.962654371383541f, 0.962616259829856f,
+ 0.962562805802114f, 0.962525034500491f, 0.962484652724640f,
+ 0.962439417576559f, 0.962379290577768f, 0.962332639787563f,
+ 0.962297429044519f, 0.962249888365194f, 0.962200268067524f,
+ 0.962140347398233f, 0.962107323902686f, 0.962041853572779f,
+ 0.962008176643193f, 0.961975646236443f, 0.961924711249088f,
+ 0.961879023273176f, 0.961831509403864f, 0.961775868784700f,
+ 0.961746822676295f, 0.961716324502158f, 0.961640369374947f,
+ 0.961611429245648f, 0.961547547285081f, 0.961509489301074f,
+ 0.961480654833700f, 0.961447331320540f, 0.961387391917649f,
+ 0.961341468430511f, 0.961304123251159f, 0.961249351920283f,
+ 0.961212476662826f, 0.961178912062857f, 0.961090639886684f,
+ 0.961045097435303f, 0.961003493534426f, 0.960967973701429f,
+ 0.960938077569799f, 0.960884289021088f, 0.960819067244493f,
+ 0.960780320069444f, 0.960743327089429f, 0.960698279588637f,
+ 0.960667663213218f, 0.960634721461931f, 0.960594821404335f,
+ 0.960541761992586f, 0.960508945187094f, 0.960469856554093f,
+ 0.960426570022417f, 0.960387981064035f, 0.960347680867052f,
+ 0.960307472352100f, 0.960253653207329f, 0.960218687435176f,
+ 0.960186390857838f, 0.960158615758153f, 0.960112507807049f,
+ 0.960071344004724f, 0.960020983326931f, 0.959987667027966f,
+ 0.959956805300620f, 0.959927767865339f, 0.959896946101205f,
+ 0.959833975596898f, 0.959804107476303f, 0.959775204077521f,
+ 0.959747793439038f, 0.959702341797309f, 0.959634363246830f,
+ 0.959569029679109f, 0.959533052569412f, 0.959501336485215f,
+ 0.959474119229892f, 0.959419839626233f, 0.959341023507977f,
+ 0.959295438847227f, 0.959260524216600f, 0.959227089969235f,
+ 0.959200063868628f, 0.959173070436206f, 0.959125094986595f,
+ 0.959090861914065f, 0.959045960373426f, 0.959019094445528f,
+ 0.958992260803902f, 0.958954605304605f, 0.958916769209807f,
+ 0.958886764667757f, 0.958841502703832f, 0.958814827566503f,
+ 0.958777559544421f, 0.958702397967575f, 0.958671995235790f,
+ 0.958641005586528f, 0.958614486425838f, 0.958577069623433f,
+ 0.958549172046238f, 0.958512135033094f, 0.958441537076639f,
+ 0.958415172644034f, 0.958365776019084f, 0.958303096769660f,
+ 0.958263716843560f, 0.958185028692631f, 0.958145375013881f,
+ 0.958119191626474f, 0.958068694182465f, 0.958018918920774f,
+ 0.957949774071843f, 0.957901198070889f, 0.957866533472984f,
+ 0.957836112980788f, 0.957808708199475f, 0.957782766016934f,
+ 0.957746388685411f, 0.957713730335926f, 0.957680192807017f,
+ 0.957652942114070f, 0.957612146941780f, 0.957579663093048f,
+ 0.957545516193684f, 0.957506903711571f, 0.957472054074152f,
+ 0.957436503578588f, 0.957395181689988f, 0.957359417445984f,
+ 0.957311450421340f, 0.957282679922514f, 0.957209067658546f,
+ 0.957175128902001f, 0.957146509039227f, 0.957112476412398f,
+ 0.957074501733838f, 0.957036984482748f, 0.956999759246114f,
+ 0.956970851060728f, 0.956927264285104f, 0.956871217307326f,
+ 0.956844594547104f, 0.956813890060049f, 0.956780501144467f,
+ 0.956744461342733f, 0.956714994383490f, 0.956665821298225f,
+ 0.956640789566225f, 0.956610849917976f, 0.956550012130147f,
+ 0.956496846132444f, 0.956456019909584f, 0.956407330143974f,
+ 0.956337082514063f, 0.956307879496556f, 0.956240737167033f,
+ 0.956198057147075f, 0.956160694192385f, 0.956121596304570f,
+ 0.956084736949496f, 0.956060067954893f, 0.956028844508730f,
+ 0.956004232084434f, 0.955958879345404f, 0.955924575071532f,
+ 0.955883193326281f, 0.955847442906133f, 0.955813069719572f,
+ 0.955772060592762f, 0.955716387710303f, 0.955685728196237f,
+ 0.955645039455566f, 0.955586534578155f, 0.955554652983814f,
+ 0.955528968538545f, 0.955494147326420f, 0.955433145575848f,
+ 0.955398247978298f, 0.955366951631532f, 0.955341405225951f,
+ 0.955313054458997f, 0.955274786773442f, 0.955242793028928f,
+ 0.955217357860814f, 0.955166187024662f, 0.955135160237832f,
+ 0.955097685925402f, 0.955066290889999f, 0.955040052475596f,
+ 0.955002923204769f, 0.954979072001785f, 0.954946002019177f,
+ 0.954922204505984f, 0.954894464435323f, 0.954869328998638f,
+ 0.954822936955329f, 0.954799245855306f, 0.954763225287706f,
+ 0.954739587182904f, 0.954702687836680f, 0.954650651504783f,
+ 0.954627091600106f, 0.954586735039616f, 0.954543509103832f,
+ 0.954487508586822f, 0.954455423137410f, 0.954431992380845f,
+ 0.954395246407735f, 0.954368158761341f, 0.954328519361469f,
+ 0.954305192349057f, 0.954278682066255f, 0.954244790518497f,
+ 0.954210949137707f, 0.954183768594435f, 0.954155136691491f,
+ 0.954118315432295f, 0.954088453549574f, 0.954056625115569f,
+ 0.954015559093910f, 0.953982138679812f, 0.953953458629932f,
+ 0.953924167157067f, 0.953876244476431f, 0.953843277247482f,
+ 0.953820332674190f, 0.953783435648809f, 0.986922760337215f,
+ 0.986893347132258f, 0.986853719027791f, 0.986802177140835f,
+ 0.986780595774923f, 0.986735948250994f, 0.986714413193494f,
+ 0.986691544524921f, 0.986650126059355f, 0.986617659263719f,
+ 0.986574768928646f, 0.986553349155405f, 0.986509490127068f,
+ 0.986488116171129f, 0.986459808203524f, 0.986425749277674f,
+ 0.986401282919961f, 0.986377715956588f, 0.986347680503076f,
+ 0.986320259258079f, 0.986271810607144f, 0.986245050428010f,
+ 0.986223881966244f, 0.986176681328462f, 0.986145917556990f,
+ 0.986110482684939f, 0.986085509969948f, 0.986055947339547f,
+ 0.986034913700289f, 0.986000776580319f, 0.985965492876594f,
+ 0.985919261677516f, 0.985892883092507f, 0.985830609056223f,
+ 0.985801336195789f, 0.985779112009499f, 0.985758255139701f,
+ 0.985733238386015f, 0.985712426258423f, 0.985655501978720f,
+ 0.985626353966535f, 0.985590903455782f, 0.985563563636873f,
+ 0.985523439019124f, 0.985500817867769f, 0.985474456307468f,
+ 0.985449609353800f, 0.985405907406883f, 0.985360059543588f,
+ 0.985310904032878f, 0.985270714901873f, 0.985226727495785f,
+ 0.985193263279922f, 0.985172811115538f, 0.985115330253415f,
+ 0.985063087425340f, 0.985008336715826f, 0.984978613966300f,
+ 0.984941852360105f, 0.984912509332049f, 0.984850180222146f,
+ 0.984824312074071f, 0.984799854405289f, 0.984753768215678f,
+ 0.984732561511123f, 0.984706135723891f, 0.984678185530603f,
+ 0.984655684360588f, 0.984631067011433f, 0.984587731589628f,
+ 0.984558318722489f, 0.984523715071264f, 0.984502265042700f,
+ 0.984481759317740f, 0.984445908996804f, 0.984416448023547f,
+ 0.984384996037713f, 0.984346921353555f, 0.984323353794763f,
+ 0.984302492135683f, 0.984234826486034f, 0.984229190516759f,
+ 0.984189170519958f, 0.984154292087286f, 0.984130608803616f,
+ 0.984094955038999f, 0.984074738744969f, 0.984037558142149f,
+ 0.983994580486844f, 0.983967885641745f, 0.983943737430130f,
+ 0.983915944574975f, 0.983884236637719f, 0.983864172195582f,
+ 0.983831831959887f, 0.983802938958836f, 0.983775833669071f,
+ 0.983753187871529f, 0.983730105132050f, 0.983703133254244f,
+ 0.983669828705679f, 0.983644384789753f, 0.983620626656529f,
+ 0.983600307731900f, 0.983567972476239f, 0.983543210140435f,
+ 0.983491238528487f, 0.983454493564921f, 0.983367632603668f,
+ 0.983347051720359f, 0.983327810408723f, 0.983301876305209f,
+ 0.983276092919806f, 0.983251755401310f, 0.983232135834041f,
+ 0.983208069222592f, 0.983172240353604f, 0.983147868868929f,
+ 0.983081868982781f, 0.983045201512550f, 0.983012533352780f,
+ 0.982993510710699f, 0.982961813650806f, 0.982942002878203f,
+ 0.982889024861584f, 0.982867015386914f, 0.982847649649163f,
+ 0.982824371463141f, 0.982805048997046f, 0.982773218775641f,
+ 0.982748418352122f, 0.982729158100405f, 0.982703997651039f,
+ 0.982682108753123f, 0.982655505774959f, 0.982623018430868f,
+ 0.982593842523021f, 0.982573406804049f, 0.982547353367972f,
+ 0.982519585224023f, 0.982459288284645f, 0.982436394370087f,
+ 0.982415125919954f, 0.982387582740862f, 0.982367292542386f,
+ 0.982334965662622f, 0.982312635851139f, 0.982294117369415f,
+ 0.982251715517260f, 0.982224369337035f, 0.982205907157262f,
+ 0.982182761751450f, 0.982159903255411f, 0.982126373095434f,
+ 0.982107184687307f, 0.982084954430489f, 0.982041099920316f,
+ 0.982022769212310f, 0.981977331062889f, 0.981939690609133f,
+ 0.981914923334885f, 0.981856360333239f, 0.981834102582585f,
+ 0.981809598272734f, 0.981770121636027f, 0.981739618531661f,
+ 0.981717245565463f, 0.981683230681818f, 0.981652689781140f,
+ 0.981634196393471f, 0.981613495190574f, 0.981564013372326f,
+ 0.981524277410168f, 0.981498926071219f, 0.981450560157412f,
+ 0.981425266998042f, 0.981402192708996f, 0.981384225827395f,
+ 0.981365906020084f, 0.981343753206944f, 0.981300572619224f,
+ 0.981282678369968f, 0.981264437359234f, 0.981228534383292f,
+ 0.981209049239098f, 0.981182762882197f, 0.981158372244174f,
+ 0.981134700930964f, 0.981112259373601f, 0.981094152386932f,
+ 0.981068500570816f, 0.981036844052188f, 0.980986685235874f,
+ 0.980968652491355f, 0.980913591726037f, 0.980889441073170f,
+ 0.980848240136851f, 0.980807605621403f, 0.980774106571861f,
+ 0.980755251245704f, 0.980729769330780f, 0.980680692805421f,
+ 0.980654718270742f, 0.980636884095367f, 0.980615965819145f,
+ 0.980595938595629f, 0.980561974203509f, 0.980496049774783f,
+ 0.980478290932049f, 0.980460550538442f, 0.980432150774978f,
+ 0.980410648593876f, 0.980393295011126f, 0.980357014272223f,
+ 0.980326248344638f, 0.980299534184272f, 0.980264913469273f,
+ 0.980247317913468f, 0.980230066117568f, 0.980208414358917f,
+ 0.980187260862336f, 0.980132312570413f, 0.980109049768295f,
+ 0.980075371162398f, 0.980051209784426f, 0.980031865718607f,
+ 0.979994042174202f, 0.979937198188725f, 0.979920111293897f,
+ 0.979898807860237f, 0.979877564940339f, 0.979856737310176f,
+ 0.979835492266172f, 0.979818488652277f, 0.979774221453093f,
+ 0.979745444849853f, 0.979727225403310f, 0.979700314470918f,
+ 0.979683392321718f, 0.979666182005815f, 0.979611629473783f,
+ 0.979590852554355f, 0.979573692364784f, 0.979556851511248f,
+ 0.979534389742842f, 0.979506478571303f, 0.979478322317171f,
+ 0.979455398954295f, 0.979438638890386f, 0.979412895074252f,
+ 0.979392385523425f, 0.979371783642596f, 0.979355088151969f,
+ 0.979333607767233f, 0.979313189453699f, 0.979292658573124f,
+ 0.979263179385256f, 0.979246564075690f, 0.979198755309747f,
+ 0.979174516662574f, 0.979146797165384f, 0.979123464872970f,
+ 0.979106928065550f, 0.979069685325203f, 0.979053179759749f,
+ 0.979030332693243f, 0.978986657751362f, 0.978970198965351f,
+ 0.978946974036735f, 0.978926682675456f, 0.978905244289410f,
+ 0.978862765525649f, 0.978846097879915f, 0.978821788954481f,
+ 0.978803246624047f, 0.978766048702116f, 0.978746831450756f,
+ 0.978725426164543f, 0.978709138346453f, 0.978685752931826f,
+ 0.978669496205975f, 0.978649410758398f, 0.978624780793713f,
+ 0.978608570268773f, 0.978592094654600f, 0.978550920367351f,
+ 0.978513470662572f, 0.978497042038316f, 0.978480907605864f,
+ 0.978464788541799f, 0.978434611643655f, 0.978418522841283f,
+ 0.978390826033207f, 0.978374767625467f, 0.978353654444658f,
+ 0.978337626389741f, 0.978305022211732f, 0.978282789753918f,
+ 0.978266806773291f, 0.978234606202049f, 0.978203779970373f,
+ 0.978184787014873f, 0.978165052701335f, 0.978112832751907f,
+ 0.978096668038331f, 0.978058855230544f, 0.978042990406293f,
+ 0.978026872130719f, 0.978008154881375f, 0.977992335006128f,
+ 0.977968977528089f, 0.977951947084624f, 0.977932854511879f,
+ 0.977913681988638f, 0.977897936222764f, 0.977878414356271f,
+ 0.977862698102290f, 0.977844552571458f, 0.977825081855045f,
+ 0.977804621661370f, 0.977787726784343f, 0.977768071434286f,
+ 0.977752443360056f, 0.977733052522146f, 0.977717453632544f,
+ 0.977698822040011f, 0.977673199611945f, 0.977619793686062f,
+ 0.977602075088056f, 0.977576669465553f, 0.977542954045465f,
+ 0.977527455035169f, 0.977504152387557f, 0.977488682089939f,
+ 0.977472970424132f, 0.977448260145925f, 0.977398011663338f,
+ 0.977372297882134f, 0.977355666246754f, 0.977330698924145f,
+ 0.977298426353436f, 0.977283068966841f, 0.977250107782567f,
+ 0.977207119451928f, 0.977191803723299f, 0.977176502215050f,
+ 0.977161214907559f, 0.977102300457946f, 0.977063703061534f,
+ 0.977040908723148f, 0.977025676110134f, 0.977004557438028f,
+ 0.976989105443786f, 0.976970178122162f, 0.976938120674094f,
+ 0.976922957790298f, 0.976906582534861f, 0.976891202359989f,
+ 0.976872365503571f, 0.976857258525261f, 0.976833112633702f,
+ 0.976815174516876f, 0.976791566318235f, 0.976756563193258f,
+ 0.976741524718733f, 0.976698317437522f, 0.976651346433524f,
+ 0.976632645407126f, 0.976617419614971f, 0.976595534775329f,
+ 0.976576881856942f, 0.976561937972295f, 0.976546768986203f,
+ 0.976528820169054f, 0.976508065016963f, 0.976482720813795f,
+ 0.976467844882108f, 0.976446124920447f, 0.976403186250199f,
+ 0.976388350299752f, 0.976349887902191f, 0.976319445660324f,
+ 0.976304414770861f, 0.976289632025076f, 0.976264145624284f,
+ 0.976240532650802f, 0.976209273788994f, 0.976194311547957f,
+ 0.976179595220112f, 0.976161231562614f, 0.976146541915870f,
+ 0.976123701179350f, 0.976105154089893f, 0.976082960591513f,
+ 0.976042090614826f, 0.976026251263904f, 0.976007993676071f,
+ 0.975989338013885f, 0.975962781592164f, 0.975944570379698f,
+ 0.975930011937128f, 0.975907027483125f, 0.975888636596245f,
+ 0.975874117446146f, 0.975858399012390f, 0.975814767533837f,
+ 0.975770098214803f, 0.975755629917150f, 0.975740950932038f,
+ 0.975700126458313f, 0.975679056973671f, 0.975664640074239f,
+ 0.975650236167557f, 0.975580150537865f, 0.975564561643136f,
+ 0.975550195260922f, 0.975511963306388f, 0.975491182846468f,
+ 0.975476854410694f, 0.975458937949595f, 0.975441037900350f,
+ 0.975403340889900f, 0.975378834602348f, 0.975354759608012f,
+ 0.975337489516777f, 0.975321825596625f, 0.975307598487264f,
+ 0.975283675623818f, 0.975269473727394f, 0.975250515677110f,
+ 0.975222503407069f, 0.975208339209176f, 0.975185839227530f,
+ 0.975164426313621f, 0.975150299633837f, 0.975132834025883f,
+ 0.975114957906684f, 0.975095028384530f, 0.975080951799970f,
+ 0.975066887752846f, 0.975041741905158f, 0.975027702605693f,
+ 0.975005031349011f, 0.974987469475237f, 0.974973467389524f,
+ 0.974937686203278f, 0.974920168212932f, 0.974902828972747f,
+ 0.974872881920399f, 0.974855407841305f, 0.974841479047993f,
+ 0.974824552649550f, 0.974807122649889f, 0.974793024340947f,
+ 0.974779144750431f, 0.974761758864481f, 0.974743009828295f,
+ 0.974702179104436f, 0.974688347653034f, 0.974654844155268f,
+ 0.974629896769268f, 0.974609196289333f, 0.974595412768757f,
+ 0.974578144647479f, 0.974554761535420f, 0.974512105515984f,
+ 0.974493684651349f, 0.974479960679643f, 0.974445019848412f,
+ 0.974420342456999f, 0.974406653869030f, 0.974389302704651f,
+ 0.974375638158094f, 0.974356380485032f, 0.974342739773739f,
+ 0.974329111024600f, 0.974308667973371f, 0.974295062936910f,
+ 0.974277268860387f, 0.974257232359766f, 0.974243662862119f,
+ 0.974226418880396f, 0.974209427476104f, 0.974160143649720f,
+ 0.974135024493451f, 0.974121512976261f, 0.974108013252395f,
+ 0.974080630570305f, 0.974065964331530f, 0.974015781527702f,
+ 0.974002134457994f, 0.973968181110526f, 0.973941430008605f,
+ 0.973900624962740f, 0.973887216439222f, 0.973870418549401f,
+ 0.973850724231017f, 0.973837350500092f, 0.973820594740841f,
+ 0.973798522327485f, 0.973777807158058f, 0.973762348240277f,
+ 0.973720684603163f, 0.973707190440952f, 0.973690525402443f,
+ 0.973642098553068f, 0.973616296682685f, 0.973596682908872f,
+ 0.973574106353138f, 0.973560681801021f, 0.973542922676264f,
+ 0.973514709802091f, 0.973501505700315f, 0.973484979448508f,
+ 0.973471798042410f, 0.973456500215393f, 0.973431510917770f,
+ 0.973406975669394f, 0.973387629418889f, 0.973367520795037f,
+ 0.973303609216807f, 0.973284719314881f, 0.973267149528169f,
+ 0.973250597568072f, 0.973237526949800f, 0.973221187129731f,
+ 0.973199716550181f, 0.973183406462760f, 0.973164990751466f,
+ 0.973148718186700f, 0.973132284540874f, 0.973116049604857f,
+ 0.973096165761680f, 0.973079968266462f, 0.973062611300583f,
+ 0.973011998962548f, 0.972975003152676f, 0.972962075047062f,
+ 0.972945970132927f, 0.972923360946523f, 0.972900115256482f,
+ 0.972870276402172f, 0.972833571656061f, 0.972811347406848f,
+ 0.972777650857546f, 0.972760603364386f, 0.972744749946391f,
+ 0.972728915316704f, 0.972491425539914f, 0.972700300574877f,
+ 0.972657493357715f, 0.972632608746347f, 0.972616859264461f,
+ 0.972598619191692f, 0.972345186679530f, 0.972332446611726f,
+ 0.972312645617935f, 0.972296953045242f, 0.972283079206423f,
+ 0.972354259937865f, 0.972459209202242f, 0.972446537807611f,
+ 0.972430934483078f, 0.972176086196692f, 0.972163441757064f,
+ 0.972150807994720f, 0.972135323233078f, 0.972332090264623f,
+ 0.972296656512621f, 0.972281150355395f, 0.972261199146408f,
+ 0.972040937679384f, 0.972020853805726f, 0.971986352971951f,
+ 0.971970171296574f, 0.971957642162397f, 0.971945123560269f,
+ 0.971925366207946f, 0.971903023702926f, 0.971890536316386f,
+ 0.971869250638017f, 0.971845667162464f, 0.971833210739844f,
+ 0.971804277862056f, 0.971791841948099f, 0.971779416449271f,
+ 0.971764022143080f, 0.971747980164594f, 0.971735585761423f,
+ 0.971717421387968f, 0.971705047573062f, 0.971692684095817f,
+ 0.971677569616385f, 0.971654562083561f, 0.971639423267713f,
+ 0.971627100732641f, 0.971586709457336f, 0.971574406851170f,
+ 0.971562114493840f, 0.971547034241681f, 0.971528565510735f,
+ 0.971512518293816f, 0.971500266599958f, 0.971464929056654f,
+ 0.971452697226202f, 0.971418394914989f, 0.971406182921065f,
+ 0.971393981063291f, 0.971376907284318f, 0.971343341781312f,
+ 0.971331169765085f, 0.971319007835423f, 0.971298578641970f,
+ 0.971283662437030f, 0.971271530457587f, 0.971252454892243f,
+ 0.971240342827092f, 0.971225487479047f, 0.971204546317460f,
+ 0.971192464074439f, 0.971180391807126f, 0.971165370878734f,
+ 0.971153318463628f, 0.971140124638148f, 0.971123237224655f,
+ 0.971106480118568f, 0.971083157368327f, 0.971071154129043f,
+ 0.971034062159235f, 0.971018455159871f, 0.971006480918128f,
+ 0.970994516519120f, 0.970977092320320f, 0.970965003529432f,
+ 0.970950326173765f, 0.970937252551404f, 0.970925337052152f,
+ 0.970913431323699f, 0.970898580575337f, 0.970880595387861f,
+ 0.970865984305819f, 0.970854117296896f, 0.970827109552462f,
+ 0.970787121116513f, 0.970771664135147f, 0.970734868566354f,
+ 0.970720322134539f, 0.970708511741481f, 0.970696710990446f,
+ 0.970670091597675f, 0.970638391535586f, 0.970594506910305f,
+ 0.970571972858635f, 0.970560218611173f, 0.970545523090248f,
+ 0.970524813815583f, 0.970506896143813f, 0.970470838904634f,
+ 0.970456283769786f, 0.970444585924069f, 0.970432897582902f,
+ 0.970421218734771f, 0.970408406937207f, 0.970396747017460f,
+ 0.970385096556351f, 0.970370753011372f, 0.970353198100957f,
+ 0.970341440710966f, 0.970329827852595f, 0.970315487202049f,
+ 0.970303893080486f, 0.970269092080499f, 0.970251821409422f,
+ 0.970240254808422f, 0.970228697553167f, 0.970204163935976f,
+ 0.970191352781056f, 0.970179823267678f, 0.970165616505559f,
+ 0.970154105529172f, 0.970125646226645f, 0.970114153457064f,
+ 0.970102669944216f, 0.970087197011956f, 0.970075731899408f,
+ 0.970061466817810f, 0.970050020142580f, 0.970019988110384f,
+ 0.969972599795979f, 0.969961179607361f, 0.969948632115153f,
+ 0.969908746684612f, 0.969894682809931f, 0.969883170119172f,
+ 0.969871795082014f, 0.969860429159958f, 0.969846343221063f,
+ 0.969834995442697f, 0.969823656746862f, 0.969812327122728f,
+ 0.969798343939773f, 0.969787032374211f, 0.969772004681985f,
+ 0.969760711166196f, 0.969749426668237f, 0.969688426500697f,
+ 0.969677159032194f, 0.969663245949753f, 0.969651996391660f,
+ 0.969625257076576f, 0.969605977523474f, 0.969594754369765f,
+ 0.969583416110931f, 0.969572210865374f, 0.969561014532258f,
+ 0.969547180480629f, 0.969513246925795f, 0.969502076773797f,
+ 0.969485003257335f, 0.969472720232916f, 0.969461576525169f,
+ 0.969450319795174f, 0.969439193805961f, 0.969420426798349f,
+ 0.969406597238909f, 0.969395497473808f, 0.969376382415080f,
+ 0.969365300059954f, 0.969354226471582f, 0.969343161639623f,
+ 0.969325363726951f, 0.969314316299151f, 0.969299688455677f,
+ 0.969287530932357f, 0.969276509560222f, 0.969265496882931f,
+ 0.969254492890265f, 0.969243497572002f, 0.969229888366585f,
+ 0.969216720237517f, 0.969205750840223f, 0.969191858846575f,
+ 0.969172668560500f, 0.969153335935209f, 0.969142400641392f,
+ 0.969126015512418f, 0.969115097292469f, 0.969077096987749f,
+ 0.969066195393082f, 0.969054177875027f, 0.969035207922252f,
+ 0.969016820085756f, 0.969005952381187f, 0.968992380215632f,
+ 0.968981529478561f, 0.968968080712556f, 0.968955177833115f,
+ 0.968944352461800f, 0.968926930585352f, 0.968913081597044f,
+ 0.968902281458119f, 0.968891489754488f, 0.968877127176801f,
+ 0.968865230815253f, 0.968851865757681f, 0.968841107561196f,
+ 0.968827062840517f, 0.968816321345894f, 0.968790935192768f,
+ 0.968768188742240f, 0.968757471871783f, 0.968732819132344f,
+ 0.968722118670559f, 0.968708836038462f, 0.968698152157404f,
+ 0.968657086013096f, 0.968646418161465f, 0.968624158304073f,
+ 0.968613506791441f, 0.968596572545570f, 0.968579061738822f,
+ 0.968561014979841f, 0.968550396126364f, 0.968537202998056f,
+ 0.968526600536290f, 0.968516006279852f, 0.968505420219268f,
+ 0.968494842345072f, 0.968484165957274f, 0.968448598566619f,
+ 0.968438044781854f, 0.968427499146142f, 0.968390635470169f,
+ 0.968375420846665f, 0.968364899038218f, 0.968354385341824f,
+ 0.968332577679763f, 0.968322079974605f, 0.968311590353718f,
+ 0.968301004306507f, 0.968290530868138f, 1.00143961392577f,
+ 1.00142968724369f, 1.00141976816660f, 1.00140446209241f,
+ 1.00137317954622f, 1.00136328275028f, 1.00135014137470f,
+ 1.00133902200513f, 1.00131146903538f, 1.00130160207297f,
+ 1.00128450674836f, 1.00127465472096f, 1.00126481021308f,
+ 1.00125497321619f, 1.00124225940996f, 1.00122500973736f,
+ 1.00121519503829f, 1.00120538781624f, 1.00119545326034f,
+ 1.00118313784023f, 1.00117335295109f, 1.00116357550511f,
+ 1.00115380549385f, 1.00113942387171f, 1.00112966862280f,
+ 1.00109314412405f, 1.00107946844994f, 1.00106973486180f,
+ 1.00105418133174f, 1.00104229664803f, 1.00103006985615f,
+ 1.00102036557219f, 1.00100778834931f, 1.00096616031686f,
+ 1.00095647745121f, 1.00092507998694f, 1.00091541138654f,
+ 1.00090575009718f, 1.00089487173027f, 1.00087920118928f,
+ 1.00086956165688f, 1.00084041349058f, 1.00082467685478f,
+ 1.00080693845908f, 1.00079732740243f, 1.00078092556175f,
+ 1.00074906862448f, 1.00073947877630f, 1.00072989615011f,
+ 1.00072019315343f, 1.00066830628723f, 1.00065874454301f,
+ 1.00064918998899f, 1.00063246831712f, 1.00061832499982f,
+ 1.00060879176472f, 1.00059926568779f, 1.00058974676105f,
+ 1.00058023497646f, 1.00055457310189f, 1.00054507529979f,
+ 1.00049419893387f, 1.00048471465008f, 1.00044437316419f,
+ 1.00043490254958f, 1.00042341268056f, 1.00039435986397f,
+ 1.00038491011887f, 1.00037546743804f, 1.00036303684998f,
+ 1.00034636152703f, 1.00033693981721f, 1.00032504191268f,
+ 1.00031563421870f, 1.00030623354207f, 1.00029683987498f,
+ 1.00028352575811f, 1.00026603619147f, 1.00025666330226f,
+ 1.00024729739164f, 1.00023128713687f, 1.00022193504773f,
+ 1.00020066879491f, 1.00019133040773f, 1.00017618304649f,
+ 1.00016577323552f, 1.00015645552966f, 1.00013420200033f,
+ 1.00009861649226f, 1.00008931885617f, 1.00008002811490f,
+ 1.00007062639745f, 1.00005669782209f, 1.00004496048148f,
+ 1.00003569712223f, 1.00002644061987f, 1.00000924294396f,
+ 0.999999999999995f};
+
+// Curve which is scaled by |kCurveCorrectionMultipliers| and added to the curve
+// generated by the |kHighReverberationCorrectionCurve| polynomial, for
+// reverberation times which result in a feedback factor index greater than
+// |kCurveChangeoverIndex|.
+static const float kHighCorrectionCurve[kCorrectionCurveLength] = {
+ 0.00905424704513036f, 0.00926599025691877f, 0.00947778313262726f,
+ 0.00968962562016418f, 0.00990151766744984f, 0.0101134592224174f,
+ 0.0103254502330116f, 0.0105374906471899f, 0.0107495804129221f,
+ 0.0109617194781898f, 0.0111739077909878f, 0.0113861452993219f,
+ 0.0115984319512115f, 0.0118107676946871f, 0.0120231524777923f,
+ 0.0122355862485828f, 0.0124480689551263f, 0.0126606005455030f,
+ 0.0128731809678055f, 0.0130858101701389f, 0.0132984881006193f,
+ 0.0135112147073766f, 0.0137239899385524f, 0.0139368137423006f,
+ 0.0141496860667869f, 0.0143626068601902f, 0.0145755760707011f,
+ 0.0147885936465225f, 0.0150016595358697f, 0.0152147736869704f,
+ 0.0154279360480643f, 0.0156411465674036f, 0.0158544051932527f,
+ 0.0160677118738880f, 0.0162810665575990f, 0.0164944691926864f,
+ 0.0167079197274641f, 0.0169214181102577f, 0.0171349642894055f,
+ 0.0173485582132578f, 0.0175621998301772f, 0.0177758890885386f,
+ 0.0179896259367293f, 0.0182034103231487f, 0.0184172421962087f,
+ 0.0186311215043333f, 0.0188450481959587f, 0.0190590222195339f,
+ 0.0192730435235194f, 0.0194871120563886f, 0.0197012277666270f,
+ 0.0199153906027323f, 0.0201296005132146f, 0.0203438574465959f,
+ 0.0205581613514112f, 0.0207725121762073f, 0.0209869098695431f,
+ 0.0212013543799902f, 0.0214158456561324f, 0.0216303836465656f,
+ 0.0218449682998980f, 0.0220595995647505f, 0.0222742773897556f,
+ 0.0224890017235585f, 0.0227037725148169f, 0.0229185897122002f,
+ 0.0231334532643903f, 0.0233483631200817f, 0.0235633192279807f,
+ 0.0237783215368064f, 0.0239933699952896f, 0.0242084645521738f,
+ 0.0244236051562147f, 0.0246387917561803f, 0.0248540243008508f,
+ 0.0250693027390185f, 0.0252846270194886f, 0.0254999970910778f,
+ 0.0257154129026155f, 0.0259308744029435f, 0.0261463815409154f,
+ 0.0263619342653978f, 0.0265775325252690f, 0.0267931762694196f,
+ 0.0270088654467529f, 0.0272246000061842f, 0.0274403798966409f,
+ 0.0276562050670630f, 0.0278720754664027f, 0.0280879910436245f,
+ 0.0283039517477049f, 0.0285199575276330f, 0.0287360083324103f,
+ 0.0289521041110502f, 0.0291682448125785f, 0.0293844303860333f,
+ 0.0296006607804654f, 0.0298169359449371f, 0.0300332558285236f,
+ 0.0302496203803120f, 0.0304660295494019f, 0.0306824832849052f,
+ 0.0308989815359457f, 0.0311155242516602f, 0.0313321113811973f,
+ 0.0315487428737179f, 0.0317654186783949f, 0.0319821387444144f,
+ 0.0321989030209739f, 0.0324157114572834f, 0.0326325640025653f,
+ 0.0328494606060544f, 0.0330664012169974f, 0.0332833857846536f,
+ 0.0335004142582945f, 0.0337174865872039f, 0.0339346027206777f,
+ 0.0341517626080246f, 0.0343689661985646f, 0.0345862134416310f,
+ 0.0348035042865692f, 0.0350208386827363f, 0.0352382165795020f,
+ 0.0354556379262483f, 0.0356731026723698f, 0.0358906107672728f,
+ 0.0361081621603763f, 0.0363257568011115f, 0.0365433946389216f,
+ 0.0367610756232627f, 0.0369787997036024f, 0.0371965668294212f,
+ 0.0374143769502114f, 0.0376322300154781f, 0.0378501259747384f,
+ 0.0380680647775213f, 0.0382860463733691f, 0.0385040707118353f,
+ 0.0387221377424862f, 0.0389402474149004f, 0.0391583996786689f,
+ 0.0393765944833944f, 0.0395948317786927f, 0.0398131115141910f,
+ 0.0400314336395295f, 0.0402497981043605f, 0.0404682048583482f,
+ 0.0406866538511695f, 0.0409051450325135f, 0.0411236783520815f,
+ 0.0413422537595872f, 0.0415608712047565f, 0.0417795306373275f,
+ 0.0419982320070504f, 0.0422169752636886f, 0.0424357603570167f,
+ 0.0426545872368219f, 0.0428734558529040f, 0.0430923661550748f,
+ 0.0433113180931585f, 0.0435303116169914f, 0.0437493466764225f,
+ 0.0439684232213125f, 0.0441875412015349f, 0.0444067005669753f,
+ 0.0446259012675311f, 0.0448451432531127f, 0.0450644264736426f,
+ 0.0452837508790555f, 0.0455031164192982f, 0.0457225230443301f,
+ 0.0459419707041227f, 0.0461614593486596f, 0.0463809889279372f,
+ 0.0466005593919636f, 0.0468201706907599f, 0.0470398227743584f,
+ 0.0472595155928048f, 0.0474792490961565f, 0.0476990232344832f,
+ 0.0479188379578670f, 0.0481386932164022f, 0.0483585889601954f,
+ 0.0485785251393657f, 0.0487985017040441f, 0.0490185186043742f,
+ 0.0492385757905118f, 0.0494586732126247f, 0.0496788108208932f,
+ 0.0498989885655103f, 0.0501192063966806f, 0.0401356533420085f,
+ 0.0403559511969490f, 0.0405762889891309f, 0.0407966666688085f,
+ 0.0410170841862478f, 0.0412375414917278f, 0.0414580385355386f,
+ 0.0416785752679840f, 0.0418991516393793f, 0.0421197676000519f,
+ 0.0423404231003423f, 0.0425611180906021f, 0.0427818525211963f,
+ 0.0430026263425014f, 0.0432234395049069f, 0.0434442919588138f,
+ 0.0436651836546360f, 0.0438861145427996f, 0.0441070845737422f,
+ 0.0443280936979149f, 0.0445491418657804f, 0.0447702290278135f,
+ 0.0449913551345018f, 0.0452125201363448f, 0.0454337239838546f,
+ 0.0456549666275551f, 0.0458762480179831f, 0.0460975681056872f,
+ 0.0463189268412284f, 0.0465403241751802f, 0.0467617600581280f,
+ 0.0469832344406698f, 0.0472047472734157f, 0.0474262985069884f,
+ 0.0476478880920221f, 0.0478695159791643f, 0.0480911821190742f,
+ 0.0483128864624232f, 0.0485346289598951f, 0.0487564095621865f,
+ 0.0489782282200054f, 0.0492000848840726f, 0.0494219795051212f,
+ 0.0496439120338962f, 0.0498658824211554f, 0.0500878906176685f,
+ 0.0503099365742177f, 0.0505320202415973f, 0.0507541415706141f,
+ 0.0509763005120871f, 0.0511984970168471f, 0.0514207310357381f,
+ 0.0516430025196157f, 0.0518653114193479f, 0.0520876576858154f,
+ 0.0523100412699105f, 0.0525324621225380f, 0.0527549201946156f,
+ 0.0529774154370724f, 0.0531999478008504f, 0.0534225172369033f,
+ 0.0536451236961979f, 0.0538677671297125f, 0.0540904474884381f,
+ 0.0543131647233778f, 0.0545359187855472f, 0.0547587096259737f,
+ 0.0549815371956978f, 0.0552044014457715f, 0.0554273023272596f,
+ 0.0556502397912386f, 0.0558732137887982f, 0.0560962242710392f,
+ 0.0563192711890757f, 0.0565423544940334f, 0.0567654741370510f,
+ 0.0569886300692788f, 0.0572118222418796f, 0.0574350506060288f,
+ 0.0576583151129133f, 0.0578816157137332f, 0.0581049523597000f,
+ 0.0583283250020387f, 0.0585517335919851f, 0.0485713671581760f,
+ 0.0487948474970971f, 0.0490183636374095f, 0.0492419155303987f,
+ 0.0494655031273628f, 0.0496891263796120f, 0.0499127852384687f,
+ 0.0501364796552677f, 0.0503602095813560f, 0.0505839749680932f,
+ 0.0508077757668506f, 0.0510316119290123f, 0.0512554834059744f,
+ 0.0514793901491452f, 0.0517033321099457f, 0.0519273092398090f,
+ 0.0521513214901801f, 0.0523753688125170f, 0.0525994511582891f,
+ 0.0528235684789790f, 0.0530477207260808f, 0.0532719078511013f,
+ 0.0534961298055596f, 0.0537203865409869f, 0.0539446780089268f,
+ 0.0541690041609351f, 0.0543933649485798f, 0.0546177603234417f,
+ 0.0548421902371131f, 0.0550666546411992f, 0.0552911534873171f,
+ 0.0555156867270965f, 0.0557402543121789f, 0.0559648561942188f,
+ 0.0561894923248822f, 0.0564141626558481f, 0.0566388671388072f,
+ 0.0568636057254629f, 0.0570883783675304f, 0.0573131850167378f,
+ 0.0575380256248254f, 0.0577629001435447f, 0.0579878085246610f,
+ 0.0582127507199510f, 0.0584377266812042f, 0.0586627363602216f,
+ 0.0588877797088170f, 0.0591128566788167f, 0.0593379672220590f,
+ 0.0595631112903944f, 0.0597882888356857f, 0.0600134998098081f,
+ 0.0602387441646493f, 0.0604640218521088f, 0.0606893328240985f,
+ 0.0609146770325428f, 0.0611400544293785f, 0.0613654649665540f,
+ 0.0615909085960309f, 0.0618163852697822f, 0.0620418949397940f,
+ 0.0622674375580639f, 0.0624930130766024f, 0.0627186214474318f,
+ 0.0629442626225872f, 0.0631699365541155f, 0.0633956431940764f,
+ 0.0534175715719286f, 0.0536433434849815f, 0.0538691479627182f,
+ 0.0540949849572480f, 0.0543208544206912f, 0.0545467563051810f,
+ 0.0547726905628627f, 0.0549986571458940f, 0.0552246560064449f,
+ 0.0554506870966974f, 0.0556767503688461f, 0.0559028457750979f,
+ 0.0561289732676714f, 0.0563551327987983f, 0.0565813243207222f,
+ 0.0568075477856989f, 0.0570338031459965f, 0.0572600903538957f,
+ 0.0574864093616889f, 0.0577127601216814f, 0.0579391425861903f,
+ 0.0581655567075452f, 0.0583920024380880f, 0.0586184797301730f,
+ 0.0588449885361663f, 0.0590715288084468f, 0.0592981004994055f,
+ 0.0595247035614457f, 0.0597513379469827f, 0.0599780036084447f,
+ 0.0602047004982714f, 0.0604314285689156f, 0.0606581877728418f,
+ 0.0608849780625269f, 0.0611117993904601f, 0.0613386517091431f,
+ 0.0615655349710893f, 0.0617924491288254f, 0.0620193941348894f,
+ 0.0622463699418319f, 0.0624733765022160f, 0.0627004137686168f,
+ 0.0629274816936218f, 0.0631545802298307f, 0.0633817093298557f,
+ 0.0636088689463210f, 0.0638360590318633f, 0.0640632795391316f,
+ 0.0642905304207867f, 0.0645178116295023f, 0.0647451231179644f,
+ 0.0649724648388708f, 0.0651998367449316f, 0.0654272387888696f,
+ 0.0656546709234196f, 0.0658821331013287f, 0.0661096252753566f,
+ 0.0663371473982747f, 0.0665646994228671f, 0.0667922813019303f,
+ 0.0568160820656600f, 0.0570437235121022f, 0.0572713946714774f,
+ 0.0574990954966311f, 0.0577268259404211f, 0.0579545859557173f,
+ 0.0581823754954017f, 0.0584101945123692f, 0.0586380429595264f,
+ 0.0588659207897921f, 0.0590938279560984f, 0.0593217644113885f,
+ 0.0595497301086181f, 0.0597777250007558f, 0.0600057490407822f,
+ 0.0602338021816895f, 0.0604618843764834f, 0.0606899955781809f,
+ 0.0609181357398114f, 0.0611463048144174f, 0.0613745027550527f,
+ 0.0616027295147840f, 0.0618309850466897f, 0.0620592693038611f,
+ 0.0622875822394012f, 0.0625159238064261f, 0.0627442939580634f,
+ 0.0629726926474533f, 0.0632011198277480f, 0.0634295754521126f,
+ 0.0636580594737237f, 0.0638865718457710f, 0.0641151125214557f,
+ 0.0643436814539919f, 0.0645722785966058f, 0.0648009039025354f,
+ 0.0650295573250316f, 0.0652582388173575f, 0.0654869483327883f,
+ 0.0657156858246115f, 0.0659444512461268f, 0.0661732445506464f,
+ 0.0664020656914949f, 0.0666309146220087f, 0.0668597912955365f,
+ 0.0670886956654401f, 0.0673176276850925f, 0.0675465873078798f,
+ 0.0677755744872001f, 0.0680045891764636f, 0.0682336313290928f,
+ 0.0684627008985227f, 0.0686917978382008f, 0.0689209221015861f,
+ 0.0691500736421506f, 0.0693792524133784f, 0.0594046474461534f,
+ 0.0596338805392087f, 0.0598631407234532f, 0.0600924279524199f,
+ 0.0603217421796543f, 0.0605510833587138f, 0.0607804514431689f,
+ 0.0610098463866017f, 0.0612392681426066f, 0.0614687166647906f,
+ 0.0616981919067730f, 0.0619276938221849f, 0.0621572223646700f,
+ 0.0623867774878845f, 0.0626163591454967f, 0.0628459672911870f,
+ 0.0630756018786481f, 0.0633052628615854f, 0.0635349501937161f,
+ 0.0637646638287699f, 0.0639944037204887f, 0.0642241698226267f,
+ 0.0644539620889507f, 0.0646837804732392f, 0.0649136249292833f,
+ 0.0651434954108865f, 0.0653733918718643f, 0.0656033142660447f,
+ 0.0658332625472679f, 0.0660632366693863f, 0.0662932365862647f,
+ 0.0665232622517804f, 0.0667533136198224f, 0.0669833906442924f,
+ 0.0672134932791041f, 0.0674436214781841f, 0.0676737751954706f,
+ 0.0679039543849144f, 0.0681341590004783f, 0.0683643889961381f,
+ 0.0685946443258808f, 0.0688249249437064f, 0.0690552308036274f,
+ 0.0692855618596678f, 0.0695159180658648f, 0.0697462993762669f,
+ 0.0699767057449354f, 0.0702071371259442f, 0.0704375934733789f,
+ 0.0706680747413375f, 0.0708985808839306f, 0.0711291118552808f,
+ 0.0713596676095232f, 0.0715902481008047f, 0.0616170423606727f,
+ 0.0618476721885236f, 0.0620783266159294f, 0.0623090055970863f,
+ 0.0625397090862030f, 0.0627704370375001f, 0.0630011894052112f,
+ 0.0632319661435816f, 0.0634627672068692f, 0.0636935925493440f,
+ 0.0639244421252883f, 0.0641553158889967f, 0.0643862137947761f,
+ 0.0646171357969457f, 0.0648480818498370f, 0.0650790519077937f,
+ 0.0653100459251719f, 0.0655410638563397f, 0.0657721056556779f,
+ 0.0660031712775794f, 0.0662342606764491f, 0.0664653738067047f,
+ 0.0666965106227758f, 0.0669276710791043f, 0.0671588551301446f,
+ 0.0673900627303632f, 0.0676212938342389f, 0.0678525483962630f,
+ 0.0680838263709389f, 0.0683151277127820f, 0.0685464523763204f,
+ 0.0687778003160946f, 0.0690091714866568f, 0.0692405658425719f,
+ 0.0694719833384170f, 0.0697034239287815f, 0.0699348875682669f,
+ 0.0701663742114875f, 0.0703978838130692f, 0.0706294163276505f,
+ 0.0708609717098825f, 0.0710925499144278f, 0.0713241508959620f,
+ 0.0715557746091728f, 0.0717874210087600f, 0.0720190900494359f,
+ 0.0722507816859246f, 0.0724824958729632f, 0.0727142325653007f,
+ 0.0729459917176984f, 0.0731777732849299f, 0.0734095772217809f,
+ 0.0736414034830499f, 0.0738732520235469f, 0.0741051227980950f,
+ 0.0641332048389163f, 0.0643651199460837f, 0.0645970571518437f,
+ 0.0648290164110684f, 0.0650609976786417f, 0.0652930009094604f,
+ 0.0655250260584330f, 0.0657570730804803f, 0.0659891419305358f,
+ 0.0662212325635450f, 0.0664533449344658f, 0.0666854789982679f,
+ 0.0669176347099340f, 0.0671498120244587f, 0.0673820108968489f,
+ 0.0676142312821242f, 0.0678464731353154f, 0.0680787364114669f,
+ 0.0683110210656344f, 0.0685433270528865f, 0.0687756543283037f,
+ 0.0690080028469791f, 0.0692403725640174f, 0.0694727634345366f,
+ 0.0697051754136662f, 0.0699376084565483f, 0.0701700625183374f,
+ 0.0704025375541997f, 0.0706350335193146f, 0.0708675503688727f,
+ 0.0711000880580778f, 0.0713326465421456f, 0.0715652257763042f,
+ 0.0717978257157935f, 0.0720304463158665f, 0.0722630875317879f,
+ 0.0724957493188349f, 0.0727284316322967f, 0.0729611344274751f,
+ 0.0731938576596843f, 0.0734266012842503f, 0.0736593652565118f,
+ 0.0738921495318196f, 0.0741249540655367f, 0.0743577788130385f,
+ 0.0745906237297128f, 0.0748234887709595f, 0.0750563738921907f,
+ 0.0752892790488311f, 0.0755222041963173f, 0.0757551492900986f,
+ 0.0759881142856362f, 0.0762210991384039f, 0.0764541038038875f,
+ 0.0766871282375853f, 0.0769201723950076f, 0.0669494253090649f,
+ 0.0671825087805169f, 0.0674156118422988f, 0.0676487344499700f,
+ 0.0678818765591026f, 0.0681150381252804f, 0.0683482191041003f,
+ 0.0685814194511707f, 0.0688146391221127f, 0.0690478780725596f,
+ 0.0692811362581574f, 0.0695144136345632f, 0.0697477101574477f,
+ 0.0699810257824931f, 0.0702143604653941f, 0.0704477141618579f,
+ 0.0706810868276036f, 0.0709144784183627f, 0.0711478888898790f,
+ 0.0713813181979088f, 0.0716147662982205f, 0.0718482331465947f,
+ 0.0720817186988244f, 0.0723152229107147f, 0.0725487457380832f,
+ 0.0727822871367598f, 0.0730158470625864f, 0.0732494254714176f,
+ 0.0734830223191197f, 0.0737166375615722f, 0.0739502711546658f,
+ 0.0741839230543041f, 0.0744175932164030f, 0.0746512815968905f,
+ 0.0748849881517070f, 0.0751187128368048f, 0.0753524556081493f,
+ 0.0755862164217173f, 0.0758199952334984f, 0.0760537919994943f,
+ 0.0762876066757191f, 0.0765214392181990f, 0.0767552895829728f,
+ 0.0769891577260911f, 0.0772230436036172f, 0.0774569471716267f,
+ 0.0776908683862069f, 0.0779248072034582f, 0.0781587635794926f,
+ 0.0783927374704347f, 0.0786267288324215f, 0.0788607376216020f,
+ 0.0790947637941377f, 0.0793288073062022f, 0.0795628681139814f,
+ 0.0797969461736736f, 0.0800310414414898f, 0.0802651538736520f,
+ 0.0804992834263956f, 0.0807334300559682f, 0.0707637827960168f,
+ 0.0709979634480382f, 0.0712321610457045f, 0.0714663755453118f,
+ 0.0717006069031690f, 0.0719348550755973f, 0.0721691200189298f,
+ 0.0724034016895124f, 0.0726377000437030f, 0.0728720150378716f,
+ 0.0731063466284005f, 0.0733406947716849f, 0.0735750594241317f,
+ 0.0738094405421600f, 0.0740438380822017f, 0.0742782520007005f,
+ 0.0745126822541126f, 0.0747471287989061f, 0.0749815915915625f,
+ 0.0752160705885742f, 0.0754505657464466f, 0.0756850770216972f,
+ 0.0759196043708562f, 0.0761541477504655f, 0.0763887071170792f,
+ 0.0766232824272645f, 0.0768578736376001f, 0.0770924807046775f,
+ 0.0773271035850998f, 0.0775617422354831f, 0.0777963966124555f,
+ 0.0780310666726576f, 0.0782657523727417f, 0.0785004536693729f,
+ 0.0787351705192282f, 0.0789699028789975f, 0.0792046507053824f,
+ 0.0794394139550970f, 0.0796741925848675f, 0.0799089865514328f,
+ 0.0801437958115436f, 0.0803786203219632f, 0.0806134600394671f,
+ 0.0808483149208430f, 0.0810831849228909f, 0.0813180700024232f,
+ 0.0815529701162645f, 0.0817878852212516f, 0.0820228152742338f,
+ 0.0822577602320724f, 0.0824927200516413f, 0.0827276946898264f,
+ 0.0829626841035263f, 0.0831976882496509f, 0.0834327070851236f,
+ 0.0836677405668795f, 0.0839027886518659f, 0.0841378512970423f,
+ 0.0843729284593812f, 0.0846080200958663f, 0.0848431261634945f,
+ 0.0850782466192747f, 0.0853133814202278f, 0.0855485305233871f,
+ 0.0857836938857984f, 0.0860188714645199f, 0.0862540632166216f,
+ 0.0762854581765733f, 0.0765206781466951f, 0.0767559121614814f,
+ 0.0769911601780519f, 0.0772264221535377f, 0.0774616980450831f,
+ 0.0776969878098444f, 0.0779322914049897f, 0.0781676087877001f,
+ 0.0784029399151685f, 0.0786382847446002f, 0.0788736432332130f,
+ 0.0791090153382364f, 0.0793444010169132f, 0.0795798002264975f,
+ 0.0798152129242558f, 0.0800506390674675f, 0.0802860786134237f,
+ 0.0805215315194281f, 0.0807569977427964f, 0.0809924772408571f,
+ 0.0812279699709503f, 0.0814634758904288f, 0.0816989949566574f,
+ 0.0819345271270137f, 0.0821700723588872f, 0.0824056306096794f,
+ 0.0826412018368049f, 0.0828767859976898f, 0.0831123830497728f,
+ 0.0833479929505050f, 0.0835836156573494f, 0.0838192511277818f,
+ 0.0840548993192899f, 0.0842905601893735f, 0.0845262336955456f,
+ 0.0847619197953302f, 0.0849976184462647f, 0.0852333296058980f,
+ 0.0854690532317919f, 0.0857047892815197f, 0.0859405377126680f,
+ 0.0861762984828349f, 0.0864120715496310f, 0.0866478568706792f,
+ 0.0868836544036147f, 0.0871194641060850f, 0.0873552859357498f,
+ 0.0875911198502811f, 0.0878269658073632f, 0.0880628237646929f,
+ 0.0882986936799788f, 0.0885345755109422f, 0.0887704692153166f,
+ 0.0890063747508475f, 0.0892422920752930f, 0.0894782211464236f,
+ 0.0897141619220215f, 0.0899501143598818f, 0.0901860784178115f,
+ 0.0904220540536300f, 0.0906580412251691f, 0.0908940398902726f,
+ 0.0911300500067969f, 0.0913660715326105f, 0.0916021044255941f,
+ 0.0918381486436412f, 0.0920742041446566f, 0.0923102708865582f,
+ 0.0925463488272760f, 0.0927824379247522f, 0.0930185381369413f,
+ 0.0932546494218102f, 0.0934907717373377f, 0.0937269050415152f,
+ 0.0939630492923465f, 0.0941992044478474f, 0.0944353704660461f,
+ 0.0946715473049831f, 0.0949077349227112f, 0.0951439332772953f,
+ 0.0953801423268129f, 0.0956163620293533f, 0.0856487814204062f,
+ 0.0858850223033106f, 0.0861212737135806f, 0.0863575356093548f,
+ 0.0865938079487840f, 0.0868300906900319f, 0.0870663837912740f,
+ 0.0873026872106978f, 0.0875390009065038f, 0.0877753248369042f,
+ 0.0880116589601236f, 0.0882480032343993f, 0.0884843576179805f,
+ 0.0887207220691284f, 0.0889570965461172f, 0.0891934810072328f,
+ 0.0894298754107737f, 0.0896662797150505f, 0.0899026938783861f,
+ 0.0901391178591160f, 0.0903755516155873f, 0.0906119951061600f,
+ 0.0908484482892064f, 0.0910849111231104f, 0.0913213835662688f,
+ 0.0915578655770907f, 0.0917943571139971f, 0.0920308581354216f,
+ 0.0922673685998101f, 0.0925038884656203f, 0.0927404176913226f,
+ 0.0929769562353998f, 0.0932135040563467f, 0.0934500611126704f,
+ 0.0936866273628904f, 0.0939232027655385f, 0.0941597872791585f,
+ 0.0943963808623070f, 0.0946329834735524f, 0.0948695950714755f,
+ 0.0951062156146697f, 0.0953428450617401f, 0.0955794833713046f,
+ 0.0958161305019931f, 0.0960527864124481f, 0.0962894510613237f,
+ 0.0965261244072870f, 0.0967628064090171f, 0.0969994970252055f,
+ 0.0972361962145558f, 0.0974729039357838f, 0.0977096201476179f,
+ 0.0979463448087987f, 0.0981830778780787f, 0.0984198193142229f,
+ 0.0986565690760093f, 0.0988933271222270f, 0.0991300934116780f,
+ 0.0993668679031768f, 0.0996036505555495f, 0.0998404413276351f,
+ 0.100077240178285f, 0.100314047066361f, 0.100550861950741f,
+ 0.100787684790311f, 0.101024515543973f, 0.101261354170637f,
+ 0.101498200629230f, 0.101735054878688f, 0.101971916877960f,
+ 0.102208786586009f, 0.102445663961807f, 0.102682548964341f,
+ 0.102919441552610f, 0.103156341685624f, 0.103393249322406f,
+ 0.103630164421992f, 0.103867086943429f, 0.104104016845776f,
+ 0.104340954088107f, 0.104577898629505f, 0.104814850429067f,
+ 0.105051809445902f, 0.105288775639131f, 0.105525748967888f,
+ 0.105762729391318f, 0.105999716868580f, 0.106236711358845f,
+ 0.106473712821294f, 0.106710721215122f, 0.106947736499538f,
+ 0.107184758633760f, 0.107421787577020f, 0.107658823288562f,
+ 0.107895865727643f, 0.108132914853530f, 0.108369970625506f,
+ 0.108607033002864f, 0.108844101944907f, 0.109081177410956f,
+ 0.109318259360339f, 0.109555347752398f, 0.109792442546489f,
+ 0.110029543701978f, 0.110266651178245f, 0.110503764934681f,
+ 0.110740884930689f, 0.110978011125686f, 0.111215143479099f,
+ 0.111452281950370f, 0.111689426498952f, 0.111926577084308f,
+ 0.112163733665917f, 0.112400896203270f, 0.112638064655866f,
+ 0.112875238983221f, 0.113112419144862f, 0.113349605100326f,
+ 0.113586796809166f, 0.113823994230944f, 0.114061197325236f,
+ 0.114298406051631f, 0.114535620369728f, 0.114772840239140f,
+ 0.115010065619491f, 0.115247296470419f, 0.115484532751574f,
+ 0.115721774422616f, 0.115959021443219f, 0.116196273773071f,
+ 0.116433531371868f, 0.116670794199323f, 0.116908062215158f,
+ 0.117145335379108f, 0.117382613650922f, 0.117619896990358f,
+ 0.117857185357189f, 0.118094478711200f, 0.118331777012186f,
+ 0.118569080219959f, 0.118806388294337f, 0.119043701195156f,
+ 0.119281018882261f, 0.119518341315510f, 0.119755668454773f,
+ 0.119993000259933f, 0.120230336690886f, 0.120467677707538f,
+ 0.120705023269809f, 0.120942373337631f, 0.121179727870948f,
+ 0.121417086829716f, 0.121654450173904f, 0.121891817863493f,
+ 0.122129189858476f, 0.122366566118858f, 0.122603946604659f,
+ 0.122841331275906f, 0.123078720092644f, 0.123316113014926f,
+ 0.123553510002819f, 0.123790911016403f, 0.124028316015769f,
+ 0.124265724961022f, 0.124503137812276f, 0.124740554529660f,
+ 0.124977975073316f, 0.125215399403394f, 0.125452827480062f,
+ 0.125690259263497f, 0.125927694713887f, 0.126165133791435f,
+ 0.126402576456356f, 0.126640022668876f, 0.126877472389234f,
+ 0.127114925577681f, 0.127352382194480f, 0.127589842199908f,
+ 0.127827305554251f, 0.128064772217811f, 0.128302242150900f,
+ 0.128539715313843f, 0.118573380744364f, 0.118810860248038f,
+ 0.119048342862613f, 0.119285828548465f, 0.119523317265979f,
+ 0.119760808975554f, 0.119998303637600f, 0.120235801212540f,
+ 0.120473301660811f, 0.120710804942859f, 0.120948311019145f,
+ 0.121185819850140f, 0.121423331396330f, 0.121660845618210f,
+ 0.121898362476291f, 0.122135881931093f, 0.122373403943149f,
+ 0.122610928473007f, 0.122848455481224f, 0.123085984928370f,
+ 0.123323516775029f, 0.123561050981795f, 0.123798587509275f,
+ 0.124036126318090f, 0.124273667368871f, 0.124511210622262f,
+ 0.124748756038919f, 0.124986303579512f, 0.125223853204721f,
+ 0.125461404875240f, 0.125698958551774f, 0.125936514195041f,
+ 0.126174071765771f, 0.126411631224707f, 0.126649192532603f,
+ 0.126886755650226f, 0.127124320538356f, 0.127361887157783f,
+ 0.127599455469312f, 0.127837025433760f, 0.128074597011953f,
+ 0.128312170164733f, 0.128549744852953f, 0.128787321037478f,
+ 0.129024898679186f, 0.129262477738966f, 0.129500058177720f,
+ 0.129737639956363f, 0.129975223035822f, 0.130212807377034f,
+ 0.130450392940952f, 0.130687979688539f, 0.130925567580771f,
+ 0.131163156578635f, 0.131400746643133f, 0.131638337735276f,
+ 0.131875929816089f, 0.132113522846610f, 0.132351116787888f,
+ 0.132588711600985f, 0.132826307246975f, 0.133063903686944f,
+ 0.133301500881990f, 0.133539098793225f, 0.133776697381772f,
+ 0.134014296608765f, 0.134251896435354f, 0.134489496822697f,
+ 0.134727097731967f, 0.134964699124348f, 0.135202300961038f,
+ 0.135439903203245f, 0.135677505812191f, 0.135915108749109f,
+ 0.136152711975246f, 0.136390315451860f, 0.136627919140220f,
+ 0.136865523001611f, 0.137103126997326f, 0.137340731088674f,
+ 0.137578335236974f, 0.137815939403557f, 0.138053543549769f,
+ 0.138291147636965f, 0.138528751626515f, 0.138766355479799f,
+ 0.139003959158210f, 0.139241562623155f, 0.139479165836051f,
+ 0.139716768758329f, 0.139954371351430f, 0.140191973576809f,
+ 0.140429575395934f, 0.140667176770284f, 0.140904777661350f,
+ 0.141142378030637f, 0.141379977839660f, 0.141617577049948f,
+ 0.141855175623041f, 0.142092773520492f, 0.142330370703868f,
+ 0.142567967134744f, 0.142805562774712f, 0.143043157585373f,
+ 0.143280751528341f, 0.143518344565244f, 0.143755936657720f,
+ 0.143993527767421f, 0.144231117856009f, 0.144468706885161f,
+ 0.144706294816565f, 0.144943881611921f, 0.145181467232942f,
+ 0.145419051641353f, 0.145656634798891f, 0.145894216667305f,
+ 0.146131797208357f, 0.146369376383822f, 0.146606954155485f,
+ 0.146844530485146f, 0.147082105334614f, 0.147319678665714f,
+ 0.147557250440280f, 0.147794820620161f, 0.148032389167217f,
+ 0.148269956043319f, 0.148507521210353f, 0.148745084630214f,
+ 0.148982646264813f, 0.149220206076071f, 0.149457764025922f,
+ 0.149695320076311f, 0.149932874189196f, 0.150170426326549f,
+ 0.150407976450352f, 0.150645524522601f, 0.150883070505302f,
+ 0.151120614360475f, 0.151358156050152f, 0.151595695536379f,
+ 0.151833232781210f, 0.152070767746715f, 0.152308300394975f,
+ 0.152545830688083f, 0.152783358588146f, 0.153020884057280f,
+ 0.153258407057616f, 0.153495927551297f, 0.153733445500477f,
+ 0.153970960867323f, 0.154208473614016f, 0.154445983702745f,
+ 0.154683491095716f, 0.154920995755145f, 0.155158497643259f,
+ 0.155395996722300f, 0.155633492954521f, 0.155870986302186f,
+ 0.156108476727575f, 0.156345964192975f, 0.156583448660691f,
+ 0.156820930093035f, 0.157058408452335f, 0.157295883700930f,
+ 0.157533355801171f, 0.147567013792809f, 0.147804479483445f,
+ 0.148041941912855f, 0.148279401043438f, 0.148516856837608f,
+ 0.148754309257790f, 0.148991758266421f, 0.149229203825950f,
+ 0.149466645898839f, 0.149704084447562f, 0.149941519434606f,
+ 0.150178950822470f, 0.150416378573664f, 0.150653802650711f,
+ 0.150891223016147f, 0.151128639632520f, 0.151366052462390f,
+ 0.151603461468329f, 0.151840866612921f, 0.152078267858765f,
+ 0.152315665168468f, 0.152553058504653f, 0.152790447829953f,
+ 0.153027833107014f, 0.153265214298494f, 0.153502591367065f,
+ 0.153739964275408f, 0.153977332986220f, 0.154214697462207f,
+ 0.154452057666090f, 0.154689413560599f, 0.154926765108481f,
+ 0.155164112272490f, 0.155401455015396f, 0.155638793299981f,
+ 0.155876127089037f, 0.156113456345370f, 0.156350781031798f,
+ 0.156588101111152f, 0.156825416546274f, 0.157062727300019f,
+ 0.157300033335253f, 0.157537334614857f, 0.157774631101722f,
+ 0.158011922758752f, 0.158249209548862f, 0.158486491434983f,
+ 0.158723768380054f, 0.158961040347028f, 0.159198307298871f,
+ 0.159435569198561f, 0.159672826009087f, 0.159910077693452f,
+ 0.160147324214669f, 0.160384565535767f, 0.160621801619783f,
+ 0.160859032429770f, 0.161096257928790f, 0.161333478079920f,
+ 0.161570692846248f, 0.161807902190875f, 0.162045106076912f,
+ 0.162282304467485f, 0.162519497325732f, 0.162756684614802f,
+ 0.162993866297856f, 0.153027231415457f, 0.153264401776015f,
+ 0.153501566420118f, 0.153738725310976f, 0.153975878411812f,
+ 0.154213025685861f, 0.154450167096373f, 0.154687302606606f,
+ 0.154924432179834f, 0.155161555779340f, 0.155398673368422f,
+ 0.155635784910389f, 0.155872890368562f, 0.156109989706276f,
+ 0.156347082886876f, 0.156584169873721f, 0.156821250630182f,
+ 0.157058325119641f, 0.157295393305493f, 0.157532455151146f,
+ 0.157769510620020f, 0.158006559675547f, 0.158243602281171f,
+ 0.158480638400349f, 0.158717667996550f, 0.158954691033254f,
+ 0.159191707473956f, 0.159428717282161f, 0.159665720421387f,
+ 0.159902716855164f, 0.160139706547035f, 0.160376689460556f,
+ 0.160613665559292f, 0.160850634806823f, 0.161087597166742f,
+ 0.161324552602651f, 0.161561501078168f, 0.161798442556921f,
+ 0.162035377002551f, 0.162272304378710f, 0.162509224649065f,
+ 0.162746137777293f, 0.162983043727084f, 0.163219942462139f,
+ 0.163456833946175f, 0.163693718142917f, 0.163930595016105f,
+ 0.164167464529489f, 0.164404326646834f, 0.164641181331916f,
+ 0.164878028548522f, 0.165114868260454f, 0.165351700431524f,
+ 0.155384714102944f, 0.155621531083777f, 0.155858340415260f,
+ 0.156095142061255f, 0.156331935985636f, 0.156568722152290f,
+ 0.156805500525115f, 0.157042271068022f, 0.157279033744935f,
+ 0.157515788519790f, 0.157752535356533f, 0.157989274219126f,
+ 0.158226005071540f, 0.158462727877761f, 0.158699442601785f,
+ 0.158936149207623f, 0.159172847659294f, 0.159409537920833f,
+ 0.159646219956287f, 0.159882893729713f, 0.160119559205183f,
+ 0.160356216346779f, 0.160592865118596f, 0.160829505484743f,
+ 0.161066137409338f, 0.161302760856514f, 0.161539375790416f,
+ 0.161775982175200f, 0.162012579975035f, 0.162249169154102f,
+ 0.162485749676594f, 0.162722321506719f, 0.162958884608692f,
+ 0.163195438946746f, 0.163431984485122f, 0.163668521188075f,
+ 0.163905049019873f, 0.164141567944796f, 0.164378077927134f,
+ 0.164614578931192f, 0.164851070921286f, 0.165087553861745f,
+ 0.165324027716909f, 0.165560492451133f, 0.165796948028780f,
+ 0.166033394414230f, 0.156066020649259f, 0.156302448543495f,
+ 0.156538867138740f, 0.156775276399421f, 0.157011676289976f,
+ 0.157248066774858f, 0.157484447818530f, 0.157720819385467f,
+ 0.157957181440159f, 0.158193533947106f, 0.158429876870820f,
+ 0.158666210175827f, 0.158902533826663f, 0.159138847787880f,
+ 0.159375152024038f, 0.159611446499712f, 0.159847731179489f,
+ 0.160084006027966f, 0.160320271009756f, 0.160556526089482f,
+ 0.160792771231779f, 0.161029006401295f, 0.161265231562690f,
+ 0.161501446680637f, 0.161737651719821f, 0.161973846644939f,
+ 0.162210031420699f, 0.162446206011824f, 0.162682370383048f,
+ 0.162918524499116f, 0.163154668324787f, 0.163390801824832f,
+ 0.163626924964034f, 0.163863037707188f, 0.164099140019102f,
+ 0.164335231864596f, 0.164571313208501f, 0.164807384015662f,
+ 0.165043444250936f, 0.165279493879192f, 0.165515532865311f,
+ 0.165751561174187f, 0.155783767848112f, 0.156019774697231f,
+ 0.156255770763861f, 0.156491756012944f, 0.156727730409436f,
+ 0.156963693918304f, 0.157199646504527f, 0.157435588133098f,
+ 0.157671518769019f, 0.157907438377308f, 0.158143346922994f,
+ 0.158379244371116f, 0.158615130686729f, 0.158851005834898f,
+ 0.159086869780700f, 0.159322722489227f, 0.159558563925579f,
+ 0.159794394054872f, 0.160030212842232f, 0.160266020252799f,
+ 0.160501816251724f, 0.160737600804171f, 0.160973373875317f,
+ 0.161209135430348f, 0.161444885434466f, 0.161680623852884f,
+ 0.161916350650826f, 0.162152065793531f, 0.162387769246248f,
+ 0.162623460974239f, 0.162859140942778f, 0.163094809117152f,
+ 0.163330465462660f, 0.163566109944612f, 0.163801742528333f,
+ 0.164037363179157f, 0.164272971862433f, 0.164508568543521f,
+ 0.164744153187794f, 0.154775914838023f, 0.155011475304832f,
+ 0.155247023631016f, 0.155482559781998f, 0.155718083723212f,
+ 0.155953595420103f, 0.156189094838130f, 0.156424581942764f,
+ 0.156660056699488f, 0.156895519073797f, 0.157130969031199f,
+ 0.157366406537214f, 0.157601831557375f, 0.157837244057224f,
+ 0.158072644002320f, 0.158308031358231f, 0.158543406090539f,
+ 0.158778768164837f, 0.159014117546731f, 0.159249454201839f,
+ 0.159484778095791f, 0.159720089194232f, 0.159955387462814f,
+ 0.160190672867206f, 0.160425945373088f, 0.160661204946150f,
+ 0.160896451552098f, 0.161131685156647f, 0.161366905725526f,
+ 0.161602113224477f, 0.161837307619252f, 0.162072488875617f,
+ 0.162307656959350f, 0.162542811836242f, 0.162777953472093f,
+ 0.163013081832719f, 0.163248196883948f, 0.163483298591617f,
+ 0.153514575998966f, 0.153749650917085f, 0.153984712389236f,
+ 0.154219760381308f, 0.154454794859201f, 0.154689815788829f,
+ 0.154924823136117f, 0.155159816867001f, 0.155394796947432f,
+ 0.155629763343373f, 0.155864716020796f, 0.156099654945689f,
+ 0.156334580084050f, 0.156569491401892f, 0.156804388865237f,
+ 0.157039272440121f, 0.157274142092592f, 0.157508997788710f,
+ 0.157743839494548f, 0.157978667176190f, 0.158213480799735f,
+ 0.158448280331291f, 0.158683065736980f, 0.158917836982936f,
+ 0.159152594035305f, 0.159387336860246f, 0.159622065423929f,
+ 0.159856779692538f, 0.160091479632268f, 0.160326165209327f,
+ 0.160560836389934f, 0.160795493140323f, 0.161030135426737f,
+ 0.161264763215433f, 0.161499376472680f, 0.161733975164760f,
+ 0.161968559257967f, 0.151999317795992f, 0.152233872590381f,
+ 0.152468412684851f, 0.152702938045745f, 0.152937448639417f,
+ 0.153171944432235f, 0.153406425390579f, 0.153640891480840f,
+ 0.153875342669423f, 0.154109778922744f, 0.154344200207233f,
+ 0.154578606489328f, 0.154812997735486f, 0.155047373912170f,
+ 0.155281734985858f, 0.155516080923042f, 0.155750411690222f,
+ 0.155984727253915f, 0.156219027580646f, 0.156453312636956f,
+ 0.156687582389395f, 0.156921836804528f, 0.157156075848931f,
+ 0.157390299489191f, 0.157624507691911f, 0.157858700423702f,
+ 0.158092877651190f, 0.158327039341012f, 0.158561185459819f,
+ 0.158795315974272f, 0.159029430851045f, 0.159263530056826f,
+ 0.159497613558314f, 0.159731681322219f, 0.159965733315264f,
+ 0.160199769504187f, 0.150229978933122f, 0.150463983414054f,
+ 0.150697971991144f, 0.150931944631177f, 0.151165901300950f,
+ 0.151399841967272f, 0.151633766596964f, 0.151867675156863f,
+ 0.152101567613812f, 0.152335443934671f, 0.152569304086311f,
+ 0.152803148035615f, 0.153036975749479f, 0.153270787194809f,
+ 0.153504582338527f, 0.153738361147564f, 0.153972123588865f,
+ 0.154205869629387f, 0.154439599236099f, 0.154673312375982f,
+ 0.154907009016030f, 0.155140689123249f, 0.155374352664657f,
+ 0.155607999607285f, 0.155841629918175f, 0.156075243564383f,
+ 0.156308840512976f, 0.156542420731034f, 0.156775984185648f,
+ 0.157009530843923f, 0.157243060672975f, 0.157476573639934f,
+ 0.157710069711939f, 0.157943548856145f, 0.158177011039717f,
+ 0.158410456229833f, 0.158643884393684f, 0.148673484575858f,
+ 0.148906878588797f, 0.149140255477114f, 0.149373615208048f,
+ 0.149606957748852f, 0.149840283066790f, 0.150073591129136f,
+ 0.150306881903181f, 0.150540155356224f, 0.150773411455578f,
+ 0.151006650168570f, 0.151239871462536f, 0.151473075304826f,
+ 0.151706261662802f, 0.151939430503840f, 0.152172581795324f,
+ 0.152405715504656f, 0.152638831599245f, 0.152871930046515f,
+ 0.153105010813902f, 0.153338073868854f, 0.153571119178832f,
+ 0.153804146711309f, 0.154037156433768f, 0.154270148313708f,
+ 0.154503122318637f, 0.154736078416078f, 0.154969016573565f,
+ 0.155201936758644f, 0.155434838938874f, 0.155667723081826f,
+ 0.155900589155082f, 0.156133437126239f, 0.156366266962903f,
+ 0.156599078632696f, 0.156831872103249f, 0.157064647342207f,
+ 0.147093593394615f, 0.147326332073366f, 0.147559052423530f,
+ 0.147791754412800f, 0.148024438008882f, 0.148257103179495f,
+ 0.148489749892369f, 0.148722378115246f, 0.148954987815883f,
+ 0.149187578962047f, 0.149420151521517f, 0.149652705462085f,
+ 0.149885240751555f, 0.150117757357745f, 0.150350255248482f,
+ 0.150582734391608f, 0.150815194754977f, 0.151047636306454f,
+ 0.151280059013917f, 0.151512462845255f, 0.151744847768373f,
+ 0.151977213751184f, 0.152209560761616f, 0.152441888767607f,
+ 0.152674197737110f, 0.152906487638089f, 0.153138758438519f,
+ 0.153371010106390f, 0.153603242609701f, 0.153835455916467f,
+ 0.154067649994712f, 0.154299824812474f, 0.154531980337803f,
+ 0.154764116538760f, 0.154996233383421f, 0.155228330839873f,
+ 0.155460408876213f, 0.145488656537942f, 0.145720695638407f,
+ 0.145952715223132f, 0.146184715260266f, 0.146416695717967f,
+ 0.146648656564411f, 0.146880597767780f, 0.147112519296273f,
+ 0.147344421118099f, 0.147576303201479f, 0.147808165514649f,
+ 0.148040008025854f, 0.148271830703353f, 0.148503633515418f,
+ 0.148735416430330f, 0.148967179416387f, 0.149198922441895f,
+ 0.149430645475174f, 0.149662348484558f, 0.149894031438390f,
+ 0.150125694305028f, 0.150357337052841f, 0.150588959650210f,
+ 0.150820562065529f, 0.151052144267205f, 0.151283706223655f,
+ 0.151515247903310f, 0.151746769274613f, 0.151978270306019f,
+ 0.152209750965996f, 0.152441211223024f, 0.152672651045593f,
+ 0.152904070402210f, 0.153135469261390f, 0.153366847591662f,
+ 0.153598205361567f, 0.153829542539659f, 0.154060859094504f,
+ 0.154292154994679f, 0.154523430208775f, 0.144550873782782f,
+ 0.144782107530540f, 0.145013320498062f, 0.145244512653990f,
+ 0.145475683966974f, 0.145706834405678f, 0.145937963938779f,
+ 0.146169072534965f, 0.146400160162936f, 0.146631226791407f,
+ 0.146862272389103f, 0.147093296924760f, 0.147324300367130f,
+ 0.147555282684973f, 0.147786243847065f, 0.148017183822193f,
+ 0.148248102579156f, 0.148479000086764f, 0.148709876313842f,
+ 0.148940731229225f, 0.149171564801762f, 0.149402377000313f,
+ 0.149633167793751f, 0.149863937150961f, 0.150094685040840f,
+ 0.150325411432298f, 0.150556116294257f, 0.150786799595650f,
+ 0.151017461305425f, 0.151248101392539f, 0.151478719825965f,
+ 0.151709316574685f, 0.151939891607695f, 0.152170444894002f,
+ 0.152400976402628f, 0.152631486102603f, 0.152861973962974f,
+ 0.153092439952797f, 0.153322884041140f, 0.153553306197087f,
+ 0.153783706389730f, 0.154014084588175f, 0.144040629838929f,
+ 0.144270963956348f, 0.144501275986961f, 0.144731565899924f,
+ 0.144961833664404f, 0.145192079249581f, 0.145422302624648f,
+ 0.145652503758808f, 0.145882682621278f, 0.146112839181288f,
+ 0.146342973408078f, 0.146573085270901f, 0.146803174739024f,
+ 0.147033241781725f, 0.147263286368294f, 0.147493308468033f,
+ 0.147723308050258f, 0.147953285084295f, 0.148183239539485f,
+ 0.148413171385178f, 0.148643080590738f, 0.148872967125543f,
+ 0.149102830958981f, 0.149332672060452f, 0.149562490399370f,
+ 0.149792285945159f, 0.150022058667259f, 0.150251808535119f,
+ 0.150481535518200f, 0.150711239585978f, 0.150940920707939f,
+ 0.151170578853583f, 0.151400213992420f, 0.151629826093976f,
+ 0.151859415127784f, 0.152088981063395f, 0.152318523870367f,
+ 0.152548043518276f, 0.152777539976703f, 0.153007013215249f,
+ 0.153236463203521f, 0.153465889911143f, 0.153695293307748f,
+ 0.153924673362982f, 0.154154030046506f, 0.154383363327988f,
+ 0.144408862254502f, 0.144638148640966f, 0.144867411534476f,
+ 0.145096650904754f, 0.145325866721530f, 0.145555058954550f,
+ 0.145784227573570f, 0.146013372548361f, 0.146242493848704f,
+ 0.146471591444392f, 0.146700665305232f, 0.146929715401042f,
+ 0.147158741701652f, 0.147387744176907f, 0.147616722796661f,
+ 0.147845677530781f, 0.148074608349148f, 0.148303515221653f,
+ 0.148532398118202f, 0.148761257008710f, 0.148990091863106f,
+ 0.149218902651333f, 0.149447689343343f, 0.149676451909102f,
+ 0.149905190318589f, 0.150133904541793f, 0.150362594548718f,
+ 0.150591260309378f, 0.150819901793800f, 0.151048518972024f,
+ 0.151277111814102f, 0.151505680290097f, 0.151734224370087f,
+ 0.151962744024160f, 0.152191239222416f, 0.152419709934969f,
+ 0.152648156131945f, 0.152876577783480f, 0.153104974859726f,
+ 0.153333347330843f, 0.153561695167008f, 0.153790018338407f,
+ 0.154018316815238f, 0.154246590567715f, 0.154474839566059f,
+ 0.154703063780507f, 0.154931263181308f, 0.155159437738722f,
+ 0.155387587423022f, 0.155615712204492f, 0.155843812053431f,
+ 0.156071886940148f, 0.156299936834965f, 0.146324150785604f,
+ 0.146552150607636f, 0.146780125348808f, 0.147008074979490f,
+ 0.147235999470067f, 0.147463898790933f, 0.147691772912497f,
+ 0.147919621805179f, 0.148147445439411f, 0.148375243785639f,
+ 0.148603016814319f, 0.148830764495920f, 0.149058486800924f,
+ 0.149286183699826f, 0.149513855163131f, 0.149741501161358f,
+ 0.149969121665038f, 0.150196716644712f, 0.150424286070939f,
+ 0.150651829914283f, 0.150879348145326f, 0.151106840734659f,
+ 0.151334307652887f, 0.151561748870628f, 0.151789164358508f,
+ 0.152016554087171f, 0.152243918027269f, 0.152471256149469f,
+ 0.152698568424449f, 0.152925854822898f, 0.153153115315520f,
+ 0.153380349873030f, 0.153607558466155f, 0.153834741065634f,
+ 0.154061897642219f, 0.154289028166675f, 0.154516132609777f,
+ 0.154743210942315f, 0.154970263135090f, 0.155197289158914f,
+ 0.155424288984613f, 0.155651262583025f, 0.155878209925000f,
+ 0.156105130981400f, 0.156332025723100f, 0.156558894120987f,
+ 0.156785736145960f, 0.157012551768931f, 0.157239340960823f,
+ 0.157466103692572f, 0.157692839935127f, 0.157919549659449f,
+ 0.158146232836509f, 0.158372889437294f, 0.158599519432801f,
+ 0.158826122794039f, 0.159052699492031f, 0.159279249497811f,
+ 0.159505772782425f, 0.159732269316933f, 0.159958739072404f,
+ 0.160185182019924f, 0.160411598130588f, 0.160637987375502f,
+ 0.160864349725789f, 0.161090685152580f, 0.161316993627020f,
+ 0.161543275120267f, 0.151565718680876f, 0.151791946125256f,
+ 0.152018146501987f, 0.152244319782275f, 0.152470465937339f,
+ 0.152696584938411f, 0.152922676756732f, 0.153148741363559f,
+ 0.153374778730158f, 0.153600788827810f, 0.153826771627808f,
+ 0.154052727101455f, 0.154278655220068f, 0.154504555954977f,
+ 0.154730429277522f, 0.154956275159058f, 0.155182093570950f,
+ 0.155407884484577f, 0.155633647871328f, 0.155859383702607f,
+ 0.156085091949829f, 0.156310772584421f, 0.156536425577822f,
+ 0.156762050901484f, 0.156987648526872f, 0.157213218425462f,
+ 0.157438760568743f, 0.157664274928215f, 0.157889761475393f,
+ 0.158115220181800f, 0.158340651018977f, 0.158566053958471f,
+ 0.158791428971847f, 0.159016776030678f, 0.159242095106551f,
+ 0.159467386171067f, 0.159692649195835f, 0.159917884152481f,
+ 0.160143091012640f, 0.160368269747960f, 0.160593420330103f,
+ 0.160818542730740f, 0.161043636921558f, 0.161268702874254f,
+ 0.161493740560537f, 0.161718749952131f, 0.161943731020768f,
+ 0.162168683738195f, 0.162393608076173f, 0.162618504006471f,
+ 0.162843371500873f, 0.163068210531175f, 0.163293021069186f,
+ 0.163517803086724f, 0.163742556555623f, 0.163967281447728f,
+ 0.164191977734896f, 0.164416645388997f, 0.164641284381911f,
+ 0.164865894685533f, 0.165090476271770f, 0.165315029112540f,
+ 0.165539553179773f, 0.165764048445414f, 0.165988514881416f,
+ 0.166212952459749f, 0.166437361152392f, 0.166661740931336f,
+ 0.166886091768588f, 0.167110413636163f, 0.167334706506090f,
+ 0.167558970350412f, 0.167783205141181f, 0.168007410850464f,
+ 0.168231587450339f, 0.168455734912896f, 0.168679853210239f,
+ 0.168903942314481f, 0.169128002197751f, 0.169352032832189f,
+ 0.169576034189945f, 0.169800006243185f, 0.170023948964084f,
+ 0.170247862324832f, 0.170471746297630f, 0.170695600854690f,
+ 0.170919425968239f, 0.171143221610514f, 0.171366987753766f,
+ 0.171590724370256f, 0.171814431432261f, 0.172038108912066f,
+ 0.172261756781971f, 0.172485375014288f, 0.172708963581339f,
+ 0.172932522455463f, 0.173156051609007f, 0.173379551014331f,
+ 0.173603020643809f, 0.173826460469826f, 0.174049870464779f,
+ 0.174273250601078f, 0.174496600851146f, 0.174719921187417f,
+ 0.174943211582337f, 0.175166472008365f, 0.175389702437973f,
+ 0.175612902843644f, 0.175836073197874f, 0.176059213473171f,
+ 0.176282323642056f, 0.176505403677060f, 0.176728453550729f,
+ 0.176951473235621f, 0.177174462704304f, 0.177397421929360f,
+ 0.177620350883383f, 0.177843249538981f, 0.178066117868771f,
+ 0.178288955845384f, 0.178511763441463f, 0.178734540629664f,
+ 0.178957287382655f, 0.179180003673115f, 0.179402689473738f,
+ 0.179625344757226f, 0.179847969496298f, 0.180070563663683f,
+ 0.180293127232121f, 0.180515660174367f, 0.180738162463187f,
+ 0.180960634071358f, 0.181183074971672f, 0.181405485136931f,
+ 0.181627864539950f, 0.181850213153558f, 0.182072530950592f,
+ 0.182294817903906f, 0.182517073986364f, 0.182739299170841f,
+ 0.182961493430228f, 0.183183656737425f, 0.183405789065345f,
+ 0.183627890386914f, 0.183849960675070f, 0.184071999902763f,
+ 0.184294008042956f, 0.184515985068623f, 0.184737930952752f,
+ 0.184959845668342f, 0.185181729188404f, 0.185403581485962f,
+ 0.185625402534053f, 0.185847192305725f, 0.186068950774038f,
+ 0.186290677912066f, 0.186512373692895f, 0.186734038089621f,
+ 0.186955671075354f, 0.187177272623217f, 0.187398842706344f,
+ 0.187620381297882f, 0.187841888370990f, 0.188063363898839f,
+ 0.188284807854612f, 0.188506220211506f, 0.188727600942729f,
+ 0.188948950021500f, 0.189170267421053f, 0.189391553114633f,
+ 0.189612807075496f, 0.189834029276914f, 0.190055219692166f,
+ 0.190276378294547f, 0.190497505057364f, 0.190718599953936f,
+ 0.190939662957593f, 0.191160694041678f, 0.191381693179547f,
+ 0.191602660344568f, 0.181619784587509f, 0.181840687726985f,
+ 0.182061558813791f, 0.182282397821343f, 0.182503204723069f,
+ 0.182723979492412f, 0.182944722102825f, 0.183165432527773f,
+ 0.183386110740737f, 0.183606756715205f, 0.183827370424682f,
+ 0.184047951842681f, 0.184268500942731f, 0.184489017698371f,
+ 0.184709502083153f, 0.184929954070642f, 0.185150373634414f,
+ 0.185370760748058f, 0.185591115385175f, 0.185811437519379f,
+ 0.186031727124294f, 0.186251984173560f, 0.186472208640826f,
+ 0.186692400499755f, 0.186912559724022f, 0.187132686287313f,
+ 0.187352780163329f, 0.187572841325781f, 0.187792869748393f,
+ 0.188012865404900f, 0.188232828269052f, 0.188452758314610f,
+ 0.188672655515346f, 0.188892519845045f, 0.189112351277506f,
+ 0.189332149786539f, 0.189551915345965f, 0.189771647929618f,
+ 0.189991347511347f, 0.190211014065009f, 0.190430647564477f,
+ 0.190650247983633f, 0.190869815296374f, 0.191089349476607f,
+ 0.191308850498254f, 0.191528318335246f, 0.191747752961530f,
+ 0.191967154351062f, 0.192186522477811f, 0.192405857315761f,
+ 0.192625158838903f, 0.192844427021247f, 0.193063661836809f,
+ 0.193282863259621f, 0.193502031263726f, 0.193721165823180f,
+ 0.193940266912050f, 0.194159334504417f, 0.194378368574374f,
+ 0.194597369096023f, 0.194816336043484f, 0.195035269390884f,
+ 0.195254169112365f, 0.195473035182082f, 0.195691867574200f,
+ 0.195910666262898f, 0.196129431222366f, 0.196348162426807f,
+ 0.196566859850436f, 0.196785523467482f, 0.197004153252183f,
+ 0.197222749178791f, 0.197441311221572f, 0.197659839354801f,
+ 0.197878333552768f, 0.198096793789773f, 0.198315220040131f,
+ 0.198533612278166f, 0.198751970478218f, 0.198970294614635f,
+ 0.199188584661781f, 0.199406840594031f, 0.199625062385772f,
+ 0.199843250011403f, 0.200061403445335f, 0.200279522661994f,
+ 0.200497607635815f, 0.200715658341246f, 0.200933674752750f,
+ 0.201151656844797f, 0.201369604591875f, 0.201587517968481f,
+ 0.201805396949124f, 0.202023241508327f, 0.202241051620625f,
+ 0.202458827260564f, 0.202676568402703f, 0.202894275021614f,
+ 0.203111947091881f, 0.203329584588098f, 0.203547187484876f,
+ 0.203764755756833f, 0.203982289378603f, 0.204199788324830f,
+ 0.204417252570173f, 0.204634682089301f, 0.204852076856895f,
+ 0.205069436847651f, 0.205286762036273f, 0.195300241474869f,
+ 0.195517496983395f, 0.195734717613981f, 0.195951903341383f,
+ 0.196169054140369f, 0.196386169985719f, 0.196603250852226f,
+ 0.196820296714693f, 0.197037307547939f, 0.197254283326792f,
+ 0.197471224026094f, 0.197688129620699f, 0.197905000085473f,
+ 0.198121835395295f, 0.198338635525054f, 0.198555400449654f,
+ 0.198772130144011f, 0.198988824583052f, 0.199205483741716f,
+ 0.199422107594955f, 0.199638696117735f, 0.199855249285032f,
+ 0.200071767071834f, 0.200288249453143f, 0.200504696403972f,
+ 0.200721107899348f, 0.200937483914308f, 0.201153824423902f,
+ 0.201370129403194f, 0.201586398827258f, 0.201802632671180f,
+ 0.202018830910062f, 0.202234993519015f, 0.202451120473161f,
+ 0.202667211747639f, 0.202883267317596f, 0.203099287158194f,
+ 0.203315271244605f, 0.203531219552015f, 0.203747132055621f,
+ 0.203963008730634f, 0.204178849552277f, 0.204394654495782f,
+ 0.204610423536399f, 0.204826156649384f, 0.205041853810011f,
+ 0.205257514993562f, 0.205473140175333f, 0.205688729330634f,
+ 0.205904282434784f, 0.206119799463116f, 0.206335280390975f,
+ 0.206550725193719f, 0.206766133846718f, 0.206981506325353f,
+ 0.207196842605018f, 0.197208331738507f, 0.197423595546465f,
+ 0.197638823081710f, 0.197854014319685f, 0.198069169235847f,
+ 0.198284287805661f, 0.198499370004610f, 0.198714415808185f,
+ 0.198929425191892f, 0.199144398131246f, 0.199359334601778f,
+ 0.199574234579030f, 0.199789098038554f, 0.200003924955918f,
+ 0.200218715306700f, 0.200433469066490f, 0.200648186210891f,
+ 0.200862866715519f, 0.201077510556001f, 0.201292117707978f,
+ 0.201506688147100f, 0.201721221849034f, 0.201935718789455f,
+ 0.202150178944052f, 0.202364602288527f, 0.202578988798593f,
+ 0.202793338449976f, 0.203007651218415f, 0.203221927079658f,
+ 0.203436166009470f, 0.203650367983626f, 0.203864532977911f,
+ 0.204078660968127f, 0.204292751930084f, 0.204506805839607f,
+ 0.204720822672532f, 0.204934802404708f, 0.205148745011996f,
+ 0.205362650470268f, 0.205576518755411f, 0.205790349843322f,
+ 0.206004143709912f, 0.206217900331101f, 0.206431619682827f,
+ 0.196441490818421f, 0.196655135559069f, 0.196868742958131f,
+ 0.197082312991588f, 0.197295845635438f, 0.197509340865688f,
+ 0.197722798658360f, 0.197936218989487f, 0.198149601835112f,
+ 0.198362947171294f, 0.198576254974103f, 0.198789525219620f,
+ 0.199002757883940f, 0.199215952943169f, 0.199429110373427f,
+ 0.199642230150843f, 0.199855312251563f, 0.200068356651740f,
+ 0.200281363327544f, 0.200494332255154f, 0.200707263410764f,
+ 0.200920156770577f, 0.201133012310811f, 0.201345830007696f,
+ 0.201558609837472f, 0.201771351776394f, 0.201984055800729f,
+ 0.202196721886754f, 0.202409350010760f, 0.202621940149051f,
+ 0.202834492277942f, 0.203047006373760f, 0.203259482412845f,
+ 0.203471920371550f, 0.203684320226238f, 0.203896681953288f,
+ 0.204109005529087f, 0.204321290930037f, 0.204533538132551f,
+ 0.194541936190444f, 0.194754106925378f, 0.194966239391191f,
+ 0.195178333564346f, 0.195390389421317f, 0.195602406938593f,
+ 0.195814386092672f, 0.196026326860067f, 0.196238229217301f,
+ 0.196450093140911f, 0.196661918607446f, 0.196873705593467f,
+ 0.197085454075546f, 0.197297164030270f, 0.197508835434236f,
+ 0.197720468264055f, 0.197932062496348f, 0.198143618107750f,
+ 0.198355135074909f, 0.198566613374484f, 0.198778052983145f,
+ 0.198989453877578f, 0.199200816034477f, 0.199412139430552f,
+ 0.199623424042522f, 0.199834669847121f, 0.200045876821094f,
+ 0.200257044941199f, 0.200468174184204f, 0.200679264526893f,
+ 0.200890315946060f, 0.201101328418510f, 0.201312301921064f,
+ 0.201523236430552f, 0.201734131923817f, 0.191741177455104f,
+ 0.191951994846504f, 0.192162773152286f, 0.192373512349342f,
+ 0.192584212414577f, 0.192794873324908f, 0.193005495057265f,
+ 0.193216077588589f, 0.193426620895835f, 0.193637124955968f,
+ 0.193847589745967f, 0.194058015242823f, 0.194268401423539f,
+ 0.194478748265130f, 0.194689055744625f, 0.194899323839062f,
+ 0.195109552525495f, 0.195319741780988f, 0.195529891582617f,
+ 0.195740001907471f, 0.195950072732653f, 0.196160104035275f,
+ 0.196370095792464f, 0.196580047981357f, 0.196789960579106f,
+ 0.196999833562872f, 0.197209666909832f, 0.197419460597172f,
+ 0.197629214602091f, 0.197838928901803f, 0.198048603473531f,
+ 0.198258238294511f, 0.188264022419379f, 0.188473577670623f,
+ 0.188683093102902f, 0.188892568693502f, 0.189102004419720f,
+ 0.189311400258868f, 0.189520756188268f, 0.189730072185253f,
+ 0.189939348227172f, 0.190148584291383f, 0.190357780355258f,
+ 0.190566936396181f, 0.190776052391548f, 0.190985128318767f,
+ 0.191194164155259f, 0.191403159878457f, 0.191612115465806f,
+ 0.191821030894764f, 0.192029906142801f, 0.192238741187397f,
+ 0.192447536006049f, 0.192656290576263f, 0.192865004875557f,
+ 0.193073678881463f, 0.193282312571524f, 0.193490905923295f,
+ 0.193699458914346f, 0.193907971522257f, 0.194116443724619f,
+ 0.194324875499038f, 0.194533266823131f, 0.184537806751915f,
+ 0.184746117108257f, 0.184954386947198f, 0.185162616246405f,
+ 0.185370804983555f, 0.185578953136341f, 0.185787060682465f,
+ 0.185995127599643f, 0.186203153865602f, 0.186411139458082f,
+ 0.186619084354836f, 0.186826988533627f, 0.187034851972234f,
+ 0.187242674648445f, 0.187450456540062f, 0.187658197624897f,
+ 0.187865897880778f, 0.188073557285542f, 0.188281175817040f,
+ 0.188488753453134f, 0.188696290171701f, 0.188903785950626f,
+ 0.189111240767811f, 0.189318654601166f, 0.189526027428616f,
+ 0.189733359228097f, 0.189940649977559f, 0.190147899654962f,
+ 0.190355108238280f, 0.180358464782885f, 0.180565591112002f,
+ 0.180772676281026f, 0.180979720267981f, 0.181186723050902f,
+ 0.181393684607836f, 0.181600604916841f, 0.181807483955990f,
+ 0.182014321703366f, 0.182221118137065f, 0.182427873235197f,
+ 0.182634586975881f, 0.182841259337251f, 0.183047890297452f,
+ 0.183254479834642f, 0.183461027926990f, 0.183667534552678f,
+ 0.183873999689901f, 0.184080423316866f, 0.184286805411791f,
+ 0.184493145952909f, 0.184699444918461f, 0.184905702286705f,
+ 0.185111918035908f, 0.185318092144350f, 0.185524224590325f,
+ 0.185730315352137f, 0.185936364408104f, 0.175938560813942f,
+ 0.176144526393218f, 0.176350450201674f, 0.176556332217677f,
+ 0.176762172419604f, 0.176967970785847f, 0.177173727294808f,
+ 0.177379441924904f, 0.177585114654563f, 0.177790745462223f,
+ 0.177996334326338f, 0.178201881225372f, 0.178407386137802f,
+ 0.178612849042117f, 0.178818269916818f, 0.179023648740420f,
+ 0.179228985491448f, 0.179434280148441f, 0.179639532689949f,
+ 0.179844743094535f, 0.180049911340774f, 0.180255037407254f,
+ 0.180460121272574f, 0.180665162915346f, 0.180870162314195f,
+ 0.181075119447757f, 0.181280034294680f, 0.181484906833627f,
+ 0.171485926120658f, 0.171690713979683f, 0.171895459466788f,
+ 0.172100162560683f, 0.172304823240091f, 0.172509441483747f,
+ 0.172714017270397f, 0.172918550578801f, 0.173123041387730f,
+ 0.173327489675969f, 0.173531895422314f, 0.173736258605573f,
+ 0.173940579204567f, 0.174144857198129f, 0.174349092565105f,
+ 0.174553285284351f, 0.174757435334738f, 0.174961542695148f,
+ 0.175165607344476f, 0.175369629261627f, 0.175573608425522f,
+ 0.175777544815091f, 0.175981438409279f, 0.176185289187040f,
+ 0.176389097127344f, 0.176592862209170f, 0.176796584411511f,
+ 0.177000263713372f, 0.167000089171158f, 0.167203682609124f,
+ 0.167407233083699f, 0.167610740573936f, 0.167814205058903f,
+ 0.168017626517678f, 0.168221004929352f, 0.168424340273029f,
+ 0.168627632527822f, 0.168830881672862f, 0.169034087687287f,
+ 0.169237250550250f, 0.169440370240916f, 0.169643446738461f,
+ 0.169846480022075f, 0.170049470070959f, 0.170252416864327f,
+ 0.170455320381406f, 0.170658180601432f, 0.170860997503658f,
+ 0.171063771067346f, 0.171266501271771f, 0.171469188096220f,
+ 0.171671831519994f, 0.171874431522404f, 0.172076988082775f,
+ 0.172279501180443f, 0.162278159872144f, 0.162480585982465f,
+ 0.162682968568167f, 0.162885307608635f, 0.163087603083268f,
+ 0.163289854971476f, 0.163492063252680f, 0.163694227906317f,
+ 0.163896348911833f, 0.164098426248687f, 0.164300459896352f,
+ 0.164502449834311f, 0.164704396042060f, 0.164906298499108f,
+ 0.165108157184976f, 0.165309972079197f, 0.165511743161317f,
+ 0.165713470410892f, 0.165915153807493f, 0.166116793330702f,
+ 0.166318388960114f, 0.166519940675335f, 0.166721448455984f,
+ 0.166922912281694f, 0.167124332132106f, 0.167325707986878f,
+ 0.167527039825678f, 0.157524516705573f, 0.157725760451481f,
+ 0.157926960120494f, 0.158128115692331f, 0.158329227146721f,
+ 0.158530294463405f, 0.158731317622137f, 0.158932296602685f,
+ 0.159133231384826f, 0.159334121948353f, 0.159534968273067f,
+ 0.159735770338785f, 0.159936528125335f, 0.160137241612557f,
+ 0.160337910780302f, 0.160538535608436f, 0.160739116076836f,
+ 0.160939652165391f, 0.161140143854002f, 0.161340591122584f,
+ 0.161540993951062f, 0.161741352319375f, 0.161941666207473f,
+ 0.162141935595320f, 0.162342160462890f, 0.162542340790172f,
+ 0.162742476557164f, 0.162942567743880f, 0.152938803407730f,
+ 0.153138805373977f, 0.153338762700057f, 0.153538675366031f,
+ 0.153738543351972f, 0.153938366637967f, 0.154138145204113f,
+ 0.154337879030521f, 0.154537568097313f, 0.154737212384624f,
+ 0.154936811872602f, 0.155136366541405f, 0.155335876371206f,
+ 0.155535341342188f, 0.155734761434549f, 0.155934136628495f,
+ 0.156133466904249f, 0.156332752242043f, 0.156531992622123f,
+ 0.156731188024747f, 0.156930338430184f, 0.157129443818716f,
+ 0.157328504170639f, 0.157527519466260f, 0.157726489685896f,
+ 0.157925414809880f, 0.158124294818555f, 0.158323129692277f,
+ 0.148318108488803f, 0.148516853033737f, 0.148715552384859f,
+ 0.148914206522576f, 0.149112815427303f, 0.149311379079472f,
+ 0.149509897459523f, 0.149708370547911f, 0.149906798325103f,
+ 0.150105180771577f, 0.150303517867824f, 0.150501809594347f,
+ 0.150700055931663f, 0.150898256860299f, 0.151096412360795f,
+ 0.151294522413704f, 0.151492586999591f, 0.151690606099032f,
+ 0.151888579692616f, 0.152086507760946f, 0.152284390284635f,
+ 0.152482227244310f, 0.152680018620608f, 0.152877764394181f,
+ 0.153075464545691f, 0.153273119055814f, 0.153470727905238f,
+ 0.153668291074661f, 0.143661997622184f, 0.143859469373757f,
+ 0.144056895387502f, 0.144254275644170f, 0.144451610124522f,
+ 0.144648898809330f, 0.144846141679382f, 0.145043338715474f,
+ 0.145240489898417f, 0.145437595209035f, 0.145634654628161f,
+ 0.145831668136644f, 0.146028635715342f, 0.146225557345127f,
+ 0.146422433006884f, 0.146619262681509f, 0.146816046349909f,
+ 0.147012783993008f, 0.147209475591736f, 0.147406121127041f,
+ 0.147602720579879f, 0.147799273931220f, 0.147995781162048f,
+ 0.148192242253357f, 0.148388657186152f, 0.148585025941455f,
+ 0.148781348500295f, 0.148977624843717f, 0.149173854952777f,
+ 0.149370038808543f, 0.139362365469483f, 0.139558456761915f,
+ 0.139754501744331f, 0.139950500397849f, 0.140146452703599f,
+ 0.140342358642723f, 0.140538218196374f, 0.140734031345720f,
+ 0.140929798071938f, 0.141125518356222f, 0.141321192179772f,
+ 0.141516819523806f, 0.141712400369552f, 0.141907934698248f,
+ 0.142103422491149f, 0.142298863729519f, 0.142494258394634f,
+ 0.142689606467784f, 0.142884907930271f, 0.143080162763409f,
+ 0.143275370948524f, 0.143470532466954f, 0.143665647300050f,
+ 0.143860715429175f, 0.144055736835705f, 0.144250711501027f,
+ 0.144445639406540f, 0.144640520533658f, 0.144835354863804f,
+ 0.145030142378415f, 0.145224883058941f, 0.135215765964230f,
+ 0.135410412920980f, 0.135605012988065f, 0.135799566146984f,
+ 0.135994072379245f, 0.136188531666373f, 0.136382943989902f,
+ 0.136577309331380f, 0.136771627672365f, 0.136965898994431f,
+ 0.137160123279160f, 0.137354300508149f, 0.137548430663007f,
+ 0.137742513725355f, 0.137936549676826f, 0.138130538499066f,
+ 0.138324480173732f, 0.138518374682495f, 0.138712222007036f,
+ 0.138906022129051f, 0.139099775030246f, 0.139293480692340f,
+ 0.139487139097066f, 0.139680750226166f, 0.139874314061398f,
+ 0.140067830584528f, 0.140261299777338f, 0.140454721621621f,
+ 0.140648096099182f, 0.140841423191837f, 0.141034702881418f,
+ 0.141227935149766f, 0.141421119978735f, 0.131410446427580f,
+ 0.131603536323403f, 0.131796578725485f, 0.131989573615728f,
+ 0.132182520976047f, 0.132375420788372f, 0.132568273034642f,
+ 0.132761077696810f, 0.132953834756840f, 0.133146544196710f,
+ 0.133339205998410f, 0.133531820143939f, 0.133724386615314f,
+ 0.133916905394560f, 0.134109376463715f, 0.134301799804830f,
+ 0.134494175399969f, 0.134686503231206f, 0.134878783280630f,
+ 0.135071015530340f, 0.135263199962448f, 0.135455336559080f,
+ 0.135647425302371f, 0.135839466174471f, 0.136031459157541f,
+ 0.136223404233754f, 0.136415301385298f, 0.136607150594369f,
+ 0.136798951843178f, 0.136990705113949f, 0.137182410388915f,
+ 0.137374067650325f, 0.137565676880437f, 0.137757238061525f,
+ 0.137948751175871f, 0.127936405283160f, 0.128127822210926f,
+ 0.128319191018876f, 0.128510511689345f, 0.128701784204678f,
+ 0.128893008547232f, 0.129084184699378f, 0.129275312643499f,
+ 0.129466392361988f, 0.129657423837253f, 0.129848407051713f,
+ 0.130039341987800f, 0.130230228627958f, 0.130421066954641f,
+ 0.130611856950320f, 0.130802598597474f, 0.130993291878596f,
+ 0.131183936776193f, 0.131374533272780f, 0.131565081350888f,
+ 0.131755580993060f, 0.131946032181848f, 0.132136434899821f,
+ 0.132326789129556f, 0.132517094853645f, 0.132707352054692f,
+ 0.132897560715312f, 0.133087720818134f, 0.133277832345797f,
+ 0.133467895280954f, 0.133657909606270f, 0.133847875304422f,
+ 0.134037792358100f, 0.134227660750005f, 0.134417480462852f,
+ 0.134607251479366f, 0.134796973782287f, 0.134986647354364f,
+ 0.135176272178362f, 0.125162037314443f, 0.125351564590619f,
+ 0.125541043067079f, 0.125730472726634f, 0.125919853552109f,
+ 0.126109185526341f, 0.126298468632178f, 0.126487702852483f,
+ 0.126676888170129f, 0.126866024568002f, 0.127055112029000f,
+ 0.127244150536033f, 0.127433140072025f, 0.127622080619911f,
+ 0.127810972162637f, 0.127999814683163f, 0.128188608164462f,
+ 0.128377352589517f, 0.128566047941326f, 0.128754694202896f,
+ 0.128943291357249f, 0.129131839387418f, 0.129320338276448f,
+ 0.129508788007399f, 0.129697188563339f, 0.129885539927351f,
+ 0.130073842082530f, 0.130262095011983f, 0.130450298698829f,
+ 0.130638453126200f, 0.130826558277239f, 0.131014614135104f,
+ 0.131202620682961f, 0.131390577903992f, 0.131578485781390f,
+ 0.131766344298360f, 0.131954153438120f, 0.132141913183899f,
+ 0.132329623518940f, 0.132517284426497f, 0.132704895889836f,
+ 0.132892457892237f, 0.133079970416991f, 0.133267433447401f,
+ 0.133454846966783f, 0.133642210958465f, 0.123625714483175f,
+ 0.123812979369491f, 0.124000194678165f, 0.124187360392574f,
+ 0.124374476496108f, 0.124561542972168f, 0.124748559804169f,
+ 0.124935526975537f, 0.125122444469710f, 0.125309312270140f,
+ 0.125496130360289f, 0.125682898723633f, 0.125869617343660f,
+ 0.126056286203869f, 0.126242905287774f, 0.126429474578898f,
+ 0.126615994060779f, 0.126802463716965f, 0.126988883531018f,
+ 0.127175253486512f, 0.127361573567033f, 0.127547843756178f,
+ 0.127734064037559f, 0.127920234394799f, 0.128106354811532f,
+ 0.128292425271406f, 0.128478445758080f, 0.128664416255227f,
+ 0.128850336746530f, 0.129036207215687f, 0.129222027646406f,
+ 0.129407798022407f, 0.129593518327426f, 0.129779188545206f,
+ 0.129964808659507f, 0.130150378654098f, 0.130335898512761f,
+ 0.130521368219292f, 0.130706787757498f, 0.130892157111197f,
+ 0.131077476264222f, 0.131262745200416f, 0.131447963903636f,
+ 0.131633132357750f, 0.131818250546639f, 0.132003318454195f,
+ 0.132188336064325f, 0.132373303360945f, 0.132558220327986f,
+ 0.132743086949389f, 0.132927903209110f, 0.133112669091115f,
+ 0.133297384579382f, 0.133482049657903f, 0.133666664310682f,
+ 0.133851228521735f, 0.134035742275089f, 0.134220205554786f,
+ 0.134404618344876f, 0.134588980629427f, 0.124569481469902f,
+ 0.124753742695616f, 0.124937953368058f, 0.125122113471342f,
+ 0.125306222989596f, 0.125490281906956f, 0.125674290207575f,
+ 0.125858247875615f, 0.126042154895252f, 0.126226011250674f,
+ 0.126409816926081f, 0.126593571905685f, 0.126777276173711f,
+ 0.126960929714395f, 0.127144532511988f, 0.127328084550749f,
+ 0.127511585814954f, 0.127695036288888f, 0.127878435956849f,
+ 0.128061784803149f, 0.128245082812109f, 0.128428329968065f,
+ 0.128611526255365f, 0.128794671658368f, 0.128977766161446f,
+ 0.129160809748984f, 0.129343802405377f, 0.129526744115036f,
+ 0.129709634862381f, 0.129892474631845f, 0.130075263407874f,
+ 0.130258001174926f, 0.130440687917472f, 0.130623323619993f,
+ 0.130805908266985f, 0.130988441842955f, 0.131170924332422f,
+ 0.131353355719918f, 0.131535735989987f, 0.131718065127185f,
+ 0.131900343116080f, 0.132082569941253f, 0.132264745587298f,
+ 0.132446870038820f, 0.132628943280436f, 0.132810965296776f,
+ 0.132992936072482f, 0.133174855592210f, 0.133356723840624f,
+ 0.133538540802406f, 0.133720306462246f, 0.133902020804847f,
+ 0.134083683814926f, 0.134265295477211f, 0.134446855776441f,
+ 0.134628364697371f, 0.134809822224764f, 0.134991228343398f,
+ 0.135172583038063f, 0.135353886293561f, 0.135535138094705f,
+ 0.135716338426323f, 0.135897487273252f, 0.136078584620344f,
+ 0.136259630452462f, 0.136440624754481f, 0.136621567511290f,
+ 0.136802458707789f, 0.136983298328889f, 0.137164086359516f,
+ 0.137344822784607f, 0.137525507589110f, 0.137706140757987f,
+ 0.137886722276213f, 0.138067252128772f, 0.138247730300664f,
+ 0.138428156776899f, 0.138608531542500f, 0.138788854582503f,
+ 0.138969125881954f, 0.139149345425913f, 0.139329513199453f,
+ 0.139509629187658f, 0.139689693375625f, 0.139869705748462f,
+ 0.140049666291290f, 0.140229574989243f, 0.140409431827467f,
+ 0.140589236791119f, 0.140768989865371f, 0.140948691035404f,
+ 0.141128340286414f, 0.141307937603607f, 0.141487482972203f,
+ 0.141666976377434f, 0.141846417804544f, 0.142025807238789f,
+ 0.142205144665437f, 0.142384430069770f, 0.142563663437080f,
+ 0.142742844752673f, 0.142921974001866f, 0.143101051169990f,
+ 0.143280076242387f, 0.143459049204410f, 0.143637970041428f,
+ 0.143816838738820f, 0.143995655281975f, 0.144174419656299f,
+ 0.144353131847207f, 0.144531791840127f, 0.144710399620500f,
+ 0.144888955173778f, 0.145067458485427f, 0.145245909540924f,
+ 0.145424308325758f, 0.145602654825432f, 0.145780949025460f,
+ 0.145959190911367f, 0.146137380468694f, 0.146315517682990f,
+ 0.146493602539820f, 0.146671635024758f, 0.146849615123393f,
+ 0.147027542821324f, 0.147205418104165f, 0.147383240957540f,
+ 0.147561011367085f, 0.147738729318451f, 0.147916394797298f,
+ 0.148094007789301f, 0.148271568280146f, 0.148449076255531f,
+ 0.148626531701166f, 0.148803934602776f, 0.148981284946094f,
+ 0.149158582716869f, 0.149335827900860f, 0.149513020483839f,
+ 0.149690160451591f, 0.149867247789913f, 0.150044282484613f,
+ 0.150221264521513f, 0.150398193886446f, 0.150575070565257f,
+ 0.150751894543806f, 0.150928665807962f, 0.151105384343607f,
+ 0.151282050136638f, 0.151458663172960f, 0.151635223438494f,
+ 0.151811730919171f, 0.151988185600936f, 0.152164587469744f,
+ 0.152340936511564f, 0.152517232712377f, 0.152693476058176f,
+ 0.152869666534967f, 0.153045804128767f, 0.153221888825607f,
+ 0.153397920611528f, 0.153573899472585f, 0.153749825394845f,
+ 0.153925698364388f, 0.154101518367304f, 0.154277285389697f,
+ 0.154452999417684f, 0.154628660437392f, 0.154804268434962f,
+ 0.154979823396547f, 0.155155325308312f, 0.155330774156434f,
+ 0.155506169927103f, 0.155681512606521f, 0.155856802180902f,
+ 0.145828227713860f, 0.146003411036860f, 0.146178541213538f,
+ 0.146353618230160f, 0.146528642073000f, 0.146703612728346f,
+ 0.146878530182500f, 0.147053394421772f, 0.147228205432488f,
+ 0.147402963200985f, 0.147577667713612f, 0.147752318956730f,
+ 0.147926916916715f, 0.148101461579951f, 0.148275952932838f,
+ 0.148450390961786f, 0.148624775653217f, 0.148799106993568f,
+ 0.148973384969286f, 0.149147609566831f, 0.149321780772674f,
+ 0.149495898573301f, 0.149669962955208f, 0.149843973904904f,
+ 0.150017931408910f, 0.150191835453759f, 0.150365686025999f,
+ 0.150539483112186f, 0.150713226698891f, 0.150886916772697f,
+ 0.151060553320199f, 0.151234136328003f, 0.151407665782731f,
+ 0.151581141671012f, 0.151754563979492f, 0.151927932694826f,
+ 0.152101247803684f, 0.152274509292746f, 0.152447717148705f,
+ 0.152620871358268f, 0.152793971908151f, 0.152967018785084f,
+ 0.153140011975811f, 0.153312951467086f, 0.153485837245675f,
+ 0.153658669298358f, 0.153831447611926f, 0.154004172173183f,
+ 0.154176842968946f, 0.154349459986041f, 0.154522023211310f,
+ 0.154694532631606f, 0.154866988233794f, 0.155039390004751f,
+ 0.155211737931367f, 0.155384032000544f, 0.155556272199196f,
+ 0.155728458514250f, 0.155900590932645f, 0.156072669441331f,
+ 0.156244694027273f, 0.156416664677445f, 0.156588581378836f,
+ 0.156760444118446f, 0.156932252883288f, 0.157104007660386f,
+ 0.157275708436777f, 0.157447355199512f, 0.157618947935650f,
+ 0.157790486632267f, 0.157961971276449f, 0.158133401855293f,
+ 0.158304778355912f, 0.158476100765427f, 0.158647369070975f,
+ 0.158818583259702f, 0.158989743318769f, 0.159160849235348f,
+ 0.159331900996624f, 0.159502898589793f, 0.159673842002064f,
+ 0.159844731220658f, 0.160015566232809f, 0.160186347025763f,
+ 0.160357073586779f, 0.160527745903126f, 0.160698363962087f,
+ 0.160868927750957f, 0.161039437257044f, 0.161209892467666f,
+ 0.161380293370157f, 0.161550639951860f, 0.161720932200131f,
+ 0.161891170102339f, 0.151857542723253f, 0.152027671895491f,
+ 0.152197746683846f, 0.152367767075735f, 0.152537733058589f,
+ 0.152707644619850f, 0.152877501746973f, 0.153047304427424f,
+ 0.153217052648684f, 0.153386746398243f, 0.153556385663605f,
+ 0.153725970432286f, 0.153895500691816f, 0.154064976429733f,
+ 0.154234397633591f, 0.154403764290956f, 0.154573076389405f,
+ 0.154742333916527f, 0.154911536859925f, 0.155080685207213f,
+ 0.155249778946017f, 0.155418818063977f, 0.155587802548743f,
+ 0.155756732387980f, 0.155925607569361f, 0.156094428080577f,
+ 0.156263193909327f, 0.156431905043323f, 0.156600561470291f,
+ 0.156769163177967f, 0.156937710154101f, 0.157106202386455f,
+ 0.157274639862803f, 0.157443022570930f, 0.157611350498636f,
+ 0.157779623633731f, 0.157947841964038f, 0.158116005477393f,
+ 0.158284114161644f, 0.158452168004650f, 0.158620166994283f,
+ 0.158788111118428f, 0.158956000364983f, 0.159123834721855f,
+ 0.159291614176966f, 0.159459338718251f, 0.159627008333654f,
+ 0.159794623011134f, 0.159962182738662f, 0.160129687504220f,
+ 0.160297137295804f, 0.160464532101419f, 0.160631871909088f,
+ 0.160799156706840f, 0.160966386482721f, 0.150929750302173f,
+ 0.151096869998492f, 0.151263934637146f, 0.151430944206227f,
+ 0.151597898693841f, 0.151764798088106f, 0.151931642377153f,
+ 0.152098431549124f, 0.152265165592173f, 0.152431844494468f,
+ 0.152598468244187f, 0.152765036829522f, 0.152931550238678f,
+ 0.153098008459870f, 0.153264411481326f, 0.153430759291287f,
+ 0.153597051878007f, 0.153763289229750f, 0.153929471334795f,
+ 0.154095598181429f, 0.154261669757957f, 0.154427686052692f,
+ 0.154593647053961f, 0.154759552750102f, 0.154925403129467f,
+ 0.155091198180420f, 0.155256937891336f, 0.155422622250604f,
+ 0.155588251246622f, 0.155753824867805f, 0.155919343102577f,
+ 0.156084805939376f, 0.156250213366650f, 0.156415565372862f,
+ 0.156580861946485f, 0.156746103076005f, 0.156911288749923f,
+ 0.157076418956747f, 0.157241493685002f, 0.157406512923222f,
+ 0.157571476659956f, 0.157736384883764f, 0.157901237583217f,
+ 0.147862223824288f, 0.148026965440799f, 0.148191651498746f,
+ 0.148356281986751f, 0.148520856893447f, 0.148685376207480f,
+ 0.148849839917509f, 0.149014248012204f, 0.149178600480248f,
+ 0.149342897310336f, 0.149507138491175f, 0.149671324011486f,
+ 0.149835453859999f, 0.149999528025459f, 0.150163546496623f,
+ 0.150327509262259f, 0.150491416311149f, 0.150655267632085f,
+ 0.150819063213874f, 0.150982803045333f, 0.151146487115293f,
+ 0.151310115412596f, 0.151473687926096f, 0.151637204644661f,
+ 0.151800665557170f, 0.151964070652515f, 0.152127419919599f,
+ 0.152290713347339f, 0.152453950924663f, 0.152617132640512f,
+ 0.152780258483839f, 0.152943328443608f, 0.153106342508799f,
+ 0.153269300668400f, 0.153432202911413f, 0.153595049226854f,
+ 0.153757839603748f, 0.153920574031135f, 0.154083252498065f,
+ 0.144042064070991f, 0.144204630584213f, 0.144367141104205f,
+ 0.144529595620070f, 0.144691994120919f, 0.144854336595877f,
+ 0.145016623034082f, 0.145178853424683f, 0.145341027756842f,
+ 0.145503146019733f, 0.145665208202542f, 0.145827214294467f,
+ 0.145989164284721f, 0.146151058162526f, 0.146312895917117f,
+ 0.146474677537742f, 0.146636403013661f, 0.146798072334147f,
+ 0.146959685488484f, 0.147121242465970f, 0.147282743255912f,
+ 0.147444187847633f, 0.147605576230467f, 0.147766908393758f,
+ 0.147928184326867f, 0.148089404019162f, 0.148250567460028f,
+ 0.148411674638859f, 0.148572725545063f, 0.148733720168058f,
+ 0.148894658497279f, 0.149055540522167f, 0.149216366232181f,
+ 0.149377135616789f, 0.139334037742859f, 0.139494694445110f,
+ 0.139655294790435f, 0.139815838768352f, 0.139976326368392f,
+ 0.140136757580096f, 0.140297132393020f, 0.140457450796730f,
+ 0.140617712780806f, 0.140777918334840f, 0.140938067448435f,
+ 0.141098160111208f, 0.141258196312788f, 0.141418176042814f,
+ 0.141578099290940f, 0.141737966046832f, 0.141897776300166f,
+ 0.142057530040633f, 0.142217227257936f, 0.142376867941787f,
+ 0.142536452081915f, 0.142695979668058f, 0.142855450689968f,
+ 0.143014865137407f, 0.143174223000153f, 0.143333524267992f,
+ 0.143492768930726f, 0.143651956978168f, 0.143811088400141f,
+ 0.143970163186484f, 0.144129181327045f, 0.144288142811688f,
+ 0.144447047630285f, 0.134402084850112f, 0.134560876306290f,
+ 0.134719611066120f, 0.134878289119524f, 0.135036910456437f,
+ 0.135195475066808f, 0.135353982940597f, 0.135512434067775f,
+ 0.135670828438328f, 0.135829166042253f, 0.135987446869558f,
+ 0.136145670910266f, 0.136303838154409f, 0.136461948592034f,
+ 0.136620002213200f, 0.136777999007977f, 0.136935938966447f,
+ 0.137093822078707f, 0.137251648334863f, 0.137409417725035f,
+ 0.137567130239356f, 0.137724785867969f, 0.137882384601031f,
+ 0.138039926428711f, 0.138197411341190f, 0.138354839328662f,
+ 0.138512210381332f, 0.138669524489419f, 0.138826781643152f,
+ 0.138983981832774f, 0.128937314125928f, 0.129094400358105f,
+ 0.129251429596973f, 0.129408401832822f, 0.129565317055958f,
+ 0.129722175256696f, 0.129878976425365f, 0.130035720552306f,
+ 0.130192407627872f, 0.130349037642427f, 0.130505610586351f,
+ 0.130662126450032f, 0.130818585223873f, 0.130974986898289f,
+ 0.131131331463705f, 0.131287618910562f, 0.131443849229311f,
+ 0.131600022410415f, 0.131756138444350f, 0.131912197321604f,
+ 0.132068199032678f, 0.132224143568085f, 0.132380030918349f,
+ 0.132535861074008f, 0.132691634025612f, 0.132847349763722f,
+ 0.133003008278912f, 0.133158609561769f, 0.133314153602892f,
+ 0.133469640392892f, 0.123421258999779f, 0.123576631259414f,
+ 0.123731946239833f, 0.123887203931694f, 0.124042404325672f,
+ 0.124197547412451f, 0.124352633182726f, 0.124507661627209f,
+ 0.124662632736620f, 0.124817546501693f, 0.124972402913174f,
+ 0.125127201961822f, 0.125281943638407f, 0.125436627933713f,
+ 0.125591254838533f, 0.125745824343677f, 0.125900336439963f,
+ 0.126054791118225f, 0.126209188369305f, 0.126363528184061f,
+ 0.126517810553362f, 0.126672035468088f, 0.126826202919134f,
+ 0.126980312897405f, 0.127134365393820f, 0.127288360399307f,
+ 0.127442297904811f, 0.127596177901286f, 0.117546189457086f,
+ 0.117699954408416f, 0.117853661823656f, 0.118007311693808f,
+ 0.118160904009891f, 0.118314438762931f, 0.118467915943971f,
+ 0.118621335544062f, 0.118774697554272f, 0.118928001965676f,
+ 0.119081248769366f, 0.119234437956443f, 0.119387569518022f,
+ 0.119540643445230f, 0.119693659729205f, 0.119846618361101f,
+ 0.119999519332079f, 0.120152362633316f, 0.120305148256000f,
+ 0.120457876191332f, 0.120610546430524f, 0.120763158964802f,
+ 0.120915713785403f, 0.121068210883577f, 0.121220650250584f,
+ 0.121373031877701f, 0.121525355756213f, 0.121677621877418f,
+ 0.111626019310016f, 0.111778169890555f, 0.111930262687758f,
+ 0.112082297692972f, 0.112234274897559f, 0.112386194292890f,
+ 0.112538055870350f, 0.112689859621336f, 0.112841605537257f,
+ 0.112993293609536f, 0.113144923829605f, 0.113296496188912f,
+ 0.113448010678914f, 0.113599467291082f, 0.113750866016899f,
+ 0.113902206847861f, 0.114053489775474f, 0.114204714791260f,
+ 0.114355881886749f, 0.114506991053486f, 0.114658042283029f,
+ 0.114809035566945f, 0.114959970896817f, 0.115110848264237f,
+ 0.115261667660812f, 0.115412429078159f, 0.115563132507910f,
+ 0.115713777941706f, 0.105660554448589f, 0.105811083865453f,
+ 0.105961555261365f, 0.106111968628015f, 0.106262323957108f,
+ 0.106412621240361f, 0.106562860469501f, 0.106713041636271f,
+ 0.106863164732422f, 0.107013229749721f, 0.107163236679946f,
+ 0.107313185514885f, 0.107463076246342f, 0.107612908866132f,
+ 0.107762683366080f, 0.107912399738026f, 0.108062057973823f,
+ 0.108211658065332f, 0.108361200004431f, 0.108510683783007f,
+ 0.108660109392962f, 0.108809476826207f, 0.108958786074669f,
+ 0.109108037130284f, 0.109257229985002f, 0.109406364630786f,
+ 0.109555441059609f, 0.109704459263458f, 0.0996496083117188f,
+ 0.0997985100416282f, 0.0999473535225969f, 0.100096138746660f,
+ 0.100244865705867f, 0.100393534392276f, 0.100542144797961f,
+ 0.100690696915006f, 0.100839190735508f, 0.100987626251577f,
+ 0.101136003455335f, 0.101284322338914f, 0.101432582894463f,
+ 0.101580785114137f, 0.101728928990110f, 0.101877014514563f,
+ 0.102025041679692f, 0.102173010477705f, 0.102320920900821f,
+ 0.102468772941273f, 0.102616566591305f, 0.102764301843173f,
+ 0.102911978689148f, 0.103059597121509f, 0.103207157132551f,
+ 0.103354658714579f, 0.103502101859912f, 0.0934456756382679f,
+ 0.0935930018872135f, 0.0937402696764917f, 0.0938874789984700f,
+ 0.0940346298455276f, 0.0941817222100565f, 0.0943287560844607f,
+ 0.0944757314611567f, 0.0946226483325731f, 0.0947695066911507f,
+ 0.0949163065293431f, 0.0950630478396154f, 0.0952097306144455f,
+ 0.0953563548463236f, 0.0955029205277519f, 0.0956494276512452f,
+ 0.0957958762093304f, 0.0959422661945465f, 0.0960885975994451f,
+ 0.0962348704165899f, 0.0963810846385570f, 0.0965272402579347f,
+ 0.0966733372673236f, 0.0968193756593367f, 0.0969653554265990f,
+ 0.0971112765617480f, 0.0972571390574334f, 0.0974029429063172f,
+ 0.0873448771784613f, 0.0874905637117771f, 0.0876361915763511f,
+ 0.0877817607648946f, 0.0879272712701305f, 0.0880727230847950f,
+ 0.0882181162016359f, 0.0883634506134133f, 0.0885087263129001f,
+ 0.0886539432928808f, 0.0887991015461526f, 0.0889442010655249f,
+ 0.0890892418438194f, 0.0892342238738701f, 0.0893791471485232f,
+ 0.0895240116606371f, 0.0896688174030826f, 0.0898135643687430f,
+ 0.0899582525505135f, 0.0901028819413016f, 0.0902474525340274f,
+ 0.0903919643216231f, 0.0905364172970332f, 0.0906808114532143f,
+ 0.0908251467831358f, 0.0909694232797786f, 0.0911136409361365f,
+ 0.0912577997452155f, 0.0811980887774211f, 0.0813421298710088f,
+ 0.0814861120964089f, 0.0816300354466764f, 0.0817738999148785f,
+ 0.0819177054940949f, 0.0820614521774175f, 0.0822051399579502f,
+ 0.0823487688288098f, 0.0824923387831246f, 0.0826358498140358f,
+ 0.0827793019146968f, 0.0829226950782728f, 0.0830660292979419f,
+ 0.0832093045668942f, 0.0833525208783320f, 0.0834956782254701f,
+ 0.0836387766015353f, 0.0837818159997669f, 0.0839247964134165f,
+ 0.0840677178357478f, 0.0842105802600369f, 0.0843533836795723f,
+ 0.0844961280876545f, 0.0846388134775965f, 0.0847814398427233f,
+ 0.0849240071763727f, 0.0850665154718944f, 0.0852089647226503f,
+ 0.0751475439994024f, 0.0752898751407623f, 0.0754321472175162f,
+ 0.0755743602230756f, 0.0757165141508637f, 0.0758586089943163f,
+ 0.0760006447468815f, 0.0761426214020196f, 0.0762845389532032f,
+ 0.0764263973939170f, 0.0765681967176584f, 0.0767099369179366f,
+ 0.0768516179882736f, 0.0769932399222031f, 0.0771348027132715f,
+ 0.0772763063550374f, 0.0774177508410716f, 0.0775591361649572f,
+ 0.0777004623202896f, 0.0778417293006766f, 0.0779829370997380f,
+ 0.0781240857111062f, 0.0782651751284255f, 0.0784062053453531f,
+ 0.0785471763555577f, 0.0786880881527207f, 0.0788289407305362f,
+ 0.0789697340827096f, 0.0791104682029593f, 0.0792511430850161f,
+ 0.0691879478000098f, 0.0693285041869207f, 0.0694690013169037f,
+ 0.0696094391837383f, 0.0697498177812166f, 0.0698901371031425f,
+ 0.0700303971433326f, 0.0701705978956158f, 0.0703107393538329f,
+ 0.0704508215118373f, 0.0705908443634948f, 0.0707308079026829f,
+ 0.0708707121232921f, 0.0710105570192245f, 0.0711503425843951f,
+ 0.0712900688127309f, 0.0714297356981711f, 0.0715693432346672f,
+ 0.0717088914161831f, 0.0718483802366950f, 0.0719878096901912f,
+ 0.0721271797706725f, 0.0722664904721519f, 0.0724057417886545f,
+ 0.0725449337142179f, 0.0726840662428920f, 0.0728231393687387f,
+ 0.0729621530858327f, 0.0731011073882604f, 0.0732400022701208f,
+ 0.0733788377255252f, 0.0735176137485970f, 0.0634525194108596f,
+ 0.0635911765516861f, 0.0637297742426242f, 0.0638683124778465f,
+ 0.0640067912515382f, 0.0641452105578962f, 0.0642835703911301f,
+ 0.0644218707454617f, 0.0645601116151250f, 0.0646982929943664f,
+ 0.0648364148774443f, 0.0649744772586297f, 0.0651124801322058f,
+ 0.0652504234924680f, 0.0653883073337242f, 0.0655261316502941f,
+ 0.0656638964365102f, 0.0658016016867171f, 0.0659392473952715f,
+ 0.0660768335565426f, 0.0662143601649120f, 0.0663518272147731f,
+ 0.0664892347005321f, 0.0666265826166071f, 0.0667638709574289f,
+ 0.0669010997174400f, 0.0670382688910958f, 0.0671753784728635f,
+ 0.0673124284572228f, 0.0674494188386658f, 0.0675863496116965f,
+ 0.0677232207708315f, 0.0678600323105998f, 0.0679967842255422f,
+ 0.0579296655875996f, 0.0580662982365629f, 0.0582028712443972f,
+ 0.0583393846056928f, 0.0584758383150523f, 0.0586122323670904f,
+ 0.0587485667564341f, 0.0588848414777228f, 0.0590210565256080f,
+ 0.0591572118947538f, 0.0592933075798361f, 0.0594293435755436f,
+ 0.0595653198765770f, 0.0597012364776492f, 0.0598370933734855f,
+ 0.0599728905588236f, 0.0601086280284134f, 0.0602443057770169f,
+ 0.0603799237994086f, 0.0605154820903751f, 0.0606509806447155f,
+ 0.0607864194572412f, 0.0609217985227755f, 0.0610571178361543f,
+ 0.0611923773922258f, 0.0613275771858503f, 0.0614627172119006f,
+ 0.0615977974652616f, 0.0617328179408305f, 0.0618677786335169f,
+ 0.0620026795382425f, 0.0621375206499414f, 0.0622723019635602f,
+ 0.0624070234740572f, 0.0625416851764035f, 0.0626762870655824f,
+ 0.0526070182139767f, 0.0527415004618193f, 0.0528759228815178f,
+ 0.0530102854681045f, 0.0531445882166238f, 0.0532788311221330f,
+ 0.0534130141797010f, 0.0535471373844093f, 0.0536812007313518f,
+ 0.0538152042156344f, 0.0539491478323753f, 0.0540830315767053f,
+ 0.0542168554437671f, 0.0543506194287161f, 0.0544843235267195f,
+ 0.0546179677329570f, 0.0547515520426208f, 0.0548850764509150f,
+ 0.0550185409530562f, 0.0551519455442734f, 0.0552852902198074f,
+ 0.0554185749749120f, 0.0555517998048525f, 0.0556849647049072f,
+ 0.0558180696703662f, 0.0559511146965319f, 0.0560840997787193f,
+ 0.0562170249122554f, 0.0563498900924798f, 0.0564826953147438f,
+ 0.0566154405744115f, 0.0567481258668592f, 0.0568807511874753f,
+ 0.0570133165316607f, 0.0571458218948283f, 0.0572782672724036f,
+ 0.0574106526598240f, 0.0575429780525398f, 0.0576752434460127f,
+ 0.0578074488357176f, 0.0579395942171410f, 0.0478678686631696f,
+ 0.0479998940145396f, 0.0481318593441621f, 0.0482637646475730f,
+ 0.0483956099203204f, 0.0485273951579650f, 0.0486591203560793f,
+ 0.0487907855102481f, 0.0489223906160692f, 0.0490539356691518f,
+ 0.0491854206651178f, 0.0493168455996015f, 0.0494482104682491f,
+ 0.0495795152667194f, 0.0497107599906833f, 0.0498419446358243f,
+ 0.0499730691978377f, 0.0501041336724313f, 0.0502351380553253f,
+ 0.0503660823422521f, 0.0504969665289563f, 0.0506277906111949f,
+ 0.0507585545847371f, 0.0508892584453644f, 0.0510199021888707f,
+ 0.0511504858110618f, 0.0512810093077563f, 0.0514114726747848f,
+ 0.0515418759079902f, 0.0516722190032277f, 0.0518025019563648f,
+ 0.0519327247632811f, 0.0520628874198688f, 0.0521929899220323f,
+ 0.0523230322656880f, 0.0524530144467649f, 0.0525829364612041f,
+ 0.0527127983049592f, 0.0528425999739958f, 0.0529723414642920f,
+ 0.0531020227718380f, 0.0532316438926364f, 0.0533612048227022f,
+ 0.0534907055580622f, 0.0536201460947562f, 0.0537495264288357f,
+ 0.0538788465563647f, 0.0540081064734196f, 0.0541373061760887f,
+ 0.0542664456604730f, 0.0441917140000732f, 0.0443207330362394f,
+ 0.0444496918424971f, 0.0445785904149960f, 0.0447074287498985f,
+ 0.0448362068433790f, 0.0449649246916242f, 0.0450935822908335f,
+ 0.0452221796372181f, 0.0453507167270016f, 0.0454791935564199f,
+ 0.0456076101217214f, 0.0457359664191663f, 0.0458642624450276f,
+ 0.0459924981955904f, 0.0461206736671516f, 0.0462487888560214f,
+ 0.0463768437585213f, 0.0465048383709856f, 0.0466327726897607f,
+ 0.0467606467112054f, 0.0468884604316909f, 0.0470162138476001f,
+ 0.0471439069553288f, 0.0472715397512850f, 0.0473991122318885f,
+ 0.0475266243935721f, 0.0476540762327803f, 0.0477814677459702f,
+ 0.0479087989296110f, 0.0480360697801842f, 0.0481632802941839f,
+ 0.0482904304681158f, 0.0484175202984986f, 0.0485445497818628f,
+ 0.0486715189147517f, 0.0487984276937201f, 0.0489252761153358f,
+ 0.0490520641761785f, 0.0491787918728405f, 0.0493054592019259f,
+ 0.0494320661600513f, 0.0495586127438459f, 0.0496850989499509f,
+ 0.0498115247750195f, 0.0499378902157179f, 0.0500641952687237f,
+ 0.0501904399307276f, 0.0503166241984321f, 0.0504427480685521f,
+ 0.0505688115378147f, 0.0506948146029596f, 0.0508207572607382f,
+ 0.0509466395079148f, 0.0510724613412656f, 0.0511982227575792f,
+ 0.0513239237536566f, 0.0514495643263109f, 0.0515751444723674f,
+ 0.0517006641886638f, 0.0518261234720504f, 0.0519515223193892f,
+ 0.0520768607275548f, 0.0522021386934342f, 0.0523273562139263f,
+ 0.0524525132859427f, 0.0525776099064070f, 0.0527026460722553f,
+ 0.0426238108578230f, 0.0427487261052961f, 0.0428735808890346f,
+ 0.0429983752060237f, 0.0431231090532607f, 0.0432477824277554f,
+ 0.0433723953265295f, 0.0434969477466176f, 0.0436214396850659f,
+ 0.0437458711389333f, 0.0438702421052907f, 0.0439945525812217f,
+ 0.0441188025638218f, 0.0442429920501990f, 0.0443671210374733f,
+ 0.0444911895227774f, 0.0446151975032558f, 0.0447391449760657f,
+ 0.0448630319383766f, 0.0449868583873697f, 0.0451106243202392f,
+ 0.0452343297341911f, 0.0453579746264439f, 0.0454815589942283f,
+ 0.0456050828347873f, 0.0457285461453763f, 0.0458519489232627f,
+ 0.0459752911657265f, 0.0460985728700598f, 0.0462217940335669f,
+ 0.0463449546535647f, 0.0464680547273820f, 0.0465910942523600f,
+ 0.0467140732258525f, 0.0468369916452251f, 0.0469598495078560f,
+ 0.0470826468111357f, 0.0472053835524666f, 0.0473280597292639f,
+ 0.0474506753389546f, 0.0475732303789784f, 0.0476957248467870f,
+ 0.0478181587398445f, 0.0479405320556273f, 0.0480628447916239f,
+ 0.0481850969453354f, 0.0483072885142748f, 0.0484294194959678f,
+ 0.0485514898879519f, 0.0486734996877775f, 0.0487954488930066f,
+ 0.0489173375012139f, 0.0490391655099864f, 0.0491609329169232f,
+ 0.0492826397196357f, 0.0494042859157477f, 0.0495258715028952f,
+ 0.0496473964787265f, 0.0497688608409022f, 0.0498902645870951f,
+ 0.0500116077149904f, 0.0501328902222853f, 0.0502541121066900f,
+ 0.0503752733659261f, 0.0504963739977278f, 0.0506174139998419f,
+ 0.0507383933700272f, 0.0508593121060547f, 0.0509801702057078f,
+ 0.0511009676667822f, 0.0512217044870858f, 0.0513423806644390f,
+ 0.0514629961966743f, 0.0515835510816363f, 0.0517040453171823f,
+ 0.0518244789011815f, 0.0519448518315156f, 0.0520651641060786f,
+ 0.0521854157227766f, 0.0523056066795282f, 0.0524257369742641f,
+ 0.0525458066049274f, 0.0526658155694733f, 0.0527857638658696f,
+ 0.0529056514920961f, 0.0530254784461450f, 0.0531452447260207f,
+ 0.0532649503297400f, 0.0533845952553320f, 0.0535041795008378f,
+ 0.0536237030643112f, 0.0537431659438180f, 0.0538625681374362f,
+ 0.0539819096432564f, 0.0541011904593813f, 0.0542204105839259f,
+ 0.0543395700150173f, 0.0544586687507953f, 0.0545777067894117f,
+ 0.0546966841290304f, 0.0548156007678281f, 0.0549344567039931f,
+ 0.0550532519357268f, 0.0551719864612422f, 0.0552906602787649f,
+ 0.0554092733865326f, 0.0555278257827956f, 0.0556463174658160f,
+ 0.0557647484338686f, 0.0558831186852405f, 0.0560014282182306f,
+ 0.0561196770311506f, 0.0562378651223242f, 0.0563559924900874f,
+ 0.0564740591327887f, 0.0565920650487887f, 0.0567100102364602f,
+ 0.0568278946941885f, 0.0569457184203710f, 0.0570634814134173f,
+ 0.0571811836717497f, 0.0572988251938025f, 0.0574164059780221f,
+ 0.0575339260228674f, 0.0576513853268097f, 0.0577687838883325f,
+ 0.0578861217059312f, 0.0580033987781141f, 0.0581206151034013f,
+ 0.0582377706803254f, 0.0583548655074314f, 0.0584718995832763f,
+ 0.0585888729064294f, 0.0587057854754726f, 0.0588226372889998f,
+ 0.0589394283456172f, 0.0590561586439433f, 0.0591728281826091f,
+ 0.0592894369602576f, 0.0594059849755440f, 0.0595224722271363f,
+ 0.0596388987137142f, 0.0597552644339699f, 0.0598715693866082f,
+ 0.0599878135703456f, 0.0601039969839111f, 0.0602201196260463f,
+ 0.0603361814955049f, 0.0604521825910525f, 0.0605681229114674f,
+ 0.0606840024555402f, 0.0607998212220735f, 0.0609155792098825f,
+ 0.0610312764177944f, 0.0611469128446488f, 0.0612624884892977f,
+ 0.0613780033506051f, 0.0614934574274476f, 0.0616088507187138f,
+ 0.0617241832233048f, 0.0618394549401339f, 0.0619546658681265f,
+ 0.0620698160062207f, 0.0621849053533666f, 0.0622999339085263f,
+ 0.0624149016706749f, 0.0625298086387993f, 0.0626446548118985f,
+ 0.0627594401889842f, 0.0628741647690804f, 0.0629888285512230f,
+ 0.0631034315344604f, 0.0632179737178534f, 0.0633324551004748f,
+ 0.0634468756814098f, 0.0635612354597560f, 0.0636755344346234f,
+ 0.0637897726051337f, 0.0639039499704215f, 0.0640180665296333f,
+ 0.0641321222819281f, 0.0642461172264772f, 0.0643600513624639f,
+ 0.0644739246890841f, 0.0645877372055457f, 0.0647014889110692f,
+ 0.0648151798048871f, 0.0649288098862443f, 0.0650423791543980f,
+ 0.0651558876086177f, 0.0652693352481850f, 0.0653827220723941f,
+ 0.0654960480805510f, 0.0656093132719747f, 0.0657225176459958f,
+ 0.0658356612019574f, 0.0659487439392150f, 0.0660617658571363f,
+ 0.0661747269551014f, 0.0662876272325024f, 0.0664004666887441f,
+ 0.0665132453232429f, 0.0666259631354284f, 0.0667386201247416f,
+ 0.0668512162906364f, 0.0669637516325786f, 0.0670762261500467f,
+ 0.0671886398425310f, 0.0673009927095344f, 0.0674132847505718f,
+ 0.0675255159651708f, 0.0676376863528710f, 0.0677497959132243f,
+ 0.0678618446457948f, 0.0679738325501591f, 0.0680857596259061f,
+ 0.0681976258726366f, 0.0683094312899641f, 0.0684211758775141f,
+ 0.0685328596349247f, 0.0686444825618459f, 0.0687560446579401f,
+ 0.0688675459228822f, 0.0689789863563593f, 0.0690903659580705f,
+ 0.0692016847277276f, 0.0693129426650541f, 0.0694241397697866f,
+ 0.0695352760416732f, 0.0594425405578624f, 0.0595535551633519f,
+ 0.0596645089353147f, 0.0597754018735483f, 0.0598862339778625f,
+ 0.0599970052480793f, 0.0601077156840334f, 0.0602183652855713f,
+ 0.0603289540525519f, 0.0604394819848466f, 0.0605499490823388f,
+ 0.0606603553449245f, 0.0607707007725117f, 0.0608809853650206f,
+ 0.0609912091223840f, 0.0611013720445469f, 0.0612114741314665f,
+ 0.0613215153831122f, 0.0614314957994659f, 0.0615414153805216f,
+ 0.0616512741262855f, 0.0617610720367764f, 0.0618708091120252f,
+ 0.0619804853520752f, 0.0620901007569816f, 0.0621996553268123f,
+ 0.0623091490616473f, 0.0624185819615789f, 0.0625279540267118f,
+ 0.0626372652571628f, 0.0627465156530610f, 0.0628557052145480f,
+ 0.0629648339417774f, 0.0630739018349152f, 0.0631829088941397f,
+ 0.0632918551196415f, 0.0634007405116234f, 0.0635095650703006f,
+ 0.0636183287959005f, 0.0637270316886627f, 0.0638356737488393f,
+ 0.0639442549766944f, 0.0640527753725047f, 0.0641612349365589f,
+ 0.0642696336691582f, 0.0643779715706160f, 0.0644862486412577f,
+ 0.0645944648814216f, 0.0647026202914576f, 0.0648107148717285f,
+ 0.0649187486226089f, 0.0650267215444860f, 0.0651346336377590f,
+ 0.0652424849028396f, 0.0653502753401518f, 0.0654580049501316f,
+ 0.0655656737332277f, 0.0656732816899009f, 0.0657808288206239f,
+ 0.0658883151258823f, 0.0659957406061736f, 0.0661031052620077f,
+ 0.0662104090939068f, 0.0663176521024054f, 0.0664248342880501f,
+ 0.0665319556513999f, 0.0666390161930263f, 0.0667460159135127f,
+ 0.0668529548134549f, 0.0669598328934612f, 0.0670666501541520f,
+ 0.0671734065961599f, 0.0672801022201299f, 0.0673867370267193f,
+ 0.0674933110165977f, 0.0675998241904468f, 0.0677062765489609f,
+ 0.0678126680928461f, 0.0679189988228213f, 0.0680252687396174f,
+ 0.0681314778439776f, 0.0682376261366575f, 0.0683437136184247f,
+ 0.0684497402900595f, 0.0685557061523543f, 0.0686616112061134f,
+ 0.0687674554521540f, 0.0688732388913053f, 0.0689789615244086f,
+ 0.0690846233523179f, 0.0691902243758991f, 0.0692957645960306f,
+ 0.0694012440136030f, 0.0695066626295191f, 0.0696120204446942f,
+ 0.0697173174600557f, 0.0698225536765433f, 0.0699277290951092f,
+ 0.0700328437167174f, 0.0701378975423447f, 0.0702428905729799f,
+ 0.0703478228096242f, 0.0704526942532909f, 0.0705575049050059f,
+ 0.0706622547658070f, 0.0707669438367446f, 0.0708715721188812f,
+ 0.0607723286906791f, 0.0608768353984505f, 0.0609812813206822f,
+ 0.0610856664584861f, 0.0611899908129857f, 0.0612942543853178f,
+ 0.0613984571766304f, 0.0615025991880848f, 0.0616066804208537f,
+ 0.0617107008761226f, 0.0618146605550893f, 0.0619185594589635f,
+ 0.0620223975889676f, 0.0621261749463358f, 0.0622298915323152f,
+ 0.0623335473481647f, 0.0624371423951557f, 0.0625406766745718f,
+ 0.0626441501877088f, 0.0627475629358750f, 0.0628509149203909f,
+ 0.0629542061425891f, 0.0630574366038147f, 0.0631606063054251f,
+ 0.0632637152487898f, 0.0633667634352907f, 0.0634697508663218f,
+ 0.0635726775432898f, 0.0636755434676132f, 0.0637783486407231f,
+ 0.0638810930640628f, 0.0639837767390877f, 0.0640863996672659f,
+ 0.0641889618500774f, 0.0642914632890144f, 0.0643939039855820f,
+ 0.0644962839412969f, 0.0645986031576883f, 0.0647008616362979f,
+ 0.0648030593786793f, 0.0649051963863989f, 0.0650072726610348f,
+ 0.0651092882041778f, 0.0652112430174308f, 0.0653131371024089f,
+ 0.0654149704607399f, 0.0655167430940633f, 0.0656184550040314f,
+ 0.0657201061923084f, 0.0658216966605708f, 0.0659232264105077f,
+ 0.0660246954438204f, 0.0661261037622221f, 0.0662274513674386f,
+ 0.0663287382612081f, 0.0664299644452808f, 0.0665311299214192f,
+ 0.0666322346913985f, 0.0667332787570056f, 0.0668342621200399f,
+ 0.0669351847823132f, 0.0670360467456497f, 0.0671368480118855f,
+ 0.0672375885828691f, 0.0673382684604614f, 0.0674388876465356f,
+ 0.0675394461429772f, 0.0676399439516836f, 0.0677403810745650f,
+ 0.0678407575135437f, 0.0679410732705540f, 0.0680413283475430f,
+ 0.0681415227464696f, 0.0682416564693053f, 0.0683417295180337f,
+ 0.0582379309720382f, 0.0583378826785522f, 0.0584377737169835f,
+ 0.0585376040893650f, 0.0586373737977419f, 0.0587370828441713f,
+ 0.0588367312307229f, 0.0589363189594787f, 0.0590358460325329f,
+ 0.0591353124519920f, 0.0592347182199747f, 0.0593340633386119f,
+ 0.0594333478100472f, 0.0595325716364361f, 0.0596317348199464f,
+ 0.0597308373627584f, 0.0598298792670645f, 0.0599288605350693f,
+ 0.0600277811689900f, 0.0601266411710558f, 0.0602254405435084f,
+ 0.0603241792886015f, 0.0604228574086014f, 0.0605214749057863f,
+ 0.0606200317824470f, 0.0607185280408865f, 0.0608169636834202f,
+ 0.0609153387123754f, 0.0610136531300921f, 0.0611119069389223f,
+ 0.0612101001412306f, 0.0613082327393934f, 0.0614063047357998f,
+ 0.0615043161328510f, 0.0616022669329606f, 0.0617001571385542f,
+ 0.0617979867520701f, 0.0618957557759585f, 0.0619934642126822f,
+ 0.0620911120647159f, 0.0621886993345471f, 0.0622862260246751f,
+ 0.0623836921376117f, 0.0624810976758809f, 0.0625784426420190f,
+ 0.0626757270385749f, 0.0627729508681091f, 0.0628701141331951f,
+ 0.0629672168364182f, 0.0630642589803762f, 0.0631612405676790f,
+ 0.0632581616009490f, 0.0633550220828208f, 0.0634518220159413f,
+ 0.0635485614029694f, 0.0636452402465769f, 0.0637418585494472f,
+ 0.0638384163142765f, 0.0639349135437730f, 0.0640313502406572f,
+ 0.0641277264076620f, 0.0642240420475324f, 0.0643202971630261f,
+ 0.0644164917569124f, 0.0645126258319735f, 0.0544048884683912f,
+ 0.0545009015141968f, 0.0545968540495967f, 0.0546927460774221f,
+ 0.0547885776005161f, 0.0548843486217346f, 0.0549800591439455f,
+ 0.0550757091700289f, 0.0551712987028775f, 0.0552668277453958f,
+ 0.0553622963005011f, 0.0554577043711226f, 0.0555530519602019f,
+ 0.0556483390706931f, 0.0557435657055621f, 0.0558387318677877f,
+ 0.0559338375603603f, 0.0560288827862831f, 0.0561238675485714f,
+ 0.0562187918502528f, 0.0563136556943673f, 0.0564084590839668f,
+ 0.0565032020221159f, 0.0565978845118913f, 0.0566925065563819f,
+ 0.0567870681586891f, 0.0568815693219265f, 0.0569760100492198f,
+ 0.0570703903437071f, 0.0571647102085391f, 0.0572589696468781f,
+ 0.0573531686618994f, 0.0574473072567899f, 0.0575413854347496f,
+ 0.0576354031989899f, 0.0577293605527351f, 0.0578232574992215f,
+ 0.0579170940416977f, 0.0580108701834249f, 0.0581045859276761f,
+ 0.0581982412777368f, 0.0582918362369048f, 0.0583853708084903f,
+ 0.0584788449958155f, 0.0585722588022151f, 0.0586656122310360f,
+ 0.0587589052856374f, 0.0588521379693908f, 0.0589453102856798f,
+ 0.0590384222379006f, 0.0591314738294614f, 0.0592244650637830f,
+ 0.0593173959442981f, 0.0594102664744519f, 0.0595030766577018f,
+ 0.0595958264975176f, 0.0596885159973813f, 0.0597811451607873f,
+ 0.0598737139912419f, 0.0599662224922641f, 0.0498548597447726f,
+ 0.0499472475975357f, 0.0500395751314966f, 0.0501318423502235f,
+ 0.0502240492572963f, 0.0503161958563079f, 0.0504082821508629f,
+ 0.0505003081445783f, 0.0505922738410838f, 0.0506841792440209f,
+ 0.0507760243570434f, 0.0508678091838178f, 0.0509595337280224f,
+ 0.0510511979933482f, 0.0511428019834981f, 0.0512343457021875f,
+ 0.0513258291531440f, 0.0514172523401077f, 0.0515086152668306f,
+ 0.0515999179370773f, 0.0516911603546245f, 0.0517823425232611f,
+ 0.0518734644467888f, 0.0519645261290209f, 0.0520555275737834f,
+ 0.0521464687849144f, 0.0522373497662645f, 0.0523281705216963f,
+ 0.0524189310550848f, 0.0525096313703174f, 0.0526002714712936f,
+ 0.0526908513619254f, 0.0527813710461367f, 0.0528718305278641f,
+ 0.0529622298110562f, 0.0530525688996742f, 0.0531428477976911f,
+ 0.0532330665090925f, 0.0533232250378763f, 0.0534133233880527f,
+ 0.0535033615636440f, 0.0535933395686848f, 0.0536832574072221f,
+ 0.0537731150833152f, 0.0538629126010356f, 0.0539526499644671f,
+ 0.0540423271777057f, 0.0541319442448598f, 0.0542215011700501f,
+ 0.0543109979574096f, 0.0544004346110833f, 0.0544898111352288f,
+ 0.0545791275340160f, 0.0546683838116266f, 0.0547575799722552f,
+ 0.0548467160201085f, 0.0549357919594052f, 0.0448209968717641f,
+ 0.0449099526066536f, 0.0449988482457168f, 0.0450876837932221f,
+ 0.0451764592534494f, 0.0452651746306915f, 0.0453538299292532f,
+ 0.0454424251534517f, 0.0455309603076164f, 0.0456194353960890f,
+ 0.0457078504232236f, 0.0457962053933864f, 0.0458845003109558f,
+ 0.0459727351803229f, 0.0460609100058907f, 0.0461490247920745f,
+ 0.0462370795433022f, 0.0463250742640136f, 0.0464130089586610f,
+ 0.0465008836317089f, 0.0465886982876342f, 0.0466764529309258f,
+ 0.0467641475660853f, 0.0468517821976262f, 0.0469393568300745f,
+ 0.0470268714679684f, 0.0471143261158584f, 0.0472017207783073f,
+ 0.0472890554598900f, 0.0473763301651941f, 0.0474635448988190f,
+ 0.0475506996653768f, 0.0476377944694915f, 0.0477248293157998f,
+ 0.0478118042089502f, 0.0478987191536039f, 0.0479855741544340f,
+ 0.0480723692161264f, 0.0481591043433788f, 0.0482457795409013f,
+ 0.0483323948134164f, 0.0484189501656588f, 0.0485054456023757f,
+ 0.0485918811283262f, 0.0486782567482818f, 0.0487645724670264f,
+ 0.0488508282893562f, 0.0489370242200796f, 0.0490231602640172f,
+ 0.0491092364260021f, 0.0491952527108794f, 0.0492812091235068f,
+ 0.0493671056687539f, 0.0494529423515031f, 0.0495387191766484f,
+ 0.0496244361490969f, 0.0395062823511546f, 0.0395918796329782f,
+ 0.0396774170768982f, 0.0397628946878707f, 0.0398483124708638f,
+ 0.0399336704308575f, 0.0400189685728449f, 0.0401042069018306f,
+ 0.0401893854228319f, 0.0402745041408782f, 0.0403595630610115f,
+ 0.0404445621882854f, 0.0405295015277667f, 0.0406143810845336f,
+ 0.0406992008636773f, 0.0407839608703008f, 0.0408686611095195f,
+ 0.0409533015864613f, 0.0410378823062660f, 0.0411224032740861f,
+ 0.0412068644950861f, 0.0412912659744427f, 0.0413756077173453f,
+ 0.0414598897289950f, 0.0415441120146058f, 0.0416282745794035f,
+ 0.0417123774286264f, 0.0417964205675252f, 0.0418804040013625f,
+ 0.0419643277354134f, 0.0420481917749655f, 0.0421319961253183f,
+ 0.0422157407917838f, 0.0422994257796863f, 0.0423830510943621f,
+ 0.0424666167411601f, 0.0425501227254416f, 0.0426335690525796f,
+ 0.0427169557279601f, 0.0428002827569807f, 0.0428835501450517f,
+ 0.0429667578975957f, 0.0430499060200474f, 0.0431329945178538f,
+ 0.0432160233964742f, 0.0432989926613803f, 0.0433819023180559f,
+ 0.0434647523719972f, 0.0435475428287129f, 0.0436302736937234f,
+ 0.0437129449725618f, 0.0437955566707733f, 0.0438781087939159f,
+ 0.0439606013475590f, 0.0440430343372850f, 0.0441254077686882f,
+ 0.0442077216473753f, 0.0340861650563528f, 0.0341683598464772f,
+ 0.0342504951007791f, 0.0343325708249148f, 0.0344145870245521f,
+ 0.0344965437053716f, 0.0345784408730658f, 0.0346602785333397f,
+ 0.0347420566919107f, 0.0348237753545080f, 0.0349054345268737f,
+ 0.0349870342147618f, 0.0350685744239386f, 0.0351500551601828f,
+ 0.0352314764292854f, 0.0353128382370495f, 0.0353941405892905f,
+ 0.0354753834918364f, 0.0355565669505271f, 0.0356376909712150f,
+ 0.0357187555597648f, 0.0357997607220532f, 0.0358807064639694f,
+ 0.0359615927914151f, 0.0360424197103039f, 0.0361231872265616f,
+ 0.0362038953461269f, 0.0362845440749502f, 0.0363651334189943f,
+ 0.0364456633842345f, 0.0365261339766582f, 0.0366065452022649f,
+ 0.0366868970670670f, 0.0367671895770884f, 0.0368474227383658f,
+ 0.0369275965569481f, 0.0370077110388962f, 0.0370877661902839f,
+ 0.0371677620171965f, 0.0372476985257322f, 0.0373275757220012f,
+ 0.0374073936121259f, 0.0374871522022413f, 0.0375668514984943f,
+ 0.0376464915070444f, 0.0377260722340632f, 0.0378055936857347f,
+ 0.0378850558682551f, 0.0379644587878328f, 0.0380438024506888f,
+ 0.0381230868630559f, 0.0382023120311796f, 0.0382814779613175f,
+ 0.0383605846597395f, 0.0384396321327278f, 0.0385186203865768f,
+ 0.0385975494275934f, 0.0386764192620965f, 0.0387552298964174f,
+ 0.0388339813368998f, 0.0389126735898995f, 0.0287874957391723f,
+ 0.0288660696363234f, 0.0289445843651332f, 0.0290230399320067f,
+ 0.0291014363433610f, 0.0291797736056260f, 0.0292580517252432f,
+ 0.0293362707086670f, 0.0294144305623635f, 0.0294925312928117f,
+ 0.0295705729065025f, 0.0296485554099390f, 0.0297264788096369f,
+ 0.0298043431121241f, 0.0298821483239404f, 0.0299598944516384f,
+ 0.0300375815017827f, 0.0301152094809503f, 0.0301927783957304f,
+ 0.0302702882527243f, 0.0303477390585462f, 0.0304251308198219f,
+ 0.0305024635431897f, 0.0305797372353004f, 0.0306569519028170f,
+ 0.0307341075524143f, 0.0308112041907801f, 0.0308882418246141f,
+ 0.0309652204606283f, 0.0310421401055471f, 0.0311190007661070f,
+ 0.0311958024490569f, 0.0312725451611581f, 0.0313492289091839f,
+ 0.0314258536999200f, 0.0315024195401646f, 0.0315789264367278f,
+ 0.0316553743964323f, 0.0317317634261129f, 0.0318080935326168f,
+ 0.0318843647228033f, 0.0319605770035441f, 0.0320367303817234f,
+ 0.0321128248642373f, 0.0321888604579942f, 0.0322648371699152f,
+ 0.0323407550069333f, 0.0324166139759939f, 0.0324924140840545f,
+ 0.0325681553380854f, 0.0326438377450685f, 0.0327194613119984f,
+ 0.0327950260458821f, 0.0328705319537385f, 0.0329459790425989f,
+ 0.0330213673195070f, 0.0330966967915189f, 0.0331719674657026f,
+ 0.0332471793491386f, 0.0333223324489197f, 0.0333974267721510f,
+ 0.0334724623259497f, 0.0335474391174456f, 0.0336223571537804f,
+ 0.0336972164421084f, 0.0337720169895960f, 0.0338467588034219f,
+ 0.0237176309681649f, 0.0237922553362531f, 0.0238668209922893f,
+ 0.0239413279435017f, 0.0240157761971304f, 0.0240901657604278f,
+ 0.0241644966406585f, 0.0242387688450997f, 0.0243129823810404f,
+ 0.0243871372557825f, 0.0244612334766396f, 0.0245352710509378f,
+ 0.0246092499860156f, 0.0246831702892237f, 0.0247570319679249f,
+ 0.0248308350294947f, 0.0249045794813202f, 0.0249782653308016f,
+ 0.0250518925853510f, 0.0251254612523924f, 0.0251989713393627f,
+ 0.0252724228537110f, 0.0253458158028981f, 0.0254191501943977f,
+ 0.0254924260356957f, 0.0255656433342901f, 0.0256388020976910f,
+ 0.0257119023334213f, 0.0257849440490158f, 0.0258579272520216f,
+ 0.0259308519499983f, 0.0260037181505175f, 0.0260765258611635f,
+ 0.0261492750895322f, 0.0262219658432325f, 0.0262945981298852f,
+ 0.0263671719571235f, 0.0264396873325928f, 0.0265121442639506f,
+ 0.0265845427588672f, 0.0266568828250247f, 0.0267291644701179f,
+ 0.0268013877018532f, 0.0268735525279502f, 0.0269456589561400f,
+ 0.0270177069941663f, 0.0270896966497854f, 0.0271616279307651f,
+ 0.0272335008448862f, 0.0273053153999414f, 0.0273770716037359f,
+ 0.0274487694640868f, 0.0275204089888242f, 0.0275919901857897f,
+ 0.0276635130628376f, 0.0277349776278345f, 0.0278063838886591f,
+ 0.0278777318532024f, 0.0279490215293680f, 0.0280202529250712f,
+ 0.0280914260482401f, 0.0281625409068147f, 0.0282335975087478f,
+ 0.0283045958620039f, 0.0283755359745601f, 0.0284464178544057f,
+ 0.0285172415095424f, 0.0285880069479839f, 0.0286587141777566f,
+ 0.0287293632068986f, 0.0287999540434609f, 0.0288704866955066f,
+ 0.0289409611711107f, 0.0290113774783608f, 0.0290817356253569f,
+ 0.0291520356202110f, 0.0292222774710476f, 0.0292924611860035f,
+ 0.0293625867732274f, 0.0294326542408808f, 0.0295026635971371f,
+ 0.0295726148501822f, 0.0194386970856016f, 0.0195085321568307f,
+ 0.0195783091494799f, 0.0196480280717839f, 0.0197176889319899f,
+ 0.0197872917383575f, 0.0198568364991585f, 0.0199263232226770f,
+ 0.0199957519172094f, 0.0200651225910641f, 0.0201344352525623f,
+ 0.0202036899100369f, 0.0202728865718336f, 0.0203420252463102f,
+ 0.0204111059418366f, 0.0204801286667952f, 0.0205490934295804f,
+ 0.0206180002385994f, 0.0206868491022712f, 0.0207556400290274f,
+ 0.0208243730273115f, 0.0208930481055796f, 0.0209616652723001f,
+ 0.0210302245359534f, 0.0210987259050326f, 0.0211671693880426f,
+ 0.0212355549935009f, 0.0213038827299372f, 0.0213721526058936f,
+ 0.0214403646299242f, 0.0215085188105956f, 0.0215766151564867f,
+ 0.0216446536761885f, 0.0217126343783045f, 0.0217805572714503f,
+ 0.0218484223642538f, 0.0219162296653552f, 0.0219839791834073f,
+ 0.0220516709270747f, 0.0221193049050345f, 0.0221868811259760f,
+ 0.0222543995986009f, 0.0223218603316232f, 0.0223892633337690f,
+ 0.0224566086137768f, 0.0225238961803974f, 0.0225911260423939f,
+ 0.0226582982085414f, 0.0227254126876278f, 0.0227924694884529f,
+ 0.0228594686198288f, 0.0229264100905801f, 0.0229932939095435f,
+ 0.0230601200855678f, 0.0231268886275146f, 0.0231935995442573f,
+ 0.0232602528446819f, 0.0233268485376864f, 0.0233933866321813f,
+ 0.0234598671370893f, 0.0235262900613454f, 0.0235926554138970f,
+ 0.0236589632037034f, 0.0237252134397367f, 0.0237914061309807f,
+ 0.0238575412864321f, 0.0239236189150995f, 0.0239896390260038f,
+ 0.0240556016281782f, 0.0241215067306683f, 0.0241873543425319f,
+ 0.0242531444728392f, 0.0243188771306724f, 0.0243845523251262f,
+ 0.0244501700653075f, 0.0245157303603356f, 0.0245812332193420f,
+ 0.0246466786514703f, 0.0247120666658767f, 0.0247773972717296f,
+ 0.0248426704782095f, 0.0249078862945094f, 0.0249730447298344f,
+ 0.0250381457934021f, 0.0251031894944420f, 0.0251681758421963f,
+ 0.0252331048459193f, 0.0252979765148776f, 0.0253627908583501f,
+ 0.0254275478856277f, 0.0254922476060143f, 0.0255568900288252f,
+ 0.0256214751633887f, 0.0256860030190448f, 0.0257504736051463f,
+ 0.0258148869310579f, 0.0258792430061567f, 0.0259435418398322f,
+ 0.0260077834414862f, 0.0260719678205325f, 0.0261360949863972f,
+ 0.0262001649485192f, 0.0262641777163491f, 0.0263281332993501f,
+ 0.0263920317069973f, 0.0264558729487787f, 0.0265196570341941f,
+ 0.0265833839727558f, 0.0266470537739881f, 0.0267106664474281f,
+ 0.0267742220026244f, 0.0268377204491388f, 0.0269011617965448f,
+ 0.0269645460544282f, 0.0270278732323873f, 0.0270911433400325f,
+ 0.0271543563869866f, 0.0272175123828847f, 0.0272806113373739f,
+ 0.0273436532601140f, 0.0274066381607769f, 0.0274695660490466f,
+ 0.0275324369346197f, 0.0275952508272048f, 0.0276580077365231f,
+ 0.0277207076723076f, 0.0277833506443041f, 0.0278459366622703f,
+ 0.0279084657359765f, 0.0279709378752049f, 0.0280333530897504f,
+ 0.0280957113894200f, 0.0281580127840327f, 0.0282202572834203f,
+ 0.0180786339748140f, 0.0181407647132949f, 0.0182028385861189f,
+ 0.0182648556031667f, 0.0183268157743312f, 0.0183887191095176f,
+ 0.0184505656186436f, 0.0185123553116388f, 0.0185740881984453f,
+ 0.0186357642890174f, 0.0186973835933219f, 0.0187589461213378f,
+ 0.0188204518830560f, 0.0188819008884801f, 0.0189432931476259f,
+ 0.0190046286705214f, 0.0190659074672070f, 0.0191271295477353f,
+ 0.0191882949221711f, 0.0192494036005917f, 0.0193104555930864f,
+ 0.0193714509097570f, 0.0194323895607176f, 0.0194932715560944f,
+ 0.0195540969060260f, 0.0196148656206632f, 0.0196755777101692f,
+ 0.0197362331847194f, 0.0197968320545017f, 0.0198573743297157f,
+ 0.0199178600205741f, 0.0199782891373010f, 0.0200386616901335f,
+ 0.0200989776893208f, 0.0201592371451241f, 0.0202194400678171f,
+ 0.0202795864676859f, 0.0203396763550284f, 0.0203997097401555f,
+ 0.0204596866333899f, 0.0205196070450666f, 0.0205794709855330f,
+ 0.0206392784651486f, 0.0206990294942856f, 0.0207587240833281f,
+ 0.0208183622426724f, 0.0208779439827276f, 0.0209374693139144f,
+ 0.0209969382466666f, 0.0210563507914293f, 0.0211157069586608f,
+ 0.0211750067588309f, 0.0212342502024225f, 0.0212934372999299f,
+ 0.0213525680618605f, 0.0214116424987335f, 0.0214706606210802f,
+ 0.0215296224394448f, 0.0215885279643833f, 0.0216473772064642f,
+ 0.0217061701762682f, 0.0217649068843883f, 0.0218235873414297f,
+ 0.0218822115580100f, 0.0219407795447591f, 0.0219992913123190f,
+ 0.0220577468713443f, 0.0221161462325014f, 0.0221744894064695f,
+ 0.0222327764039397f, 0.0222910072356156f, 0.0223491819122130f,
+ 0.0224073004444599f, 0.0224653628430968f, 0.0225233691188765f,
+ 0.0225813192825635f, 0.0226392133449354f, 0.0226970513167814f,
+ 0.0227548332089035f, 0.0228125590321158f, 0.0228702287972445f,
+ 0.0229278425151282f, 0.0229854001966177f, 0.0230429018525766f,
+ 0.0231003474938801f, 0.0231577371314159f, 0.0232150707760841f,
+ 0.0232723484387969f, 0.0233295701304792f, 0.0233867358620675f,
+ 0.0234438456445113f, 0.0235008994887718f, 0.0235578974058227f,
+ 0.0236148394066502f, 0.0236717255022525f, 0.0237285557036400f,
+ 0.0237853300218359f, 0.0238420484678749f, 0.0238987110528048f,
+ 0.0239553177876850f, 0.0240118686835876f, 0.0240683637515969f,
+ 0.0241248030028093f, 0.0241811864483337f, 0.0242375140992912f,
+ 0.0242937859668152f, 0.0243500020620512f, 0.0244061623961573f,
+ 0.0244622669803039f, 0.0245183158256730f, 0.0245743089434599f,
+ 0.0246302463448713f, 0.0246861280411268f, 0.0247419540434580f,
+ 0.0247977243631086f, 0.0248534390113350f, 0.0249090979994056f,
+ 0.0249647013386012f, 0.0250202490402147f, 0.0250757411155516f,
+ 0.0251311775759295f, 0.0251865584326782f, 0.0252418836971398f,
+ 0.0252971533806688f, 0.0253523674946320f, 0.0254075260504084f,
+ 0.0254626290593893f, 0.0255176765329782f, 0.0255726684825909f,
+ 0.0256276049196556f, 0.0256824858556128f, 0.0257373113019150f,
+ 0.0257920812700275f, 0.0258467957714273f, 0.0259014548176039f,
+ 0.0259560584200594f, 0.0260106065903076f, 0.0260650993398752f,
+ 0.0261195366803005f, 0.0261739186231348f, 0.0262282451799411f,
+ 0.0262825163622949f, 0.0263367321817843f, 0.0263908926500090f,
+ 0.0264449977785816f, 0.0264990475791265f, 0.0265530420632809f,
+ 0.0266069812426939f, 0.0266608651290270f, 0.0267146937339538f,
+ 0.0267684670691607f, 0.0268221851463456f, 0.0268758479772194f,
+ 0.0269294555735048f, 0.0269830079469371f, 0.0270365051092639f,
+ 0.0270899470722445f, 0.0271433338476514f, 0.0271966654472685f,
+ 0.0272499418828926f, 0.0273031631663327f, 0.0273563293094096f,
+ 0.0274094403239570f, 0.0274624962218204f, 0.0275154970148579f,
+ 0.0275684427149397f, 0.0276213333339486f, 0.0276741688837791f,
+ 0.0277269493763384f, 0.0277796748235459f, 0.0278323452373335f,
+ 0.0278849606296448f, 0.0279375210124363f, 0.0279900263976765f,
+ 0.0280424767973460f, 0.0280948722234382f, 0.0281472126879581f,
+ 0.0281994982029236f, 0.0282517287803646f, 0.0283039044323233f,
+ 0.0283560251708540f, 0.0284080910080237f, 0.0284601019559115f,
+ 0.0285120580266086f, 0.0285639592322186f, 0.0286158055848575f,
+ 0.0286675970966534f, 0.0287193337797467f, 0.0287710156462904f,
+ 0.0288226427084491f, 0.0288742149784006f, 0.0289257324683340f,
+ 0.0289771951904517f, 0.0290286031569674f, 0.0290799563801078f,
+ 0.0291312548721115f, 0.0291824986452294f, 0.0292336877117249f,
+ 0.0292848220838736f, 0.0293359017739633f, 0.0293869267942941f,
+ 0.0294378971571783f, 0.0294888128749408f, 0.0295396739599185f,
+ 0.0295904804244605f, 0.0296412322809285f, 0.0296919295416962f,
+ 0.0297425722191498f, 0.0297931603256875f, 0.0298436938737202f,
+ 0.0298941728756706f, 0.0299445973439740f, 0.0299949672910779f,
+ 0.0300452827294422f, 0.0300955436715389f, 0.0301457501298522f,
+ 0.0301959021168788f, 0.0302459996451278f, 0.0302960427271200f,
+ 0.0303460313753892f, 0.0303959656024811f, 0.0304458454209537f,
+ 0.0304956708433771f, 0.0305454418823342f, 0.0305951585504198f,
+ 0.0306448208602409f, 0.0306944288244172f, 0.0307439824555803f,
+ 0.0307934817663740f, 0.0308429267694549f, 0.0308923174774914f,
+ 0.0309416539031645f, 0.0309909360591671f, 0.0310401639582047f,
+ 0.0310893376129952f, 0.0311384570362682f, 0.0311875222407662f,
+ 0.0312365332392437f, 0.0312854900444675f, 0.0313343926692167f,
+ 0.0313832411262826f, 0.0314320354284691f, 0.0314807755885919f,
+ 0.0315294616194793f, 0.0315780935339718f, 0.0316266713449223f,
+ 0.0316751950651957f, 0.0317236647076694f, 0.0317720802852332f,
+ 0.0318204418107888f, 0.0318687492972504f, 0.0319170027575447f,
+ 0.0319652022046102f, 0.0320133476513981f, 0.0320614391108717f,
+ 0.0321094765960065f, 0.0321574601197905f, 0.0322053896952238f,
+ 0.0322532653353189f, 0.0323010870531006f, 0.0323488548616057f,
+ 0.0323965687738836f, 0.0324442288029959f, 0.0324918349620166f,
+ 0.0325393872640315f, 0.0325868857221393f, 0.0326343303494504f,
+ 0.0326817211590881f, 0.0327290581641876f, 0.0327763413778963f,
+ 0.0328235708133741f, 0.0328707464837932f, 0.0329178684023379f,
+ 0.0329649365822048f, 0.0330119510366030f, 0.0330589117787536f,
+ 0.0331058188218902f, 0.0331526721792586f, 0.0331994718641168f,
+ 0.0332462178897352f, 0.0332929102693964f, 0.0333395490163956f,
+ 0.0333861341440396f, 0.0334326656656481f, 0.0334791435945528f,
+ 0.0335255679440978f, 0.0335719387276394f, 0.0336182559585463f,
+ 0.0336645196501992f, 0.0337107298159914f, 0.0337568864693283f,
+ 0.0338029896236278f, 0.0338490392923198f, 0.0338950354888466f,
+ 0.0339409782266627f, 0.0339868675192350f, 0.0340327033800429f,
+ 0.0340784858225774f, 0.0341242148603425f, 0.0341698905068542f,
+ 0.0342155127756406f, 0.0342610816802423f, 0.0343065972342121f,
+ 0.0343520594511153f, 0.0343974683445292f, 0.0344428239280434f,
+ 0.0344881262152600f, 0.0345333752197932f, 0.0345785709552694f,
+ 0.0346237134353274f, 0.0346688026736185f, 0.0347138386838059f,
+ 0.0347588214795652f, 0.0348037510745845f, 0.0348486274825640f,
+ 0.0348934507172161f, 0.0349382207922655f, 0.0349829377214494f,
+ 0.0350276015185171f, 0.0350722121972303f, 0.0351167697713627f,
+ 0.0351612742547006f, 0.0352057256610425f, 0.0352501240041991f,
+ 0.0352944692979934f, 0.0353387615562609f, 0.0353830007928488f,
+ 0.0354271870216173f, 0.0354713202564384f, 0.0355154005111967f,
+ 0.0355594277997888f, 0.0356034021361236f, 0.0356473235341225f,
+ 0.0356911920077191f, 0.0357350075708592f, 0.0357787702375008f,
+ 0.0358224800216146f, 0.0256623260145703f, 0.0257059300755882f,
+ 0.0257494812960632f, 0.0257929796900145f, 0.0258364252714742f,
+ 0.0258798180544862f, 0.0259231580531069f, 0.0259664452814049f,
+ 0.0260096797534614f, 0.0260528614833693f, 0.0260959904852343f,
+ 0.0261390667731740f, 0.0261820903613187f, 0.0262250612638107f,
+ 0.0262679794948044f, 0.0263108450684670f, 0.0263536579989775f,
+ 0.0263964183005274f, 0.0264391259873206f, 0.0264817810735729f,
+ 0.0265243835735127f, 0.0265669335013807f, 0.0266094308714296f,
+ 0.0266518756979248f, 0.0266942679951436f, 0.0267366077773757f,
+ 0.0267788950589231f, 0.0268211298541002f, 0.0268633121772335f,
+ 0.0269054420426618f, 0.0269475194647364f, 0.0269895444578204f,
+ 0.0270315170362898f, 0.0270734372145324f, 0.0271153050069485f,
+ 0.0271571204279507f, 0.0271988834919638f, 0.0272405942134248f,
+ 0.0272822526067832f, 0.0273238586865006f, 0.0273654124670510f,
+ 0.0274069139629206f, 0.0274483631886078f, 0.0274897601586236f,
+ 0.0275311048874910f, 0.0275723973897453f, 0.0276136376799340f,
+ 0.0276548257726174f, 0.0276959616823674f, 0.0277370454237685f,
+ 0.0277780770114174f, 0.0278190564599235f, 0.0278599837839078f,
+ 0.0279008589980039f, 0.0279416821168578f, 0.0279824531551276f,
+ 0.0280231721274838f, 0.0280638390486091f, 0.0281044539331986f,
+ 0.0281450167959595f, 0.0281855276516114f, 0.0282259865148862f,
+ 0.0282663934005280f, 0.0283067483232932f, 0.0283470512979505f,
+ 0.0283873023392809f, 0.0284275014620776f, 0.0284676486811463f,
+ 0.0285077440113047f, 0.0285477874673831f, 0.0285877790642237f,
+ 0.0286277188166811f, 0.0286676067396224f, 0.0287074428479268f,
+ 0.0287472271564859f, 0.0287869596802033f, 0.0288266404339953f,
+ 0.0288662694327901f, 0.0289058466915284f, 0.0289453722251632f,
+ 0.0289848460486596f, 0.0290242681769951f, 0.0290636386251596f,
+ 0.0291029574081548f, 0.0291422245409955f, 0.0291814400387080f,
+ 0.0292206039163312f, 0.0292597161889165f, 0.0292987768715272f,
+ 0.0293377859792390f, 0.0293767435271399f, 0.0294156495303304f,
+ 0.0294545040039229f, 0.0294933069630424f, 0.0295320584228259f,
+ 0.0295707583984229f, 0.0296094069049953f, 0.0296480039577166f,
+ 0.0296865495717735f, 0.0297250437623645f, 0.0297634865447003f,
+ 0.0298018779340040f, 0.0298402179455111f, 0.0298785065944694f,
+ 0.0299167438961386f, 0.0299549298657911f, 0.0299930645187115f,
+ 0.0300311478701964f, 0.0300691799355550f, 0.0301071607301086f,
+ 0.0301450902691910f, 0.0301829685681479f, 0.0302207956423377f,
+ 0.0302585715071309f, 0.0302962961779103f, 0.0303339696700707f,
+ 0.0303715919990197f, 0.0304091631801768f, 0.0304466832289741f,
+ 0.0304841521608554f, 0.0305215699912776f, 0.0305589367357091f,
+ 0.0305962524096311f, 0.0306335170285369f, 0.0306707306079320f,
+ 0.0307078931633345f, 0.0307450047102743f, 0.0307820652642940f,
+ 0.0308190748409481f, 0.0308560334558039f, 0.0308929411244405f,
+ 0.0309297978624495f, 0.0309666036854346f, 0.0310033586090122f,
+ 0.0310400626488105f, 0.0310767158204703f, 0.0311133181396446f,
+ 0.0311498696219986f, 0.0311863702832097f, 0.0210190092163552f,
+ 0.0210554082823626f, 0.0210917565743333f, 0.0211280541079942f,
+ 0.0211643008990843f, 0.0212004969633546f, 0.0212366423165687f,
+ 0.0212727369745025f, 0.0213087809529439f, 0.0213447742676934f,
+ 0.0213807169345635f, 0.0214166089693792f, 0.0214524503879776f,
+ 0.0214882412062082f, 0.0215239814399328f, 0.0215596711050254f,
+ 0.0215953102173724f, 0.0216308987928723f, 0.0216664368474359f,
+ 0.0217019243969867f, 0.0217373614574597f, 0.0217727480448030f,
+ 0.0218080841749764f, 0.0218433698639522f, 0.0218786051277151f,
+ 0.0219137899822618f, 0.0219489244436015f, 0.0219840085277557f,
+ 0.0220190422507578f, 0.0220540256286542f, 0.0220889586775028f,
+ 0.0221238414133744f, 0.0221586738523517f, 0.0221934560105297f,
+ 0.0222281879040160f, 0.0222628695489301f, 0.0222975009614039f,
+ 0.0223320821575819f, 0.0223666131536204f, 0.0224010939656882f,
+ 0.0224355246099665f, 0.0224699051026486f, 0.0225042354599400f,
+ 0.0225385156980587f, 0.0225727458332349f, 0.0226069258817113f,
+ 0.0226410558597423f, 0.0226751357835951f, 0.0227091656695491f,
+ 0.0227431455338958f, 0.0227770753929393f, 0.0228109552629954f,
+ 0.0228447851603930f, 0.0228785651014724f, 0.0229122951025869f,
+ 0.0229459751801015f, 0.0229796053503942f, 0.0230131856298546f,
+ 0.0230467160348848f, 0.0230801965818993f, 0.0231136272873249f,
+ 0.0231470081676005f, 0.0231803392391772f, 0.0232136205185188f,
+ 0.0232468520221011f, 0.0232800337664121f, 0.0233131657679523f,
+ 0.0233462480432343f, 0.0233792806087829f, 0.0234122634811357f,
+ 0.0234451966768419f, 0.0234780802124634f, 0.0235109141045745f,
+ 0.0235436983697613f, 0.0235764330246225f, 0.0236091180857689f,
+ 0.0236417535698240f, 0.0236743394934231f, 0.0237068758732141f,
+ 0.0237393627258569f, 0.0237718000680239f, 0.0238041879163996f,
+ 0.0238365262876811f, 0.0238688151985775f, 0.0239010546658102f,
+ 0.0239332447061131f, 0.0239653853362320f, 0.0239974765729254f,
+ 0.0240295184329637f, 0.0240615109331300f, 0.0240934540902192f,
+ 0.0241253479210389f, 0.0241571924424087f, 0.0241889876711606f,
+ 0.0242207336241391f, 0.0242524303182004f, 0.0242840777702138f,
+ 0.0243156759970598f, 0.0243472250156324f, 0.0243787248428368f,
+ 0.0244101754955914f, 0.0244415769908261f, 0.0244729293454835f,
+ 0.0245042325765185f, 0.0245354867008983f, 0.0245666917356021f,
+ 0.0245978476976215f, 0.0246289546039604f, 0.0246600124716354f,
+ 0.0246910213176745f, 0.0247219811591188f, 0.0247528920130213f,
+ 0.0247837538964472f, 0.0248145668264743f, 0.0248453308201923f,
+ 0.0248760458947036f, 0.0249067120671227f, 0.0249373293545762f,
+ 0.0249678977742031f, 0.0249984173431548f, 0.0148250771559823f,
+ 0.0148554990750867f, 0.0148858721950435f, 0.0149161965330530f,
+ 0.0149464721063281f, 0.0149766989320937f, 0.0150068770275872f,
+ 0.0150370064100582f, 0.0150670870967684f, 0.0150971191049919f,
+ 0.0151271024520153f, 0.0151570371551371f, 0.0151869232316685f,
+ 0.0152167606989325f, 0.0152465495742647f, 0.0152762898750130f,
+ 0.0153059816185374f, 0.0153356248222104f, 0.0153652195034166f,
+ 0.0153947656795529f, 0.0154242633680285f, 0.0154537125862650f,
+ 0.0154831133516962f, 0.0155124656817680f, 0.0155417695939390f,
+ 0.0155710251056796f, 0.0156002322344730f, 0.0156293909978140f,
+ 0.0156585014132103f, 0.0156875634981817f, 0.0157165772702601f,
+ 0.0157455427469900f, 0.0157744599459278f, 0.0158033288846426f,
+ 0.0158321495807153f, 0.0158609220517395f, 0.0158896463153209f,
+ 0.0159183223890776f, 0.0159469502906399f, 0.0159755300376501f,
+ 0.0160040616477634f, 0.0160325451386467f, 0.0160609805279795f,
+ 0.0160893678334535f, 0.0161177070727727f, 0.0161459982636532f,
+ 0.0161742414238237f, 0.0162024365710251f, 0.0162305837230104f,
+ 0.0162586828975450f, 0.0162867341124064f, 0.0163147373853849f,
+ 0.0163426927342824f, 0.0163706001769136f, 0.0163984597311053f,
+ 0.0164262714146964f, 0.0164540352455384f, 0.0164817512414950f,
+ 0.0165094194204420f, 0.0165370398002675f, 0.0165646123988723f,
+ 0.0165921372341689f, 0.0166196143240825f, 0.0166470436865503f,
+ 0.0166744253395220f, 0.0167017593009595f, 0.0167290455888369f,
+ 0.0167562842211408f, 0.0167834752158696f, 0.0168106185910347f,
+ 0.0168377143646592f, 0.0168647625547787f, 0.0168917631794411f,
+ 0.0169187162567065f, 0.0169456218046473f, 0.0169724798413484f,
+ 0.0169992903849065f, 0.0170260534534310f, 0.0170527690650435f,
+ 0.0170794372378779f, 0.0171060579900800f, 0.0171326313398085f,
+ 0.0171591573052340f, 0.0171856359045393f, 0.0172120671559199f,
+ 0.0172384510775832f, 0.0172647876877488f, 0.0172910770046491f,
+ 0.0173173190465285f, 0.0173435138316433f, 0.0173696613782627f,
+ 0.0173957617046678f, 0.0174218148291521f, 0.0174478207700215f,
+ 0.0174737795455938f, 0.0174996911741995f, 0.0175255556741812f,
+ 0.0175513730638939f, 0.0175771433617046f, 0.0176028665859928f,
+ 0.0176285427551502f, 0.0176541718875811f, 0.0176797540017015f,
+ 0.0177052891159402f, 0.0177307772487379f, 0.0177562184185478f,
+ 0.0177816126438354f, 0.0178069599430784f, 0.0178322603347666f,
+ 0.0178575138374025f, 0.0178827204695006f, 0.0179078802495878f,
+ 0.0179329931962031f, 0.0179580593278980f, 0.0179830786632361f,
+ 0.0180080512207934f, 0.0180329770191581f, 0.0180578560769309f,
+ 0.0180826884127244f, 0.0181074740451639f, 0.0181322129928866f,
+ 0.00795309435192959f, 0.00797773998617998f, 0.00800233899169950f,
+ 0.00802689138717461f, 0.00805139719130404f, 0.00807585642279884f,
+ 0.00810026910038247f, 0.00812463524279045f, 0.00814895486877063f,
+ 0.00817322799708334f, 0.00819745464650101f, 0.00822163483580835f,
+ 0.00824576858380241f, 0.00826985590929247f, 0.00829389683110024f,
+ 0.00831789136805938f, 0.00834183953901635f, 0.00836574136282939f,
+ 0.00838959685836918f, 0.00841340604451885f, 0.00843716894017363f,
+ 0.00846088556424110f, 0.00848455593564104f, 0.00850818007330578f,
+ 0.00853175799617945f, 0.00855528972321903f, 0.00857877527339326f,
+ 0.00860221466568337f, 0.00862560791908315f, 0.00864895505259825f,
+ 0.00867225608524677f, 0.00869551103605912f, 0.00871871992407797f,
+ 0.00874188276835819f, 0.00876499958796712f, 0.00878807040198421f,
+ 0.00881109522950124f, 0.00883407408962228f, 0.00885700700146366f,
+ 0.00887989398415420f, 0.00890273505683453f, 0.00892553023865803f,
+ 0.00894827954879010f, 0.00897098300640842f, 0.00899364063070327f,
+ 0.00901625244087678f, 0.00903881845614354f, 0.00906133869573070f,
+ 0.00908381317887699f, 0.00910624192483434f, 0.00912862495286607f,
+ 0.00915096228224840f, 0.00917325393226956f, 0.00919549992223012f,
+ 0.00921770027144295f, 0.00923985499923324f, 0.00926196412493843f,
+ 0.00928402766790812f, 0.00930604564750417f, 0.00932801808310113f,
+ 0.00934994499408540f, 0.00937182639985590f, 0.00939366231982353f,
+ 0.00941545277341183f, 0.00943719778005636f, 0.00945889735920535f,
+ 0.00948055153031865f, 0.00950216031286910f, 0.00952372372634122f,
+ 0.00954524179023242f, 0.00956671452405170f, 0.00958814194732097f,
+ 0.00960952407957394f, 0.00963086094035709f, 0.00965215254922858f,
+ 0.00967339892575941f, 0.00969460008953246f, 0.00971575606014319f,
+ 0.00973686685719929f, 0.00975793250032039f, 0.00977895300913892f,
+ 0.00979992840329916f, 0.00982085870245789f, 0.00984174392628431f,
+ 0.00986258409445931f, 0.00988337922667690f, 0.00990412934264276f,
+ 0.00992483446207504f, 0.00994549460470412f, 0.00996610979027271f,
+ 0.00998668003853587f, 0.0100072053692609f, 0.0100276858022273f,
+ 0.0100481213572269f, 0.0100685120540638f, 0.0100888579125544f,
+ 0.0101091589525275f, 0.0101294151938238f, 0.0101496266562968f,
+ 0.0101697933598117f, 0.0101899153242468f, 0.0102099925694918f,
+ 0.0102300251154490f, 0.0102500129820334f, 0.0102699561891717f,
+ 0.0102898547568033f, 0.0103097087048794f, 0.0103295180533639f,
+ 0.0103492828222330f, 0.0103690030314749f, 0.0103886787010901f,
+ 0.0104083098510918f, 0.0104278965015051f, 0.0104474386723672f,
+ 0.0104669363837280f, 0.0104863896556498f, 0.0105057985082065f,
+ 0.0105251629614849f, 0.0105444830355838f, 0.0105637587506144f,
+ 0.0105829901267000f, 0.0106021771839766f, 0.0106213199425919f,
+ 0.0106404184227065f, 0.0106594726444926f, 0.0106784826281353f,
+ 0.0106974483938314f, 0.0107163699617908f, 0.0107352473522349f,
+ 0.0107540805853976f, 0.0107728696815252f, 0.0107916146608764f,
+ 0.0108103155437217f, 0.0108289723503445f, 0.0108475851010400f,
+ 0.0108661538161160f, 0.0108846785158922f, 0.0109031592207011f,
+ 0.0109215959508869f, 0.0109399887268067f, 0.0109583375688294f,
+ 0.0109766424973363f, 0.0109949035327211f, 0.0110131206953896f,
+ 0.0110312940057603f, 0.0110494234842632f, 0.000863698228728893f,
+ 0.000881740104837381f, 0.000899738210443285f, 0.000917692566026501f,
+ 0.000935603192078749f, 0.000953470109104099f, 0.000971293337619111f,
+ 0.000989072898152504f, 0.00100680881124518f, 0.00102450109745059f,
+ 0.00104214977733416f, 0.00105975487147383f, 0.00107731640045972f,
+ 0.00109483438489416f, 0.00111230884539187f, 0.00112973980257991f,
+ 0.00114712727709757f, 0.00116447128959615f, 0.00118177186073964f,
+ 0.00119902901120417f, 0.00121624276167817f, 0.00123341313286207f,
+ 0.00125054014546896f, 0.00126762382022416f, 0.00128466417786494f,
+ 0.00130166123914147f, 0.00131861502481540f, 0.00133552555566130f,
+ 0.00135239285246572f, 0.00136921693602776f, 0.00138599782715843f,
+ 0.00140273554668116f, 0.00141943011543194f, 0.00143608155425867f,
+ 0.00145268988402170f, 0.00146925512559357f, 0.00148577729985921f,
+ 0.00150225642771590f, 0.00151869253007300f, 0.00153508562785212f,
+ 0.00155143574198749f, 0.00156774289342521f, 0.00158400710312404f,
+ 0.00160022839205470f, 0.00161640678120040f, 0.00163254229155643f,
+ 0.00164863494413064f, 0.00166468475994314f, 0.00168069176002578f,
+ 0.00169665596542351f, 0.00171257739719283f, 0.00172845607640326f,
+ 0.00174429202413584f, 0.00176008526148430f, 0.00177583580955454f,
+ 0.00179154368946505f, 0.00180720892234626f, 0.00182283152934076f,
+ 0.00183841153160380f, 0.00185394895030278f, 0.00186944380661724f,
+ 0.00188489612173903f, 0.00190030591687257f, 0.00191567321323430f,
+ 0.00193099803205288f, 0.00194628039456934f, 0.00196152032203714f,
+ 0.00197671783572181f, 0.00199187295690129f, 0.00200698570686583f,
+ 0.00202205610691772f, 0.00203708417837167f, 0.00205206994255494f,
+ 0.00206701342080665f, 0.00208191463447846f, 0.00209677360493415f,
+ 0.00211159035354996f, 0.00212636490171420f, 0.00214109727082776f,
+ 0.00215578748230350f, 0.00217043555756677f, 0.00218504151805510f,
+ 0.00219960538521813f, 0.00221412718051822f, 0.00222860692542973f,
+ 0.00224304464143935f, 0.00225744035004599f, 0.00227179407276090f,
+ 0.00228610583110747f, 0.00230037564662183f, 0.00231460354085172f,
+ 0.00232878953535784f, 0.00234293365171256f, 0.00235703591150094f,
+ 0.00237109633632010f, 0.00238511494777985f, 0.00239909176750147f,
+ 0.00241302681711936f, 0.00242692011827975f, 0.00244077169264129f,
+ 0.00245458156187497f, 0.00246834974766388f, 0.00248207627170341f,
+ 0.00249576115570141f, 0.00250940442137795f, 0.00252300609046513f,
+ 0.00253656618470771f, 0.00255008472586260f, 0.00256356173569897f,
+ 0.00257699723599797f, 0.00259039124855365f, 0.00260374379517186f,
+ 0.00261705489767092f, 0.00263032457788148f, 0.00264355285764614f,
+ 0.00265673975882014f, 0.00266988530327097f, 0.00268298951287835f,
+ 0.00269605240953399f, 0.00270907401514245f, 0.00272205435162004f,
+ 0.00273499344089564f, 0.00274789130491049f, 0.00276074796561768f,
+ 0.00277356344498309f, 0.00278633776498463f, 0.00279907094761245f,
+ 0.00281176301486905f, 0.00282441398876931f, 0.00283702389134027f,
+ 0.00284959274462127f, 0.00286212057066382f, 0.00287460739153189f,
+ 0.00288705322930180f, 0.00289945810606193f, 0.00291182204391308f,
+ 0.00292414506496813f, 0.00293642719135262f, 0.00294866844520389f,
+ 0.00296086884867222f, 0.00297302842391931f, 0.00298514719311982f,
+ 0.00299722517846057f, 0.00300926240214039f, 0.00302125888637081f,
+ 0.00303321465337506f, 0.00304512972538920f, 0.00305700412466148f,
+ 0.00306883787345200f, 0.00308063099403366f, 0.00309238350869140f,
+ 0.00310409543972254f, 0.00311576680943643f, 0.00312739764015507f,
+ 0.00313898795421264f, 0.00315053777395527f, 0.00316204712174176f,
+ 0.00317351601994309f, 0.00318494449094245f, 0.00319633255713528f,
+ 0.00320768024092954f, 0.00321898756474520f, 0.00323025455101456f,
+ 0.00324148122218240f, 0.00325266760070547f, 0.00326381370905315f,
+ 0.00327491956970674f, 0.00328598520516016f, 0.00329701063791929f,
+ 0.00330799589050260f, 0.00331894098544075f, 0.00332984594527644f,
+ 0.00334071079256490f, 0.00335153554987361f, 0.00336232023978234f,
+ 0.00337306488488309f, 0.00338376950778002f, 0.00339443413108997f,
+ 0.00340505877744157f, 0.00341564346947612f, 0.00342618822984689f,
+ 0.00343669308121961f, 0.00344715804627233f, 0.00345758314769543f,
+ 0.00346796840819122f, 0.00347831385047465f, 0.00348861949727269f,
+ 0.00349888537132503f, 0.00350911149538313f, 0.00351929789221103f,
+ 0.00352944458458487f, 0.00353955159529323f, 0.00354961894713696f,
+ 0.00355964666292907f, 0.00356963476549479f, 0.00357958327767202f,
+ 0.00358949222231061f, 0.00359936162227278f, 0.00360919150043287f,
+ 0.00361898187967777f, 0.00362873278290654f, 0.00363844423303059f,
+ 0.00364811625297337f, 0.00365774886567080f, 0.00366734209407113f,
+ 0.00367689596113485f, 0.00368641048983448f, 0.00369588570315532f,
+ 0.00370532162409457f, 0.00371471827566178f, 0.00372407568087890f,
+ 0.00373339386277999f, 0.00374267284441140f, 0.00375191264883207f,
+ 0.00376111329911286f, 0.00377027481833711f, 0.00377939722960025f,
+ 0.00378848055601022f, 0.00379752482068707f, 0.00380653004676323f,
+ 0.00381549625738342f, 0.00382442347570458f, 0.00383331172489607f,
+ 0.00384216102813920f, 0.00385097140862786f, 0.00385974288956820f,
+ 0.00386847549417849f, 0.00387716924568945f, 0.00388582416734407f,
+ 0.00389444028239744f, 0.00390301761411704f, 0.00391155618578290f,
+ 0.00392005602068687f, 0.00392851714213324f, 0.00393693957343871f};
+
+// Curve which is scaled by |kCurveCorrectionMultipliers| and added to the curve
+// generated by the |kLowReverberationCorrectionCurve| polynomial, for
+// reverberation times which result in a feedback factor index less than
+// |kCurveChangeoverIndex|.
+static const float kLowCorrectionCurve[kCorrectionCurveLength] = {
+ 0.0355835132858260f, 0.0361471820669677f, 0.0367104001412021f,
+ 0.0372731677107544f, 0.0378354849778072f, 0.0383973521445001f,
+ 0.0389587694129315f, 0.0395197369851564f, 0.0400802550631881f,
+ 0.0406403238489973f, 0.0411999435445122f, 0.0417591143516188f,
+ 0.0423178364721608f, 0.0428761101079397f, 0.0434339354607135f,
+ 0.0439913127321995f, 0.0445482421240714f, 0.0451047238379613f,
+ 0.0456607580754581f, 0.0462163450381097f, 0.0467714849274197f,
+ 0.0473261779448504f, 0.0478804242918223f, 0.0484342241697123f,
+ 0.0489875777798562f, 0.0495404853235463f, 0.0500929470020328f,
+ 0.0506449630165243f, 0.0511965335681860f, 0.0517476588581411f,
+ 0.0522983390874708f, 0.0528485744572133f, 0.0533983651683647f,
+ 0.0539477114218787f, 0.0544966134186670f, 0.0550450713595981f,
+ 0.0555930854454990f, 0.0561406558771536f, 0.0566877828553037f,
+ 0.0572344665806487f, 0.0577807072538459f, 0.0583265050755101f,
+ 0.0588718602462131f, 0.0594167729664848f, 0.0599612434368130f,
+ 0.0605052718576424f, 0.0610488584293762f, 0.0615920033523746f,
+ 0.0621347068269552f, 0.0626769690533939f, 0.0632187902319240f,
+ 0.0637601705627360f, 0.0643011102459783f, 0.0648416094817569f,
+ 0.0653816684701354f, 0.0659212874111350f, 0.0664604665047346f,
+ 0.0669992059508706f, 0.0675375059494370f, 0.0680753667002852f,
+ 0.0686127884032250f, 0.0691497712580228f, 0.0696863154644029f,
+ 0.0702224212220476f, 0.0707580887305970f, 0.0712933181896472f,
+ 0.0718281097987538f, 0.0723624637574293f, 0.0728963802651431f,
+ 0.0734298595213236f, 0.0739629017253557f, 0.0744955070765820f,
+ 0.0750276757743035f, 0.0755594080177776f, 0.0760907040062205f,
+ 0.0766215639388050f, 0.0771519880146623f, 0.0776819764328806f,
+ 0.0782115293925059f, 0.0787406470925415f, 0.0792693297319489f,
+ 0.0797975775096472f, 0.0803253906245122f, 0.0808527692753781f,
+ 0.0813797136610364f, 0.0819062239802365f, 0.0824323004316849f,
+ 0.0829579432140462f, 0.0834831525259416f, 0.0840079285659512f,
+ 0.0845322715326120f, 0.0850561816244186f, 0.0855796590398233f,
+ 0.0861027039772357f, 0.0866253166350237f, 0.0871474972115118f,
+ 0.0876692459049832f, 0.0881905629136774f, 0.0887114484357927f,
+ 0.0892319026694841f, 0.0897519258128650f, 0.0902715180640052f,
+ 0.0907906796209331f, 0.0913094106816348f, 0.0918277114440527f,
+ 0.0923455821060883f, 0.0928630228655998f, 0.0933800339204031f,
+ 0.0938966154682718f, 0.0944127677069371f, 0.0949284908340879f,
+ 0.0954437850473701f, 0.0959586505443875f, 0.0964730875227018f,
+ 0.0969870961798317f, 0.0975006767132541f, 0.0980138293204033f,
+ 0.0985265541986706f, 0.0990388515454054f, 0.0995507215579148f,
+ 0.100062164433463f, 0.100573180369272f, 0.101083769562521f,
+ 0.101593932210348f, 0.102103668509848f, 0.102612978658072f,
+ 0.103121862852030f, 0.103630321288690f, 0.104138354164977f,
+ 0.104645961677773f, 0.105153144023918f, 0.105659901400211f,
+ 0.106166234003407f, 0.106672142030217f, 0.107177625677312f,
+ 0.107682685141321f, 0.108187320618829f, 0.108691532306379f,
+ 0.109195320400472f, 0.109698685097565f, 0.110201626594074f,
+ 0.110704145086373f, 0.111206240770793f, 0.111707913843621f,
+ 0.112209164501105f, 0.112709992939447f, 0.113210399354808f,
+ 0.113710383943307f, 0.114209946901020f, 0.114709088423982f,
+ 0.115207808708182f, 0.115706107949571f, 0.116203986344054f,
+ 0.116701444087495f, 0.117198481375716f, 0.117695098404497f,
+ 0.118191295369573f, 0.118687072466638f, 0.119182429891344f,
+ 0.119677367839301f, 0.120171886506076f, 0.120665986087191f,
+ 0.121159666778131f, 0.121652928774333f, 0.122145772271196f,
+ 0.122638197464072f, 0.123130204548276f, 0.123621793719075f,
+ 0.124112965171697f, 0.124603719101328f, 0.125094055703108f,
+ 0.125583975172139f, 0.126073477703476f, 0.126562563492136f,
+ 0.127051232733090f, 0.127539485621269f, 0.128027322351559f,
+ 0.128514743118806f, 0.129001748117814f, 0.129488337543340f,
+ 0.129974511590103f, 0.130460270452779f, 0.130945614326000f,
+ 0.131430543404356f, 0.131915057882395f, 0.132399157954622f,
+ 0.132882843815500f, 0.133366115659450f, 0.133848973680849f,
+ 0.134331418074033f, 0.134813449033295f, 0.130933338745307f,
+ 0.131414543419434f, 0.131895335242262f, 0.132375714407915f,
+ 0.132855681110473f, 0.133335235543976f, 0.133814377902417f,
+ 0.134293108379751f, 0.134771427169888f, 0.135249334466697f,
+ 0.135726830464003f, 0.136203915355590f, 0.136680589335197f,
+ 0.137156852596525f, 0.137632705333228f, 0.138108147738920f,
+ 0.138583180007172f, 0.139057802331512f, 0.139532014905426f,
+ 0.140005817922358f, 0.140479211575709f, 0.140952196058836f,
+ 0.141424771565058f, 0.141896938287645f, 0.142368696419831f,
+ 0.142840046154803f, 0.143310987685707f, 0.143781521205648f,
+ 0.144251646907686f, 0.144721364984839f, 0.145190675630085f,
+ 0.145659579036357f, 0.146128075396545f, 0.146596164903499f,
+ 0.147063847750025f, 0.147531124128886f, 0.147997994232804f,
+ 0.148464458254458f, 0.148930516386483f, 0.149396168821473f,
+ 0.149861415751981f, 0.150326257370514f, 0.150790693869538f,
+ 0.151254725441479f, 0.151718352278716f, 0.152181574573589f,
+ 0.152644392518394f, 0.153106806305386f, 0.153568816126774f,
+ 0.154030422174728f, 0.154491624641374f, 0.154952423718796f,
+ 0.155412819599036f, 0.155872812474093f, 0.156332402535922f,
+ 0.156791589976438f, 0.157250374987511f, 0.157708757760971f,
+ 0.158166738488604f, 0.158624317362153f, 0.159081494573321f,
+ 0.159538270313766f, 0.159994644775104f, 0.160450618148909f,
+ 0.160906190626713f, 0.161361362400004f, 0.161816133660228f,
+ 0.162270504598790f, 0.162724475407050f, 0.163178046276328f,
+ 0.163631217397900f, 0.164083988962999f, 0.164536361162817f,
+ 0.164988334188502f, 0.165439908231161f, 0.165891083481858f,
+ 0.166341860131613f, 0.166792238371406f, 0.167242218392172f,
+ 0.167691800384806f, 0.168140984540158f, 0.168589771049037f,
+ 0.169038160102209f, 0.169486151890398f, 0.165572018596708f,
+ 0.166019216426932f, 0.166466017564089f, 0.166912422198731f,
+ 0.167358430521371f, 0.167804042722478f, 0.168249258992475f,
+ 0.168694079521749f, 0.169138504500638f, 0.169582534119443f,
+ 0.170026168568418f, 0.170469408037777f, 0.170912252717692f,
+ 0.171354702798289f, 0.171796758469656f, 0.172238419921836f,
+ 0.172679687344829f, 0.173120560928594f, 0.173561040863046f,
+ 0.174001127338058f, 0.174440820543462f, 0.174880120669046f,
+ 0.175319027904554f, 0.175757542439691f, 0.176195664464117f,
+ 0.176633394167450f, 0.177070731739265f, 0.177507677369096f,
+ 0.177944231246432f, 0.178380393560723f, 0.178816164501372f,
+ 0.179251544257744f, 0.179686533019159f, 0.180121130974894f,
+ 0.180555338314184f, 0.180989155226223f, 0.181422581900160f,
+ 0.181855618525104f, 0.182288265290119f, 0.182720522384228f,
+ 0.183152389996411f, 0.183583868315606f, 0.184014957530707f,
+ 0.184445657830568f, 0.184875969403997f, 0.185305892439763f,
+ 0.185735427126589f, 0.186164573653160f, 0.186593332208113f,
+ 0.187021702980047f, 0.187449686157516f, 0.187877281929033f,
+ 0.188304490483066f, 0.188731312008043f, 0.189157746692349f,
+ 0.189583794724325f, 0.190009456292270f, 0.190434731584443f,
+ 0.190859620789056f, 0.191284124094282f, 0.191708241688250f,
+ 0.192131973759046f, 0.192555320494716f, 0.192978282083259f,
+ 0.193400858712636f, 0.193823050570763f, 0.194244857845513f,
+ 0.190304552717141f, 0.190725591388591f, 0.191146246040030f,
+ 0.191566516859164f, 0.191986404033653f, 0.192405907751115f,
+ 0.192825028199128f, 0.193243765565223f, 0.193662120036893f,
+ 0.194080091801586f, 0.194497681046707f, 0.194914887959619f,
+ 0.195331712727645f, 0.195748155538060f, 0.196164216578102f,
+ 0.196579896034964f, 0.196995194095795f, 0.197410110947704f,
+ 0.197824646777756f, 0.198238801772974f, 0.198652576120338f,
+ 0.199065970006786f, 0.199478983619212f, 0.199891617144471f,
+ 0.200303870769370f, 0.200715744680679f, 0.201127239065121f,
+ 0.201538354109379f, 0.201949090000093f, 0.202359446923861f,
+ 0.202769425067235f, 0.203179024616730f, 0.203588245758813f,
+ 0.203997088679912f, 0.204405553566412f, 0.204813640604653f,
+ 0.205221349980936f, 0.205628681881516f, 0.206035636492609f,
+ 0.206442214000385f, 0.206848414590972f, 0.207254238450459f,
+ 0.207659685764888f, 0.208064756720260f, 0.208469451502535f,
+ 0.208873770297627f, 0.209277713291411f, 0.209681280669718f,
+ 0.210084472618335f, 0.210487289323009f, 0.210889730969442f,
+ 0.211291797743296f, 0.211693489830188f, 0.212094807415693f,
+ 0.212495750685345f, 0.212896319824633f, 0.213296515019006f,
+ 0.213696336453868f, 0.214095784314582f, 0.214494858786468f,
+ 0.210531832047226f, 0.210930160297245f, 0.211328115714141f,
+ 0.211725698483062f, 0.212122908789116f, 0.212519746817367f,
+ 0.212916212752837f, 0.213312306780505f, 0.213708029085308f,
+ 0.214103379852140f, 0.214498359265853f, 0.214892967511255f,
+ 0.215287204773112f, 0.215681071236149f, 0.216074567085046f,
+ 0.216467692504443f, 0.216860447678934f, 0.217252832793074f,
+ 0.217644848031373f, 0.218036493578299f, 0.218427769618278f,
+ 0.218818676335693f, 0.219209213914884f, 0.219599382540149f,
+ 0.219989182395744f, 0.220378613665880f, 0.220767676534728f,
+ 0.221156371186415f, 0.221544697805026f, 0.221932656574604f,
+ 0.222320247679147f, 0.222707471302613f, 0.223094327628916f,
+ 0.223480816841929f, 0.223866939125480f, 0.224252694663356f,
+ 0.224638083639300f, 0.225023106237015f, 0.225407762640159f,
+ 0.225792053032349f, 0.226175977597157f, 0.226559536518116f,
+ 0.226942729978713f, 0.227325558162394f, 0.227708021252562f,
+ 0.228090119432578f, 0.228471852885760f, 0.228853221795383f,
+ 0.229234226344680f, 0.229614866716842f, 0.229995143095015f,
+ 0.230375055662304f, 0.230754604601773f, 0.231133790096440f,
+ 0.231512612329282f, 0.231891071483235f, 0.227907439733612f,
+ 0.228285173278418f, 0.228662544292882f, 0.229039552959768f,
+ 0.229416199461797f, 0.229792483981648f, 0.230168406701958f,
+ 0.230543967805320f, 0.230919167474285f, 0.231294005891360f,
+ 0.231668483239013f, 0.232042599699666f, 0.232416355455699f,
+ 0.232789750689451f, 0.233162785583216f, 0.233535460319247f,
+ 0.233907775079755f, 0.234279730046907f, 0.234651325402827f,
+ 0.235022561329597f, 0.235393438009258f, 0.235763955623805f,
+ 0.236134114355194f, 0.236503914385335f, 0.236873355896098f,
+ 0.237242439069310f, 0.237611164086754f, 0.237979531130171f,
+ 0.238347540381260f, 0.238715192021677f, 0.239082486233034f,
+ 0.239449423196903f, 0.239816003094811f, 0.240182226108245f,
+ 0.240548092418646f, 0.240913602207415f, 0.241278755655909f,
+ 0.241643552945443f, 0.242007994257290f, 0.242372079772679f,
+ 0.242735809672797f, 0.243099184138788f, 0.243462203351754f,
+ 0.243824867492754f, 0.244187176742804f, 0.244549131282879f,
+ 0.244910731293909f, 0.245271976956783f, 0.245632868452347f,
+ 0.245993405961404f, 0.246353589664714f, 0.246713419742997f,
+ 0.247072896376926f, 0.247432019747135f, 0.243429062026635f,
+ 0.243787479411131f, 0.244145544073548f, 0.244503256194350f,
+ 0.244860615953955f, 0.245217623532740f, 0.245574279111040f,
+ 0.245930582869146f, 0.246286534987307f, 0.246642135645729f,
+ 0.246997385024576f, 0.247352283303969f, 0.247706830663986f,
+ 0.248061027284663f, 0.248414873345993f, 0.248768369027926f,
+ 0.249121514510370f, 0.249474309973191f, 0.249826755596210f,
+ 0.250178851559207f, 0.250530598041920f, 0.250881995224043f,
+ 0.251233043285228f, 0.251583742405084f, 0.251934092763177f,
+ 0.252284094539032f, 0.252633747912130f, 0.252983053061909f,
+ 0.253332010167765f, 0.253680619409052f, 0.254028880965080f,
+ 0.254376795015117f, 0.254724361738388f, 0.255071581314077f,
+ 0.255418453921322f, 0.255764979739221f, 0.256111158946830f,
+ 0.256456991723160f, 0.256802478247180f, 0.257147618697817f,
+ 0.257492413253955f, 0.257836862094436f, 0.258180965398058f,
+ 0.258524723343577f, 0.258868136109707f, 0.259211203875118f,
+ 0.259553926818439f, 0.259896305118254f, 0.260238338953107f,
+ 0.260580028501497f, 0.260921373941883f, 0.261262375452678f,
+ 0.261603033212256f, 0.261943347398944f, 0.262283318191031f,
+ 0.258261217759182f, 0.258600502296753f, 0.258939443974328f,
+ 0.259278042970020f, 0.259616299461904f, 0.259954213628010f,
+ 0.260291785646327f, 0.260629015694800f, 0.260965903951331f,
+ 0.261302450593780f, 0.261638655799966f, 0.261974519747662f,
+ 0.262310042614600f, 0.262645224578471f, 0.262980065816920f,
+ 0.263314566507552f, 0.263648726827928f, 0.263982546955566f,
+ 0.264316027067943f, 0.264649167342491f, 0.264981967956602f,
+ 0.265314429087624f, 0.265646550912860f, 0.265978333609575f,
+ 0.266309777354988f, 0.266640882326276f, 0.266971648700574f,
+ 0.267302076654974f, 0.267632166366524f, 0.267961918012231f,
+ 0.268291331769060f, 0.268620407813930f, 0.268949146323722f,
+ 0.269277547475269f, 0.269605611445366f, 0.269933338410763f,
+ 0.270260728548167f, 0.270587782034243f, 0.270914499045615f,
+ 0.271240879758860f, 0.271566924350517f, 0.271892632997080f,
+ 0.272218005874999f, 0.272543043160685f, 0.272867745030503f,
+ 0.273192111660776f, 0.273516143227786f, 0.273839839907771f,
+ 0.274163201876925f, 0.274486229311402f, 0.274808922387312f,
+ 0.275131281280722f, 0.275453306167657f, 0.275774997224098f,
+ 0.276096354625986f, 0.276417378549215f, 0.272376341162064f,
+ 0.272696698655497f, 0.273016723197707f, 0.273336414964418f,
+ 0.273655774131314f, 0.273974800874035f, 0.274293495368180f,
+ 0.274611857789301f, 0.274929888312913f, 0.275247587114485f,
+ 0.275564954369443f, 0.275881990253173f, 0.276198694941014f,
+ 0.276515068608266f, 0.276831111430185f, 0.277146823581984f,
+ 0.277462205238834f, 0.277777256575864f, 0.278091977768158f,
+ 0.278406368990758f, 0.278720430418665f, 0.279034162226836f,
+ 0.279347564590185f, 0.279660637683583f, 0.279973381681861f,
+ 0.280285796759803f, 0.280597883092155f, 0.280909640853616f,
+ 0.281221070218844f, 0.281532171362457f, 0.281842944459025f,
+ 0.282153389683078f, 0.282463507209106f, 0.282773297211551f,
+ 0.283082759864817f, 0.283391895343261f, 0.283700703821200f,
+ 0.284009185472909f, 0.284317340472619f, 0.284625168994516f,
+ 0.284932671212748f, 0.285239847301418f, 0.285546697434584f,
+ 0.285853221786265f, 0.286159420530436f, 0.286465293841028f,
+ 0.286770841891931f, 0.287076064856991f, 0.287380962910013f,
+ 0.287685536224756f, 0.287989784974941f, 0.288293709334242f,
+ 0.288597309476293f, 0.288900585574683f, 0.289203537802961f,
+ 0.289506166334631f, 0.289808471343155f, 0.290110453001953f,
+ 0.290412111484400f, 0.290713446963832f, 0.286652731605961f,
+ 0.286953421599191f, 0.287253789109152f, 0.287553834309004f,
+ 0.287853557371869f, 0.288152958470824f, 0.288452037778905f,
+ 0.288750795469103f, 0.289049231714367f, 0.289347346687604f,
+ 0.289645140561678f, 0.289942613509411f, 0.290239765703580f,
+ 0.290536597316922f, 0.290833108522128f, 0.291129299491851f,
+ 0.291425170398696f, 0.291720721415230f, 0.292015952713973f,
+ 0.292310864467405f, 0.292605456847963f, 0.292899730028041f,
+ 0.293193684179989f, 0.293487319476116f, 0.293780636088688f,
+ 0.294073634189927f, 0.294366313952014f, 0.294658675547085f,
+ 0.294950719147237f, 0.295242444924520f, 0.295533853050944f,
+ 0.295824943698475f, 0.296115717039037f, 0.296406173244510f,
+ 0.296696312486734f, 0.296986134937503f, 0.297275640768571f,
+ 0.297564830151647f, 0.297853703258398f, 0.298142260260449f,
+ 0.298430501329382f, 0.298718426636735f, 0.299006036354006f,
+ 0.299293330652647f, 0.299580309704069f, 0.299866973679640f,
+ 0.300153322750686f, 0.300439357088489f, 0.300725076864288f,
+ 0.301010482249282f, 0.301295573414623f, 0.301580350531424f,
+ 0.301864813770753f, 0.302148963303636f, 0.302432799301057f,
+ 0.302716321933956f, 0.302999531373231f, 0.303282427789736f,
+ 0.303565011354285f, 0.303847282237646f, 0.304129240610547f,
+ 0.304410886643670f, 0.304692220507658f, 0.304973242373109f,
+ 0.305253952410579f, 0.305534350790580f, 0.305814437683583f,
+ 0.301732485252438f, 0.302011949682684f, 0.302291103137086f,
+ 0.302569945785942f, 0.302848477799510f, 0.303126699348003f,
+ 0.303404610601592f, 0.303682211730404f, 0.303959502904526f,
+ 0.304236484294000f, 0.304513156068825f, 0.304789518398959f,
+ 0.305065571454316f, 0.305341315404768f, 0.305616750420142f,
+ 0.305891876670227f, 0.306166694324763f, 0.306441203553453f,
+ 0.306715404525953f, 0.306989297411878f, 0.307262882380802f,
+ 0.307536159602252f, 0.307809129245716f, 0.308081791480638f,
+ 0.308354146476418f, 0.308626194402416f, 0.308897935427946f,
+ 0.309169369722281f, 0.309440497454651f, 0.309711318794243f,
+ 0.309981833910203f, 0.310252042971631f, 0.310521946147587f,
+ 0.310791543607086f, 0.311060835519102f, 0.311329822052565f,
+ 0.311598503376364f, 0.311866879659343f, 0.312134951070305f,
+ 0.312402717778008f, 0.312670179951171f, 0.312937337758465f,
+ 0.313204191368524f, 0.313470740949935f, 0.313736986671243f,
+ 0.314002928700952f, 0.314268567207521f, 0.314533902359368f,
+ 0.314798934324867f, 0.315063663272350f, 0.315328089370105f,
+ 0.315592212786379f, 0.315856033689375f, 0.316119552247253f,
+ 0.316382768628132f, 0.316645683000087f, 0.316908295531148f,
+ 0.317170606389307f, 0.317432615742509f, 0.317694323758658f,
+ 0.317955730605615f, 0.318216836451199f, 0.318477641463185f,
+ 0.318738145809305f, 0.318998349657250f, 0.319258253174666f,
+ 0.319517856529158f, 0.319777159888288f, 0.320036163419573f,
+ 0.320294867290491f, 0.320553271668473f, 0.320811376720911f,
+ 0.321069182615152f, 0.321326689518500f, 0.321583897598218f,
+ 0.321840807021525f, 0.322097417955597f, 0.322353730567567f,
+ 0.322609745024527f, 0.322865461493525f, 0.323120880141565f,
+ 0.323376001135609f, 0.323630824642579f, 0.319523622821772f,
+ 0.319777851855178f, 0.320031783902010f, 0.320285419129017f,
+ 0.320538757702904f, 0.320791799790335f, 0.321044545557929f,
+ 0.321296995172263f, 0.321549148799872f, 0.321801006607247f,
+ 0.322052568760837f, 0.322303835427048f, 0.322554806772243f,
+ 0.322805482962742f, 0.323055864164824f, 0.323305950544722f,
+ 0.323555742268629f, 0.323805239502694f, 0.324054442413022f,
+ 0.324303351165679f, 0.324551965926684f, 0.324800286862015f,
+ 0.325048314137607f, 0.325296047919353f, 0.325543488373102f,
+ 0.325790635664660f, 0.326037489959792f, 0.326284051424219f,
+ 0.326530320223619f, 0.326776296523626f, 0.327021980489835f,
+ 0.327267372287794f, 0.327512472083010f, 0.327757280040949f,
+ 0.328001796327030f, 0.328246021106633f, 0.328489954545092f,
+ 0.328733596807703f, 0.328976948059713f, 0.329220008466331f,
+ 0.329462778192721f, 0.329705257404004f, 0.329947446265259f,
+ 0.330189344941523f, 0.330430953597789f, 0.330672272399006f,
+ 0.330913301510083f, 0.331154041095883f, 0.331394491321230f,
+ 0.331634652350901f, 0.331874524349634f, 0.332114107482121f,
+ 0.332353401913014f, 0.332592407806920f, 0.332831125328403f,
+ 0.333069554641987f, 0.333307695912150f, 0.333545549303328f,
+ 0.333783114979916f, 0.334020393106265f, 0.334257383846681f,
+ 0.334494087365431f, 0.334730503826737f, 0.334966633394778f,
+ 0.335202476233691f, 0.335438032507570f, 0.335673302380465f,
+ 0.335908286016386f, 0.336142983579296f, 0.336377395233120f,
+ 0.336611521141736f, 0.336845361468981f, 0.337078916378650f,
+ 0.337312186034494f, 0.337545170600220f, 0.337777870239495f,
+ 0.338010285115941f, 0.338242415393138f, 0.338474261234623f,
+ 0.338705822803890f, 0.338937100264391f, 0.339168093779534f,
+ 0.339398803512685f, 0.339629229627166f, 0.339859372286258f,
+ 0.340089231653198f, 0.340318807891179f, 0.340548101163354f,
+ 0.340777111632832f, 0.341005839462677f, 0.341234284815913f,
+ 0.341462447855520f, 0.341690328744436f, 0.341917927645554f,
+ 0.342145244721727f, 0.342372280135763f, 0.342599034050427f,
+ 0.342825506628444f, 0.343051698032493f, 0.343277608425212f,
+ 0.343503237969194f, 0.343728586826993f, 0.343953655161116f,
+ 0.344178443134029f, 0.344402950908157f, 0.344627178645878f,
+ 0.344851126509531f, 0.345074794661410f, 0.345298183263766f,
+ 0.345521292478809f, 0.345744122468705f, 0.345966673395577f,
+ 0.346188945421505f, 0.346410938708527f, 0.346632653418637f,
+ 0.346854089713787f, 0.347075247755886f, 0.347296127706800f,
+ 0.347516729728352f, 0.347737053982323f, 0.347957100630451f,
+ 0.348176869834429f, 0.348396361755910f, 0.348615576556502f,
+ 0.348834514397771f, 0.349053175441242f, 0.349271559848394f,
+ 0.349489667780664f, 0.349707499399448f, 0.349925054866097f,
+ 0.350142334341920f, 0.350359337988183f, 0.350576065966110f,
+ 0.350792518436880f, 0.351008695561632f, 0.351224597501459f,
+ 0.351440224417413f, 0.351655576470505f, 0.351870653821698f,
+ 0.352085456631918f, 0.352299985062043f, 0.352514239272912f,
+ 0.352728219425319f, 0.352941925680015f, 0.353155358197710f,
+ 0.353368517139069f, 0.353581402664716f, 0.353794014935231f,
+ 0.354006354111151f, 0.354218420352971f, 0.354430213821142f,
+ 0.354641734676074f, 0.354852983078131f, 0.355063959187638f,
+ 0.355274663164873f, 0.355485095170075f, 0.355695255363438f,
+ 0.355905143905114f, 0.356114760955211f, 0.356324106673794f,
+ 0.356533181220887f, 0.356741984756471f, 0.356950517440481f,
+ 0.357158779432813f, 0.357366770893317f, 0.357574491981803f,
+ 0.357781942858036f, 0.357989123681738f, 0.358196034612590f,
+ 0.358402675810229f, 0.358609047434249f, 0.358815149644201f,
+ 0.359020982599593f, 0.359226546459892f, 0.359431841384519f,
+ 0.359636867532855f, 0.359841625064236f, 0.360046114137957f,
+ 0.360250334913268f, 0.360454287549378f, 0.360657972205452f,
+ 0.360861389040612f, 0.356702810206361f, 0.356905691876891f,
+ 0.357108306203617f, 0.357310653345491f, 0.357512733461420f,
+ 0.357714546710270f, 0.357916093250863f, 0.358117373241979f,
+ 0.358318386842353f, 0.358519134210680f, 0.358719615505611f,
+ 0.358919830885752f, 0.359119780509670f, 0.359319464535885f,
+ 0.359518883122878f, 0.359718036429085f, 0.359916924612898f,
+ 0.360115547832669f, 0.360313906246705f, 0.360512000013271f,
+ 0.360709829290588f, 0.360907394236835f, 0.361104695010150f,
+ 0.361301731768624f, 0.361498504670307f, 0.361695013873208f,
+ 0.361891259535291f, 0.362087241814476f, 0.362282960868644f,
+ 0.362478416855630f, 0.362673609933225f, 0.362868540259182f,
+ 0.363063207991205f, 0.363257613286961f, 0.363451756304069f,
+ 0.363645637200109f, 0.363839256132616f, 0.364032613259082f,
+ 0.364225708736957f, 0.364418542723648f, 0.364611115376518f,
+ 0.364803426852889f, 0.364995477310039f, 0.365187266905203f,
+ 0.365378795795572f, 0.365570064138297f, 0.365761072090484f,
+ 0.365951819809197f, 0.366142307451455f, 0.366332535174237f,
+ 0.366522503134478f, 0.366712211489069f, 0.366901660394860f,
+ 0.367090850008657f, 0.367279780487222f, 0.367468451987276f,
+ 0.367656864665497f, 0.367845018678518f, 0.368032914182932f,
+ 0.368220551335288f, 0.368407930292089f, 0.368595051209801f,
+ 0.368781914244842f, 0.368968519553590f, 0.369154867292378f,
+ 0.369340957617498f, 0.369526790685198f, 0.369712366651684f,
+ 0.369897685673117f, 0.370082747905618f, 0.370267553505262f,
+ 0.370452102628084f, 0.370636395430075f, 0.370820432067181f,
+ 0.371004212695309f, 0.371187737470320f, 0.371371006548033f,
+ 0.371554020084224f, 0.371736778234628f, 0.371919281154933f,
+ 0.372101529000787f, 0.372283521927796f, 0.372465260091519f,
+ 0.372646743647477f, 0.372827972751145f, 0.373008947557956f,
+ 0.373189668223299f, 0.373370134902522f, 0.373550347750928f,
+ 0.373730306923778f, 0.373910012576292f, 0.374089464863644f,
+ 0.374268663940966f, 0.374447609963348f, 0.374626303085837f,
+ 0.374804743463435f, 0.374982931251104f, 0.375160866603761f,
+ 0.375338549676281f, 0.375515980623497f, 0.375693159600195f,
+ 0.375870086761124f, 0.376046762260986f, 0.376223186254440f,
+ 0.376399358896105f, 0.376575280340554f, 0.376750950742319f,
+ 0.376926370255889f, 0.377101539035708f, 0.377276457236179f,
+ 0.377451125011662f, 0.377625542516473f, 0.377799709904887f,
+ 0.377973627331134f, 0.378147294949401f, 0.378320712913835f,
+ 0.378493881378536f, 0.378666800497564f, 0.378839470424936f,
+ 0.379011891314623f, 0.379184063320557f, 0.379355986596625f,
+ 0.379527661296671f, 0.379699087574496f, 0.379870265583860f,
+ 0.380041195478477f, 0.380211877412020f, 0.380382311538119f,
+ 0.380552498010361f, 0.380722436982289f, 0.380892128607405f,
+ 0.381061573039165f, 0.381230770430986f, 0.381399720936239f,
+ 0.381568424708253f, 0.381736881900314f, 0.381905092665666f,
+ 0.382073057157508f, 0.382240775528999f, 0.382408247933252f,
+ 0.382575474523339f, 0.382742455452288f, 0.382909190873084f,
+ 0.383075680938671f, 0.383241925801948f, 0.383407925615771f,
+ 0.383573680532955f, 0.383739190706270f, 0.383904456288443f,
+ 0.384069477432160f, 0.384234254290062f, 0.384398787014749f,
+ 0.384563075758777f, 0.384727120674658f, 0.384890921914863f,
+ 0.385054479631818f, 0.385217793977909f, 0.385380865105476f,
+ 0.385543693166817f, 0.385706278314188f, 0.385868620699801f,
+ 0.386030720475826f, 0.386192577794389f, 0.386354192807573f,
+ 0.386515565667420f, 0.386676696525926f, 0.386837585535046f,
+ 0.386998232846693f, 0.387158638612734f, 0.387318802984995f,
+ 0.387478726115260f, 0.387638408155268f, 0.383436121249137f,
+ 0.383595321563679f, 0.383754281242925f, 0.383913000438444f,
+ 0.384071479301760f, 0.384229717984357f, 0.384387716637672f,
+ 0.384545475413102f, 0.384702994462001f, 0.384860273935677f,
+ 0.385017313985400f, 0.385174114762392f, 0.385330676417835f,
+ 0.385486999102868f, 0.385643082968586f, 0.385798928166041f,
+ 0.385954534846243f, 0.386109903160157f, 0.386265033258709f,
+ 0.386419925292778f, 0.386574579413201f, 0.386728995770773f,
+ 0.386883174516247f, 0.387037115800330f, 0.387190819773689f,
+ 0.387344286586945f, 0.387497516390679f, 0.387650509335427f,
+ 0.387803265571684f, 0.387955785249900f, 0.388108068520482f,
+ 0.388260115533796f, 0.388411926440164f, 0.388563501389864f,
+ 0.388714840533132f, 0.388865944020162f, 0.389016812001104f,
+ 0.389167444626063f, 0.389317842045105f, 0.389468004408251f,
+ 0.389617931865478f, 0.389767624566722f, 0.389917082661874f,
+ 0.390066306300784f, 0.390215295633258f, 0.390364050809059f,
+ 0.390512571977908f, 0.390660859289481f, 0.390808912893412f,
+ 0.390956732939294f, 0.391104319576674f, 0.391251672955058f,
+ 0.391398793223907f, 0.391545680532642f, 0.391692335030638f,
+ 0.391838756867229f, 0.391984946191705f, 0.392130903153313f,
+ 0.392276627901259f, 0.392422120584702f, 0.392567381352763f,
+ 0.392712410354515f, 0.392857207738992f, 0.393001773655183f,
+ 0.393146108252035f, 0.393290211678450f, 0.393434084083290f,
+ 0.393577725615372f, 0.393721136423470f, 0.393864316656316f,
+ 0.394007266462598f, 0.389788257983385f, 0.389930747382434f,
+ 0.390073006800726f, 0.390215036386779f, 0.390356836289067f,
+ 0.390498406656018f, 0.390639747636022f, 0.390780859377423f,
+ 0.390921742028522f, 0.391062395737578f, 0.391202820652806f,
+ 0.391343016922379f, 0.391482984694427f, 0.391622724117036f,
+ 0.391762235338250f, 0.391901518506070f, 0.392040573768452f,
+ 0.392179401273312f, 0.392318001168520f, 0.392456373601907f,
+ 0.392594518721257f, 0.392732436674313f, 0.392870127608774f,
+ 0.393007591672297f, 0.393144829012496f, 0.393281839776942f,
+ 0.393418624113161f, 0.393555182168638f, 0.393691514090816f,
+ 0.393827620027092f, 0.393963500124822f, 0.394099154531318f,
+ 0.394234583393851f, 0.394369786859647f, 0.394504765075889f,
+ 0.394639518189717f, 0.394774046348230f, 0.394908349698482f,
+ 0.395042428387484f, 0.395176282562205f, 0.395309912369570f,
+ 0.395443317956462f, 0.395576499469721f, 0.395709457056142f,
+ 0.395842190862479f, 0.395974701035442f, 0.396106987721700f,
+ 0.396239051067876f, 0.396370891220552f, 0.396502508326267f,
+ 0.396633902531515f, 0.396765073982748f, 0.396896022826377f,
+ 0.397026749208768f, 0.397157253276243f, 0.392925807167507f,
+ 0.393055867043950f, 0.393185705044190f, 0.393315321314378f,
+ 0.393444716000622f, 0.393573889248988f, 0.393702841205497f,
+ 0.393831572016130f, 0.393960081826821f, 0.394088370783464f,
+ 0.394216439031910f, 0.394344286717965f, 0.394471913987393f,
+ 0.394599320985916f, 0.394726507859211f, 0.394853474752914f,
+ 0.394980221812616f, 0.395106749183867f, 0.395233057012172f,
+ 0.395359145442995f, 0.395485014621754f, 0.395610664693828f,
+ 0.395736095804550f, 0.395861308099210f, 0.395986301723057f,
+ 0.396111076821296f, 0.396235633539087f, 0.396359972021550f,
+ 0.396484092413761f, 0.396607994860752f, 0.396731679507512f,
+ 0.396855146498989f, 0.396978395980086f, 0.397101428095663f,
+ 0.397224242990538f, 0.397346840809486f, 0.397469221697237f,
+ 0.397591385798481f, 0.397713333257862f, 0.397835064219984f,
+ 0.397956578829405f, 0.398077877230641f, 0.398198959568167f,
+ 0.398319825986412f, 0.398440476629763f, 0.398560911642564f,
+ 0.398681131169117f, 0.398801135353680f, 0.398920924340467f,
+ 0.394678770266074f, 0.394798129289784f, 0.394917273548105f,
+ 0.395036203185081f, 0.395154918344712f, 0.395273419170954f,
+ 0.395391705807720f, 0.395509778398882f, 0.395627637088268f,
+ 0.395745282019662f, 0.395862713336806f, 0.395979931183398f,
+ 0.396096935703093f, 0.396213727039505f, 0.396330305336203f,
+ 0.396446670736713f, 0.396562823384518f, 0.396678763423060f,
+ 0.396794490995734f, 0.396910006245896f, 0.397025309316857f,
+ 0.397140400351884f, 0.397255279494203f, 0.397369946886996f,
+ 0.397484402673401f, 0.397598646996516f, 0.397712679999392f,
+ 0.397826501825040f, 0.397940112616427f, 0.398053512516475f,
+ 0.398166701668066f, 0.398279680214038f, 0.398392448297186f,
+ 0.398505006060259f, 0.398617353645969f, 0.398729491196979f,
+ 0.398841418855912f, 0.398953136765347f, 0.399064645067821f,
+ 0.399175943905828f, 0.399287033421816f, 0.399397913758195f,
+ 0.399508585057326f, 0.399619047461532f, 0.399729301113091f,
+ 0.395477618146659f, 0.395587454719585f, 0.395697082966438f,
+ 0.395806503029326f, 0.395915715050310f, 0.396024719171410f,
+ 0.396133515534604f, 0.396242104281823f, 0.396350485554960f,
+ 0.396458659495860f, 0.396566626246330f, 0.396674385948129f,
+ 0.396781938742976f, 0.396889284772547f, 0.396996424178473f,
+ 0.397103357102343f, 0.397210083685704f, 0.397316604070058f,
+ 0.397422918396866f, 0.397529026807543f, 0.397634929443464f,
+ 0.397740626445960f, 0.397846117956317f, 0.397951404115781f,
+ 0.398056485065553f, 0.398161360946792f, 0.398266031900612f,
+ 0.398370498068087f, 0.398474759590244f, 0.398578816608071f,
+ 0.398682669262511f, 0.398786317694462f, 0.398889762044784f,
+ 0.398993002454288f, 0.399096039063747f, 0.399198872013887f,
+ 0.399301501445394f, 0.399403927498908f, 0.399506150315030f,
+ 0.399608170034313f, 0.399709986797270f, 0.399811600744371f,
+ 0.395551284008465f, 0.395652492745088f, 0.395753499087005f,
+ 0.395854303174512f, 0.395954905147863f, 0.396055305147269f,
+ 0.396155503312898f, 0.396255499784873f, 0.396355294703278f,
+ 0.396454888208150f, 0.396554280439485f, 0.396653471537236f,
+ 0.396752461641311f, 0.396851250891577f, 0.396949839427857f,
+ 0.397048227389931f, 0.397146414917536f, 0.397244402150366f,
+ 0.397342189228072f, 0.397439776290262f, 0.397537163476500f,
+ 0.397634350926308f, 0.397731338779165f, 0.397828127174505f,
+ 0.397924716251721f, 0.398021106150163f, 0.398117297009136f,
+ 0.398213288967904f, 0.398309082165687f, 0.398404676741662f,
+ 0.398500072834962f, 0.398595270584678f, 0.398690270129858f,
+ 0.398785071609507f, 0.398879675162585f, 0.398974080928012f,
+ 0.399068289044663f, 0.399162299651370f, 0.399256112886922f,
+ 0.399349728890064f, 0.399443147799501f, 0.395174641746314f,
+ 0.395267666884276f, 0.395360495344382f, 0.395453127265163f,
+ 0.395545562785107f, 0.395637802042658f, 0.395729845176217f,
+ 0.395821692324142f, 0.395913343624749f, 0.396004799216310f,
+ 0.396096059237054f, 0.396187123825166f, 0.396277993118789f,
+ 0.396368667256023f, 0.396459146374925f, 0.396549430613508f,
+ 0.396639520109742f, 0.396729415001555f, 0.396819115426830f,
+ 0.396908621523410f, 0.396997933429091f, 0.397087051281630f,
+ 0.397175975218736f, 0.397264705378080f, 0.397353241897287f,
+ 0.397441584913940f, 0.397529734565577f, 0.397617690989695f,
+ 0.397705454323747f, 0.397793024705144f, 0.397880402271252f,
+ 0.397967587159395f, 0.398054579506854f, 0.398141379450867f,
+ 0.398227987128627f, 0.398314402677288f, 0.398400626233956f,
+ 0.398486657935698f, 0.398572497919536f, 0.398658146322447f,
+ 0.394381875273792f, 0.394467140925618f, 0.394552215407196f,
+ 0.394637098855334f, 0.394721791406795f, 0.394806293198300f,
+ 0.394890604366525f, 0.394974725048106f, 0.395058655379632f,
+ 0.395142395497653f, 0.395225945538672f, 0.395309305639152f,
+ 0.395392475935511f, 0.395475456564125f, 0.395558247661327f,
+ 0.395640849363404f, 0.395723261806605f, 0.395805485127131f,
+ 0.395887519461143f, 0.395969364944757f, 0.396051021714048f,
+ 0.396132489905045f, 0.396213769653737f, 0.396294861096067f,
+ 0.396375764367938f, 0.396456479605206f, 0.396537006943688f,
+ 0.396617346519155f, 0.396697498467336f, 0.396777462923917f,
+ 0.396857240024539f, 0.396936829904804f, 0.397016232700266f,
+ 0.397095448546439f, 0.397174477578793f, 0.397253319932756f,
+ 0.397331975743711f, 0.397410445146998f, 0.397488728277915f,
+ 0.397566825271718f, 0.393283008256039f, 0.393360733381202f,
+ 0.393438272774754f, 0.393515626571778f, 0.393592794907312f,
+ 0.393669777916352f, 0.393746575733851f, 0.393823188494718f,
+ 0.393899616333820f, 0.393975859385979f, 0.394051917785976f,
+ 0.394127791668548f, 0.394203481168389f, 0.394278986420149f,
+ 0.394354307558436f, 0.394429444717814f, 0.394504398032805f,
+ 0.394579167637888f, 0.394653753667496f, 0.394728156256022f,
+ 0.394802375537816f, 0.394876411647182f, 0.394950264718382f,
+ 0.395023934885638f, 0.395097422283124f, 0.395170727044974f,
+ 0.395243849305278f, 0.395316789198083f, 0.395389546857393f,
+ 0.395462122417168f, 0.395534516011326f, 0.395606727773741f,
+ 0.395678757838244f, 0.395750606338623f, 0.395822273408624f,
+ 0.395893759181948f, 0.395965063792254f, 0.396036187373157f,
+ 0.396107130058230f, 0.396177891981003f, 0.396248473274960f,
+ 0.391957146065968f, 0.392027366502581f, 0.392097406710579f,
+ 0.392167266823275f, 0.392236946973940f, 0.392306447295801f,
+ 0.392375767922042f, 0.392444908985804f, 0.392513870620185f,
+ 0.392582652958240f, 0.392651256132980f, 0.392719680277373f,
+ 0.392787925524346f, 0.392855992006779f, 0.392923879857513f,
+ 0.392991589209342f, 0.393059120195020f, 0.393126472947256f,
+ 0.393193647598717f, 0.393260644282026f, 0.393327463129763f,
+ 0.393394104274464f, 0.393460567848624f, 0.393526853984694f,
+ 0.393592962815081f, 0.393658894472149f, 0.393724649088219f,
+ 0.393790226795571f, 0.393855627726437f, 0.393920852013011f,
+ 0.393985899787441f, 0.394050771181833f, 0.394115466328248f,
+ 0.394179985358706f, 0.394244328405183f, 0.394308495599612f,
+ 0.394372487073883f, 0.394436302959841f, 0.394499943389291f,
+ 0.394563408493992f, 0.394626698405663f, 0.390328085248398f,
+ 0.390391025168985f, 0.390453790291433f, 0.390516380747287f,
+ 0.390578796668047f, 0.390641038185172f, 0.390703105430078f,
+ 0.390764998534136f, 0.390826717628674f, 0.390888262844978f,
+ 0.390949634314291f, 0.391010832167811f, 0.391071856536696f,
+ 0.391132707552056f, 0.391193385344964f, 0.391253890046444f,
+ 0.391314221787480f, 0.391374380699013f, 0.391434366911939f,
+ 0.391494180557114f, 0.391553821765346f, 0.391613290667404f,
+ 0.391672587394012f, 0.391731712075852f, 0.391790664843562f,
+ 0.391849445827736f, 0.391908055158926f, 0.391966492967641f,
+ 0.392024759384347f, 0.392082854539465f, 0.392140778563375f,
+ 0.392198531586412f, 0.392256113738870f, 0.392313525150997f,
+ 0.392370765953001f, 0.392427836275045f, 0.392484736247248f,
+ 0.392541465999688f, 0.392598025662398f, 0.392654415365369f,
+ 0.392710635238548f, 0.392766685411840f, 0.392822566015106f,
+ 0.392878277178163f, 0.388572091023208f, 0.388627463695129f,
+ 0.388682667316036f, 0.388737702015574f, 0.388792567923345f,
+ 0.388847265168909f, 0.388901793881780f, 0.388956154191431f,
+ 0.389010346227292f, 0.389064370118749f, 0.389118225995145f,
+ 0.389171913985779f, 0.389225434219908f, 0.389278786826745f,
+ 0.389331971935462f, 0.389384989675184f, 0.389437840174996f,
+ 0.389490523563938f, 0.389543039971008f, 0.389595389525160f,
+ 0.389647572355306f, 0.389699588590314f, 0.389751438359007f,
+ 0.389803121790169f, 0.389854639012537f, 0.389905990154806f,
+ 0.389957175345630f, 0.390008194713615f, 0.390059048387330f,
+ 0.390109736495295f, 0.390160259165990f, 0.390210616527851f,
+ 0.390260808709272f, 0.390310835838602f, 0.390360698044147f,
+ 0.390410395454172f, 0.390459928196896f, 0.390509296400497f,
+ 0.390558500193108f, 0.390607539702819f, 0.390656415057679f,
+ 0.390705126385692f, 0.390753673814819f, 0.390802057472977f,
+ 0.390850277488041f, 0.390898333987844f, 0.390946227100173f,
+ 0.390993956952773f, 0.386679795665769f, 0.386727199381975f,
+ 0.386774440221429f, 0.386821518311702f, 0.386868433780325f,
+ 0.386915186754783f, 0.386961777362520f, 0.387008205730934f,
+ 0.387054471987382f, 0.387100576259178f, 0.387146518673590f,
+ 0.387192299357847f, 0.387237918439132f, 0.387283376044585f,
+ 0.387328672301304f, 0.387373807336342f, 0.387418781276710f,
+ 0.387463594249377f, 0.387508246381265f, 0.387552737799258f,
+ 0.387597068630192f, 0.387641239000863f, 0.387685249038022f,
+ 0.387729098868378f, 0.387772788618595f, 0.387816318415297f,
+ 0.387859688385061f, 0.387902898654424f, 0.387945949349877f,
+ 0.387988840597871f, 0.388031572524811f, 0.388074145257060f,
+ 0.388116558920937f, 0.388158813642719f, 0.388200909548640f,
+ 0.388242846764889f, 0.388284625417613f, 0.388326245632916f,
+ 0.388367707536857f, 0.388409011255455f, 0.388450156914683f,
+ 0.388491144640472f, 0.388531974558710f, 0.388572646795240f,
+ 0.388613161475865f, 0.388653518726341f, 0.388693718672384f,
+ 0.388733761439666f, 0.388773647153814f, 0.388813375940413f,
+ 0.388852947925006f, 0.388892363233092f, 0.388931621990125f,
+ 0.384608996313940f, 0.384647942345062f, 0.384686732201239f,
+ 0.384725366007753f, 0.384763843889844f, 0.384802165972708f,
+ 0.384840332381498f, 0.384878343241323f, 0.384916198677251f,
+ 0.384953898814305f, 0.384991443777464f, 0.385028833691666f,
+ 0.385066068681804f, 0.385103148872729f, 0.385140074389248f,
+ 0.385176845356125f, 0.385213461898081f, 0.385249924139793f,
+ 0.385286232205896f, 0.385322386220981f, 0.385358386309596f,
+ 0.385394232596246f, 0.385429925205392f, 0.385465464261452f,
+ 0.385500849888801f, 0.385536082211772f, 0.385571161354653f,
+ 0.385606087441689f, 0.385640860597082f, 0.385675480944992f,
+ 0.385709948609534f, 0.385744263714780f, 0.385778426384760f,
+ 0.385812436743460f, 0.385846294914823f, 0.385880001022748f,
+ 0.385913555191092f, 0.385946957543667f, 0.385980208204245f,
+ 0.386013307296550f, 0.386046254944268f, 0.386079051271037f,
+ 0.386111696400456f, 0.386144190456078f, 0.386176533561413f,
+ 0.386208725839929f, 0.386240767415049f, 0.386272658410155f,
+ 0.386304398948585f, 0.386335989153633f, 0.386367429148549f,
+ 0.386398719056542f, 0.386429859000777f, 0.386460849104374f,
+ 0.386491689490413f, 0.386522380281929f, 0.386552921601912f,
+ 0.386583313573312f, 0.386613556319033f, 0.386643649961939f,
+ 0.386673594624847f, 0.386703390430534f, 0.386733037501732f,
+ 0.386762535961129f, 0.382430157923795f, 0.382459359527486f,
+ 0.382488412887186f, 0.382517318125409f, 0.382546075364630f,
+ 0.382574684727277f, 0.382603146335737f, 0.382631460312354f,
+ 0.382659626779427f, 0.382687645859213f, 0.382715517673926f,
+ 0.382743242345736f, 0.382770819996769f, 0.382798250749110f,
+ 0.382825534724800f, 0.382852672045835f, 0.382879662834169f,
+ 0.382906507211714f, 0.382933205300338f, 0.382959757221863f,
+ 0.382986163098072f, 0.383012423050702f, 0.383038537201448f,
+ 0.383064505671961f, 0.383090328583849f, 0.383116006058678f,
+ 0.383141538217968f, 0.383166925183199f, 0.383192167075804f,
+ 0.383217264017177f, 0.383242216128666f, 0.383267023531576f,
+ 0.383291686347169f, 0.383316204696664f, 0.383340578701237f,
+ 0.383364808482021f, 0.383388894160103f, 0.383412835856531f,
+ 0.383436633692307f, 0.383460287788389f, 0.383483798265695f,
+ 0.383507165245098f, 0.383530388847426f, 0.383553469193466f,
+ 0.383576406403962f, 0.383599200599613f, 0.383621851901075f,
+ 0.383644360428963f, 0.383666726303846f, 0.383688949646252f,
+ 0.383711030576663f, 0.383732969215520f, 0.383754765683220f,
+ 0.383776420100117f, 0.383797932586521f, 0.383819303262701f,
+ 0.383840532248879f, 0.383861619665237f, 0.383882565631912f,
+ 0.383903370268999f, 0.383924033696548f, 0.383944556034568f,
+ 0.383964937403022f, 0.383985177921832f, 0.384005277710877f,
+ 0.384025236889990f, 0.384045055578963f, 0.384064733897545f,
+ 0.384084271965440f, 0.384103669902310f, 0.384122927827773f,
+ 0.384142045861405f, 0.384161024122738f, 0.384179862731259f,
+ 0.384198561806415f, 0.384217121467608f, 0.384235541834196f,
+ 0.384253823025494f, 0.384271965160776f, 0.384289968359270f,
+ 0.384307832740163f, 0.384325558422595f, 0.384343145525668f,
+ 0.384360594168436f, 0.384377904469912f, 0.384395076549067f,
+ 0.384412110524825f, 0.384429006516070f, 0.384445764641642f,
+ 0.384462385020336f, 0.384478867770907f, 0.384495213012063f,
+ 0.384511420862471f, 0.384527491440755f, 0.384543424865495f,
+ 0.384559221255226f, 0.380213152720866f, 0.380228675396019f,
+ 0.380244061391516f, 0.380259310825718f, 0.380274423816947f,
+ 0.380289400483481f, 0.380304240943551f, 0.380318945315350f,
+ 0.380333513717025f, 0.380347946266679f, 0.380362243082373f,
+ 0.380376404282125f, 0.380390429983908f, 0.380404320305654f,
+ 0.380418075365251f, 0.380431695280542f, 0.380445180169328f,
+ 0.380458530149368f, 0.380471745338376f, 0.380484825854023f,
+ 0.380497771813937f, 0.380510583335702f, 0.380523260536860f,
+ 0.380535803534910f, 0.380548212447305f, 0.380560487391458f,
+ 0.380572628484737f, 0.380584635844466f, 0.380596509587927f,
+ 0.380608249832359f, 0.380619856694957f, 0.380631330292873f,
+ 0.380642670743215f, 0.380653878163048f, 0.380664952669394f,
+ 0.380675894379233f, 0.380686703409499f, 0.380697379877085f,
+ 0.380707923898839f, 0.380718335591567f, 0.380728615072032f,
+ 0.380738762456952f, 0.380748777863002f, 0.380758661406816f,
+ 0.380768413204982f, 0.380778033374047f, 0.380787522030512f,
+ 0.380796879290837f, 0.380806105271438f, 0.380815200088688f,
+ 0.380824163858915f, 0.380832996698407f, 0.380841698723405f,
+ 0.380850270050110f, 0.380858710794677f, 0.380867021073220f,
+ 0.380875201001808f, 0.380883250696466f, 0.380891170273180f,
+ 0.380898959847887f, 0.380906619536485f, 0.380914149454827f,
+ 0.380921549718722f, 0.380928820443937f, 0.380935961746195f,
+ 0.380942973741177f, 0.380949856544519f, 0.380956610271814f,
+ 0.380963235038613f, 0.380969730960421f, 0.380976098152703f,
+ 0.380982336730880f, 0.380988446810326f, 0.380994428506377f,
+ 0.381000281934323f, 0.381006007209410f, 0.381011604446843f,
+ 0.381017073761781f, 0.381022415269341f, 0.381027629084598f,
+ 0.381032715322582f, 0.381037674098280f, 0.381042505526636f,
+ 0.381047209722551f, 0.381051786800881f, 0.381056236876441f,
+ 0.381060560064002f, 0.381064756478290f, 0.381068826233990f,
+ 0.381072769445743f, 0.381076586228145f, 0.381080276695752f,
+ 0.381083840963073f, 0.381087279144577f, 0.381090591354688f,
+ 0.381093777707787f, 0.381096838318210f, 0.381099773300253f,
+ 0.381102582768166f, 0.381105266836158f, 0.381107825618392f,
+ 0.381110259228989f, 0.381112567782028f, 0.381114751391543f,
+ 0.381116810171524f, 0.381118744235920f, 0.381120553698635f,
+ 0.381122238673531f, 0.381123799274425f, 0.381125235615091f,
+ 0.381126547809262f, 0.381127735970624f, 0.381128800212824f,
+ 0.381129740649461f, 0.381130557394094f, 0.381131250560238f,
+ 0.381131820261363f, 0.381132266610899f, 0.381132589722230f,
+ 0.381132789708696f, 0.381132866683598f, 0.381132820760188f,
+ 0.381132652051679f, 0.381132360671238f, 0.381131946731992f,
+ 0.381131410347020f, 0.381130751629362f, 0.381129970692011f,
+ 0.381129067647921f, 0.381128042609999f, 0.381126895691109f,
+ 0.381125627004075f, 0.381124236661673f, 0.381122724776639f,
+ 0.381121091461664f, 0.381119336829397f, 0.381117460992443f,
+ 0.381115464063363f, 0.381113346154677f, 0.381111107378858f,
+ 0.381108747848338f, 0.381106267675507f, 0.381103666972709f,
+ 0.381100945852245f, 0.381098104426374f, 0.381095142807312f,
+ 0.381092061107230f, 0.381088859438256f, 0.381085537912476f,
+ 0.381082096641931f, 0.381078535738620f, 0.381074855314498f,
+ 0.381071055481477f, 0.381067136351425f, 0.381063098036167f,
+ 0.381058940647486f, 0.381054664297119f, 0.381050269096762f,
+ 0.381045755158066f, 0.381041122592641f, 0.381036371512051f,
+ 0.381031502027818f, 0.381026514251421f, 0.381021408294295f,
+ 0.381016184267832f, 0.381010842283379f, 0.381005382452243f,
+ 0.380999804885685f, 0.380994109694924f, 0.380988296991135f,
+ 0.380982366885450f, 0.380976319488957f, 0.380970154912701f,
+ 0.380963873267684f, 0.380957474664866f, 0.380950959215160f,
+ 0.380944327029439f, 0.380937578218531f, 0.380930712893222f,
+ 0.380923731164253f, 0.380916633142322f, 0.380909418938086f,
+ 0.380902088662155f, 0.380894642425098f, 0.380887080337441f,
+ 0.380879402509665f, 0.380871609052208f, 0.380863700075466f,
+ 0.380855675689790f, 0.380847536005489f, 0.380839281132828f,
+ 0.380830911182028f, 0.380822426263269f, 0.380813826486684f,
+ 0.380805111962367f, 0.380796282800365f, 0.380787339110683f,
+ 0.380778281003283f, 0.380769108588083f, 0.380759821974958f,
+ 0.380750421273741f, 0.380740906594218f, 0.380731278046136f,
+ 0.380721535739195f, 0.380711679783055f, 0.380701710287330f,
+ 0.380691627361591f, 0.380681431115367f, 0.380671121658143f,
+ 0.380660699099360f, 0.380650163548417f, 0.376277787107090f,
+ 0.376267025899846f, 0.376256152028377f, 0.376245165601906f,
+ 0.376234066729615f, 0.376222855520642f, 0.376211532084083f,
+ 0.376200096528988f, 0.376188548964365f, 0.376176889499179f,
+ 0.376165118242352f, 0.376153235302761f, 0.376141240789241f,
+ 0.376129134810584f, 0.376116917475536f, 0.376104588892804f,
+ 0.376092149171047f, 0.376079598418884f, 0.376066936744890f,
+ 0.376054164257595f, 0.376041281065488f, 0.376028287277012f,
+ 0.376015183000569f, 0.376001968344516f, 0.375988643417169f,
+ 0.375975208326797f, 0.375961663181629f, 0.375948008089849f,
+ 0.375934243159598f, 0.375920368498973f, 0.375906384216028f,
+ 0.375892290418775f, 0.375878087215181f, 0.375863774713170f,
+ 0.375849353020623f, 0.375834822245377f, 0.375820182495227f,
+ 0.375805433877922f, 0.375790576501171f, 0.375775610472637f,
+ 0.375760535899941f, 0.375745352890661f, 0.375730061552329f,
+ 0.375714661992437f, 0.375699154318431f, 0.375683538637716f,
+ 0.375667815057652f, 0.375651983685556f, 0.375636044628702f,
+ 0.375619997994319f, 0.375603843889596f, 0.375587582421675f,
+ 0.375571213697657f, 0.375554737824598f, 0.375538154909513f,
+ 0.375521465059370f, 0.375504668381098f, 0.375487764981579f,
+ 0.375470754967654f, 0.375453638446118f, 0.375436415523726f,
+ 0.375419086307187f, 0.375401650903168f, 0.375384109418291f,
+ 0.375366461959138f, 0.375348708632243f, 0.375330849544101f,
+ 0.375312884801161f, 0.375294814509829f, 0.370914910768890f,
+ 0.370896629699820f, 0.370878243401316f, 0.370859751979612f,
+ 0.370841155540898f, 0.370822454191318f, 0.370803648036976f,
+ 0.370784737183932f, 0.370765721738200f, 0.370746601805755f,
+ 0.370727377492524f, 0.370708048904394f, 0.370688616147207f,
+ 0.370669079326762f, 0.370649438548815f, 0.370629693919078f,
+ 0.370609845543221f, 0.370589893526868f, 0.370569837975601f,
+ 0.370549678994961f, 0.370529416690441f, 0.370509051167494f,
+ 0.370488582531529f, 0.370468010887911f, 0.370447336341962f,
+ 0.370426558998959f, 0.370405678964140f, 0.370384696342694f,
+ 0.370363611239771f, 0.370342423760476f, 0.370321134009869f,
+ 0.370299742092970f, 0.370278248114753f, 0.370256652180149f,
+ 0.370234954394047f, 0.370213154861292f, 0.370191253686683f,
+ 0.370169250974980f, 0.370147146830898f, 0.370124941359106f,
+ 0.370102634664233f, 0.370080226850863f, 0.370057718023537f,
+ 0.370035108286753f, 0.370012397744965f, 0.369989586502583f,
+ 0.369966674663976f, 0.369943662333467f, 0.369920549615336f,
+ 0.369897336613821f, 0.369874023433116f, 0.369850610177372f,
+ 0.365465368943117f, 0.365441755849571f, 0.365418042993176f,
+ 0.365394230477909f, 0.365370318407704f, 0.365346306886452f,
+ 0.365322196017997f, 0.365297985906144f, 0.365273676654654f,
+ 0.365249268367241f, 0.365224761147580f, 0.365200155099300f,
+ 0.365175450325988f, 0.365150646931187f, 0.365125745018395f,
+ 0.365100744691070f, 0.365075646052624f, 0.365050449206426f,
+ 0.365025154255803f, 0.364999761304037f, 0.364974270454366f,
+ 0.364948681809988f, 0.364922995474054f, 0.364897211549674f,
+ 0.364871330139912f, 0.364845351347791f, 0.364819275276291f,
+ 0.364793102028345f, 0.364766831706847f, 0.364740464414645f,
+ 0.364714000254544f, 0.364687439329305f, 0.364660781741648f,
+ 0.364634027594247f, 0.364607176989734f, 0.364580230030696f,
+ 0.364553186819680f, 0.364526047459185f, 0.364498812051671f,
+ 0.364471480699551f, 0.364444053505197f, 0.364416530570936f,
+ 0.364388911999053f, 0.364361197891790f, 0.359971660343764f,
+ 0.359943755472287f, 0.359915755371891f, 0.359887660144644f,
+ 0.359859469892569f, 0.359831184717647f, 0.359802804721815f,
+ 0.359774330006967f, 0.359745760674952f, 0.359717096827579f,
+ 0.359688338566610f, 0.359659485993765f, 0.359630539210721f,
+ 0.359601498319112f, 0.359572363420526f, 0.359543134616511f,
+ 0.359513812008569f, 0.359484395698161f, 0.359454885786701f,
+ 0.359425282375564f, 0.359395585566078f, 0.359365795459529f,
+ 0.359335912157159f, 0.359305935760169f, 0.359275866369713f,
+ 0.359245704086904f, 0.359215449012811f, 0.359185101248459f,
+ 0.359154660894830f, 0.359124128052863f, 0.359093502823453f,
+ 0.359062785307452f, 0.359031975605668f, 0.359001073818866f,
+ 0.358970080047767f, 0.358938994393051f, 0.358907816955351f,
+ 0.358876547835258f, 0.358845187133321f, 0.354452006942467f,
+ 0.354420463378311f, 0.354388828533694f, 0.354357102508990f,
+ 0.354325285404530f, 0.354293377320600f, 0.354261378357445f,
+ 0.354229288615266f, 0.354197108194219f, 0.354164837194419f,
+ 0.354132475715934f, 0.354100023858793f, 0.354067481722978f,
+ 0.354034849408430f, 0.354002127015045f, 0.353969314642676f,
+ 0.353936412391133f, 0.353903420360182f, 0.353870338649546f,
+ 0.353837167358905f, 0.353803906587893f, 0.353770556436105f,
+ 0.353737117003089f, 0.353703588388350f, 0.353669970691352f,
+ 0.353636264011513f, 0.353602468448208f, 0.353568584100769f,
+ 0.353534611068485f, 0.353500549450601f, 0.353466399346319f,
+ 0.353432160854798f, 0.353397834075151f, 0.353363419106450f,
+ 0.353328916047724f, 0.353294324997957f, 0.353259646056089f,
+ 0.348863151313442f, 0.348828296884025f, 0.348793354859070f,
+ 0.348758325337345f, 0.348723208417575f, 0.348688004198440f,
+ 0.348652712778576f, 0.348617334256578f, 0.348581868730996f,
+ 0.348546316300336f, 0.348510677063062f, 0.348474951117594f,
+ 0.348439138562308f, 0.348403239495538f, 0.348367254015573f,
+ 0.348331182220659f, 0.348295024208999f, 0.348258780078753f,
+ 0.348222449928035f, 0.348186033854919f, 0.348149531957434f,
+ 0.348112944333565f, 0.348076271081254f, 0.348039512298401f,
+ 0.348002668082859f, 0.347965738532442f, 0.347928723744916f,
+ 0.347891623818009f, 0.347854438849400f, 0.347817168936728f,
+ 0.347779814177587f, 0.347742374669529f, 0.347704850510061f,
+ 0.347667241796648f, 0.343267820619132f, 0.343230043090047f,
+ 0.343192181299149f, 0.343154235343728f, 0.343116205321032f,
+ 0.343078091328263f, 0.343039893462582f, 0.343001611821106f,
+ 0.342963246500909f, 0.342924797599019f, 0.342886265212423f,
+ 0.342847649438065f, 0.342808950372843f, 0.342770168113615f,
+ 0.342731302757191f, 0.342692354400342f, 0.342653323139794f,
+ 0.342614209072227f, 0.342575012294282f, 0.342535732902554f,
+ 0.342496370993594f, 0.342456926663912f, 0.342417400009971f,
+ 0.342377791128193f, 0.342338100114958f, 0.342298327066598f,
+ 0.342258472079406f, 0.342218535249630f, 0.342178516673473f,
+ 0.342138416447096f, 0.342098234666617f, 0.342057971428110f,
+ 0.342017626827605f, 0.337615472953512f, 0.337574965916929f,
+ 0.337534377806179f, 0.337493708717118f, 0.337452958745561f,
+ 0.337412127987276f, 0.337371216537989f, 0.337330224493385f,
+ 0.337289151949102f, 0.337247999000736f, 0.337206765743839f,
+ 0.337165452273921f, 0.337124058686446f, 0.337082585076838f,
+ 0.337041031540475f, 0.336999398172692f, 0.336957685068781f,
+ 0.336915892323990f, 0.336874020033523f, 0.336832068292543f,
+ 0.336790037196167f, 0.336747926839469f, 0.336705737317481f,
+ 0.336663468725190f, 0.336621121157539f, 0.336578694709430f,
+ 0.336536189475720f, 0.336493605551223f, 0.336450943030708f,
+ 0.336408202008902f, 0.336365382580488f, 0.336322484840108f,
+ 0.336279508882355f, 0.331874726794207f, 0.331831594685327f,
+ 0.331788384642604f, 0.331745096760461f, 0.331701731133276f,
+ 0.331658287855384f, 0.331614767021079f, 0.331571168724609f,
+ 0.331527493060178f, 0.331483740121949f, 0.331439910004039f,
+ 0.331396002800524f, 0.331352018605435f, 0.331307957512759f,
+ 0.331263819616440f, 0.331219605010381f, 0.331175313788438f,
+ 0.331130946044424f, 0.331086501872111f, 0.331041981365225f,
+ 0.330997384617451f, 0.330952711722427f, 0.330907962773751f,
+ 0.330863137864976f, 0.330818237089610f, 0.330773260541122f,
+ 0.330728208312932f, 0.330683080498421f, 0.330637877190924f,
+ 0.330592598483733f, 0.330547244470097f, 0.330501815243221f,
+ 0.326094582888691f, 0.326049003514778f, 0.326003349206981f,
+ 0.325957620058331f, 0.325911816161815f, 0.325865937610379f,
+ 0.325819984496923f, 0.325773956914305f, 0.325727854955339f,
+ 0.325681678712794f, 0.325635428279400f, 0.325589103747838f,
+ 0.325542705210749f, 0.325496232760730f, 0.325449686490334f,
+ 0.325403066492070f, 0.325356372858405f, 0.325309605681762f,
+ 0.325262765054519f, 0.325215851069012f, 0.325168863817535f,
+ 0.325121803392334f, 0.325074669885617f, 0.325027463389544f,
+ 0.324980183996233f, 0.324932831797761f, 0.324885406886157f,
+ 0.324837909353411f, 0.324790339291466f, 0.324742696792224f,
+ 0.324694981947541f, 0.320285466841654f, 0.320237607581489f,
+ 0.320189676251195f, 0.320141672942456f, 0.320093597746911f,
+ 0.320045450756158f, 0.319997232061749f, 0.319948941755193f,
+ 0.319900579927957f, 0.319852146671464f, 0.319803642077092f,
+ 0.319755066236177f, 0.319706419240011f, 0.319657701179843f,
+ 0.319608912146878f, 0.319560052232277f, 0.319511121527159f,
+ 0.319462120122598f, 0.319413048109625f, 0.319363905579229f,
+ 0.319314692622353f, 0.319265409329898f, 0.319216055792721f,
+ 0.319166632101636f, 0.319117138347413f, 0.319067574620779f,
+ 0.319017941012417f, 0.318968237612967f, 0.318918464513024f,
+ 0.318868621803143f, 0.318818709573831f, 0.318768727915555f,
+ 0.314356948911160f, 0.314306828666179f, 0.314256639263369f,
+ 0.314206380793024f, 0.314156053345391f, 0.314105657010674f,
+ 0.314055191879036f, 0.314004658040593f, 0.313954055585421f,
+ 0.313903384603549f, 0.313852645184966f, 0.313801837419615f,
+ 0.313750961397396f, 0.313700017208166f, 0.313649004941739f,
+ 0.313597924687883f, 0.313546776536326f, 0.313495560576751f,
+ 0.313444276898796f, 0.313392925592057f, 0.313341506746087f,
+ 0.313290020450394f, 0.313238466794443f, 0.313186845867658f,
+ 0.313135157759415f, 0.313083402559049f, 0.313031580355852f,
+ 0.312979691239072f, 0.312927735297912f, 0.312875712621533f,
+ 0.312823623299054f, 0.312771467419547f, 0.308357517064465f,
+ 0.308305228337950f, 0.308252873321368f, 0.308200452103618f,
+ 0.308147964773557f, 0.308095411419997f, 0.308042792131707f,
+ 0.307990106997414f, 0.307937356105799f, 0.307884539545501f,
+ 0.307831657405116f, 0.307778709773194f, 0.307725696738244f,
+ 0.307672618388731f, 0.307619474813076f, 0.307566266099656f,
+ 0.307512992336805f, 0.307459653612815f, 0.307406250015931f,
+ 0.307352781634359f, 0.307299248556257f, 0.307245650869742f,
+ 0.307191988662888f, 0.307138262023724f, 0.307084471040236f,
+ 0.307030615800366f, 0.306976696392013f, 0.306922712903033f,
+ 0.306868665421238f, 0.306814554034397f, 0.306760378830233f,
+ 0.306706139896429f, 0.306651837320622f, 0.302235743182830f,
+ 0.302181313585758f, 0.302126820609336f, 0.302072264341027f,
+ 0.302017644868253f, 0.301962962278390f, 0.301908216658771f,
+ 0.301853408096686f, 0.301798536679381f, 0.301743602494059f,
+ 0.301688605627880f, 0.301633546167958f, 0.301578424201366f,
+ 0.301523239815132f, 0.301467993096242f, 0.301412684131638f,
+ 0.301357313008217f, 0.301301879812834f, 0.301246384632300f,
+ 0.301190827553382f, 0.301135208662805f, 0.301079528047249f,
+ 0.301023785793351f, 0.300967981987704f, 0.300912116716858f,
+ 0.300856190067319f, 0.300800202125552f, 0.300744152977974f,
+ 0.300688042710961f, 0.300631871410846f, 0.300575639163918f,
+ 0.300519346056422f, 0.300462992174559f, 0.300406577604487f,
+ 0.295988374424745f, 0.295931838736557f, 0.295875242618375f,
+ 0.295818586156181f, 0.295761869435917f, 0.295705092543480f,
+ 0.295648255564723f, 0.295591358585456f, 0.295534401691445f,
+ 0.295477384968414f, 0.295420308502041f, 0.295363172377963f,
+ 0.295305976681771f, 0.295248721499016f, 0.295191406915201f,
+ 0.295134033015788f, 0.295076599886197f, 0.295019107611801f,
+ 0.294961556277931f, 0.294903945969875f, 0.294846276772877f,
+ 0.294788548772138f, 0.294730762052815f, 0.294672916700020f,
+ 0.294615012798824f, 0.294557050434254f, 0.294499029691292f,
+ 0.294440950654877f, 0.294382813409906f, 0.294324618041230f,
+ 0.294266364633658f, 0.294208053271955f, 0.294149684040843f,
+ 0.294091257025001f, 0.294032772309062f, 0.293974229977617f,
+ 0.289553902107637f, 0.289495244798780f, 0.289436530127930f,
+ 0.289377758179504f, 0.289318929037873f, 0.289260042787369f,
+ 0.289201099512278f, 0.289142099296842f, 0.289083042225260f,
+ 0.289023928381689f, 0.288964757850239f, 0.288905530714981f,
+ 0.288846247059938f, 0.288786906969092f, 0.288727510526381f,
+ 0.288668057815699f, 0.288608548920898f, 0.288548983925785f,
+ 0.288489362914122f, 0.288429685969631f, 0.288369953175988f,
+ 0.288310164616827f, 0.288250320375736f, 0.288190420536262f,
+ 0.288130465181907f, 0.288070454396130f, 0.288010388262347f,
+ 0.287950266863930f, 0.287890090284206f, 0.287829858606460f,
+ 0.287769571913934f, 0.287709230289826f, 0.287648833817288f,
+ 0.287588382579433f, 0.287527876659327f, 0.287467316139992f,
+ 0.287406701104411f, 0.287346031635518f, 0.287285307816206f,
+ 0.282862801721747f, 0.282801969450102f, 0.282741083076456f,
+ 0.282680142683527f, 0.282619148353990f, 0.282558100170477f,
+ 0.282496998215576f, 0.282435842571830f, 0.282374633321742f,
+ 0.282313370547767f, 0.282252054332321f, 0.282190684757772f,
+ 0.282129261906449f, 0.282067785860633f, 0.282006256702564f,
+ 0.281944674514439f, 0.281883039378409f, 0.281821351376584f,
+ 0.281759610591029f, 0.281697817103765f, 0.281635970996772f,
+ 0.281574072351982f, 0.281512121251289f, 0.281450117776538f,
+ 0.281388062009534f, 0.281325954032037f, 0.281263793925765f,
+ 0.281201581772391f, 0.281139317653543f, 0.281077001650809f,
+ 0.281014633845731f, 0.280952214319808f, 0.280889743154496f,
+ 0.280827220431206f, 0.280764646231307f, 0.280702020636124f,
+ 0.280639343726938f, 0.280576615584986f, 0.280513836291463f,
+ 0.280451005927520f, 0.280388124574262f, 0.280325192312755f,
+ 0.275900481216440f, 0.275837447381448f, 0.275774362881135f,
+ 0.275711227796389f, 0.275648042208058f, 0.275584806196942f,
+ 0.275521519843800f, 0.275458183229346f, 0.275394796434254f,
+ 0.275331359539149f, 0.275267872624617f, 0.275204335771198f,
+ 0.275140749059389f, 0.275077112569644f, 0.275013426382372f,
+ 0.274949690577941f, 0.274885905236672f, 0.274822070438846f,
+ 0.274758186264697f, 0.274694252794419f, 0.274630270108159f,
+ 0.274566238286022f, 0.274502157408071f, 0.274438027554322f,
+ 0.274373848804750f, 0.274309621239286f, 0.274245344937817f,
+ 0.274181019980186f, 0.274116646446194f, 0.274052224415596f,
+ 0.273987753968107f, 0.273923235183394f, 0.273858668141084f,
+ 0.273794052920759f, 0.273729389601958f, 0.273664678264174f,
+ 0.273599918986861f, 0.273535111849425f, 0.273470256931231f,
+ 0.273405354311600f, 0.273340404069808f, 0.273275406285090f,
+ 0.273210361036635f, 0.273145268403589f, 0.273080128465056f,
+ 0.273014941300095f, 0.272949706987721f, 0.272884425606906f,
+ 0.272819097236579f, 0.268391993948047f, 0.268326571835307f,
+ 0.268261102969579f, 0.268195587429617f, 0.268130025294132f,
+ 0.268064416641790f, 0.267998761551216f, 0.267933060100989f,
+ 0.267867312369645f, 0.267801518435677f, 0.267735678377535f,
+ 0.267669792273623f, 0.267603860202304f, 0.267537882241896f,
+ 0.267471858470674f, 0.267405788966870f, 0.267339673808669f,
+ 0.267273513074218f, 0.267207306841617f, 0.267141055188921f,
+ 0.267074758194145f, 0.267008415935258f, 0.266942028490186f,
+ 0.266875595936813f, 0.266809118352976f, 0.266742595816472f,
+ 0.266676028405051f, 0.266609416196424f, 0.266542759268253f,
+ 0.266476057698160f, 0.266409311563722f, 0.266342520942473f,
+ 0.266275685911905f, 0.266208806549462f, 0.266141882932549f,
+ 0.266074915138525f, 0.266007903244705f, 0.265940847328363f,
+ 0.265873747466726f, 0.265806603736981f, 0.265739416216268f,
+ 0.265672184981686f, 0.265604910110289f, 0.265537591679087f,
+ 0.265470229765049f, 0.265402824445097f, 0.265335375796112f,
+ 0.265267883894930f, 0.265200348818344f, 0.265132770643102f,
+ 0.265065149445912f, 0.264997485303435f, 0.264929778292289f,
+ 0.264862028489049f, 0.264794235970247f, 0.264726400812370f,
+ 0.264658523091862f, 0.264590602885124f, 0.264522640268512f,
+ 0.264454635318341f, 0.264386588110879f, 0.264318498722353f,
+ 0.264250367228946f, 0.259820465699218f, 0.259752250224420f,
+ 0.259683992873027f, 0.259615693721046f, 0.259547352844442f,
+ 0.259478970319136f, 0.259410546221005f, 0.259342080625883f,
+ 0.259273573609560f, 0.259205025247783f, 0.259136435616255f,
+ 0.259067804790635f, 0.258999132846538f, 0.258930419859537f,
+ 0.258861665905160f, 0.258792871058893f, 0.258724035396177f,
+ 0.258655158992408f, 0.258586241922943f, 0.258517284263090f,
+ 0.258448286088117f, 0.258379247473248f, 0.258310168493662f,
+ 0.258241049224495f, 0.258171889740839f, 0.258102690117744f,
+ 0.258033450430215f, 0.257964170753213f, 0.257894851161658f,
+ 0.257825491730422f, 0.257756092534337f, 0.257686653648191f,
+ 0.257617175146727f, 0.257547657104645f, 0.257478099596601f,
+ 0.257408502697209f, 0.257338866481037f, 0.257269191022612f,
+ 0.257199476396415f, 0.257129722676885f, 0.257059929938416f,
+ 0.256990098255361f, 0.256920227702025f, 0.256850318352674f,
+ 0.256780370281527f, 0.256710383562763f, 0.256640358270512f,
+ 0.256570294478866f, 0.256500192261870f, 0.256430051693527f,
+ 0.256359872847795f, 0.256289655798589f, 0.256219400619781f,
+ 0.256149107385198f, 0.256078776168626f, 0.256008407043804f,
+ 0.255938000084429f, 0.255867555364156f, 0.255797072956594f,
+ 0.255726552935309f, 0.255655995373823f, 0.255585400345616f,
+ 0.255514767924123f, 0.255444098182735f, 0.255373391194802f,
+ 0.255302647033627f, 0.255231865772471f, 0.255161047484552f,
+ 0.255090192243042f, 0.255019300121073f, 0.254948371191731f,
+ 0.254877405528058f, 0.254806403203054f, 0.254735364289674f,
+ 0.254664288860830f, 0.254593176989391f, 0.254522028748181f,
+ 0.254450844209981f, 0.254379623447529f, 0.254308366533519f,
+ 0.254237073540600f, 0.254165744541380f, 0.254094379608422f,
+ 0.254022978814245f, 0.253951542231324f, 0.253880069932092f,
+ 0.253808561988937f, 0.253737018474204f, 0.253665439460195f,
+ 0.253593825019167f, 0.253522175223334f, 0.253450490144866f,
+ 0.253378769855891f, 0.253307014428491f, 0.253235223934706f,
+ 0.253163398446532f, 0.253091538035921f, 0.253019642774782f,
+ 0.252947712734979f, 0.252875747988335f, 0.252803748606627f,
+ 0.252731714661588f, 0.252659646224911f, 0.252587543368241f,
+ 0.252515406163181f, 0.252443234681292f, 0.252371028994090f,
+ 0.252298789173046f, 0.252226515289590f, 0.252154207415106f,
+ 0.252081865620937f, 0.252009489978380f, 0.251937080558689f,
+ 0.251864637433075f, 0.251792160672704f, 0.251719650348701f,
+ 0.251647106532145f, 0.251574529294072f, 0.251501918705475f,
+ 0.251429274837301f, 0.251356597760457f, 0.251283887545805f,
+ 0.251211144264161f, 0.251138367986300f, 0.251065558782954f,
+ 0.250992716724808f, 0.250919841882507f, 0.250846934326649f,
+ 0.250773994127792f, 0.250701021356448f, 0.250628016083085f,
+ 0.250554978378129f, 0.250481908311962f, 0.250408805954920f,
+ 0.250335671377300f, 0.250262504649351f, 0.250189305841280f,
+ 0.250116075023251f, 0.250042812265383f, 0.249969517637753f,
+ 0.249896191210394f, 0.249822833053293f, 0.249749443236397f,
+ 0.249676021829606f, 0.249602568902779f, 0.249529084525730f,
+ 0.249455568768230f, 0.249382021700006f, 0.249308443390741f,
+ 0.249234833910075f, 0.249161193327604f, 0.249087521712880f,
+ 0.249013819135412f, 0.248940085664666f, 0.248866321370063f,
+ 0.248792526320980f, 0.248718700586753f, 0.248644844236672f,
+ 0.248570957339983f, 0.248497039965891f, 0.248423092183554f,
+ 0.248349114062088f, 0.248275105670567f, 0.248201067078019f,
+ 0.248126998353429f, 0.248052899565738f, 0.247978770783845f,
+ 0.247904612076603f, 0.247830423512822f, 0.247756205161271f,
+ 0.247681957090672f, 0.247607679369705f, 0.247533372067005f,
+ 0.247459035251165f, 0.247384668990733f, 0.247310273354215f,
+ 0.247235848410072f, 0.247161394226722f, 0.247086910872538f,
+ 0.247012398415851f, 0.246937856924948f, 0.242501558460495f,
+ 0.242426959105845f, 0.242352330921577f, 0.242277673975804f,
+ 0.242202988336594f, 0.242128274071971f, 0.242053531249917f,
+ 0.241978759938370f, 0.241903960205222f, 0.241829132118326f,
+ 0.241754275745486f, 0.241679391154467f, 0.241604478412987f,
+ 0.241529537588722f, 0.241454568749305f, 0.241379571962322f,
+ 0.241304547295320f, 0.241229494815798f, 0.241154414591216f,
+ 0.241079306688985f, 0.241004171176476f, 0.240929008121016f,
+ 0.240853817589887f, 0.240778599650329f, 0.240703354369537f,
+ 0.240628081814662f, 0.240552782052813f, 0.240477455151054f,
+ 0.240402101176407f, 0.240326720195847f, 0.240251312276309f,
+ 0.240175877484683f, 0.240100415887814f, 0.240024927552506f,
+ 0.239949412545517f, 0.239873870933562f, 0.239798302783312f,
+ 0.239722708161397f, 0.239647087134399f, 0.239571439768860f,
+ 0.239495766131276f, 0.239420066288100f, 0.239344340305743f,
+ 0.239268588250570f, 0.239192810188903f, 0.239117006187022f,
+ 0.239041176311160f, 0.238965320627509f, 0.238889439202216f,
+ 0.238813532101387f, 0.238737599391080f, 0.238661641137313f,
+ 0.238585657406058f, 0.238509648263246f, 0.238433613774760f,
+ 0.238357554006444f, 0.238281469024095f, 0.238205358893469f,
+ 0.238129223680276f, 0.238053063450183f, 0.237976878268814f,
+ 0.237900668201750f, 0.237824433314526f, 0.237748173672635f,
+ 0.237671889341525f, 0.237595580386603f, 0.237519246873230f,
+ 0.237442888866723f, 0.237366506432358f, 0.237290099635364f,
+ 0.237213668540929f, 0.237137213214196f, 0.237060733720264f,
+ 0.236984230124190f, 0.236907702490985f, 0.236831150885619f,
+ 0.236754575373016f, 0.236677976018057f, 0.236601352885581f,
+ 0.236524706040380f, 0.236448035547206f, 0.236371341470764f,
+ 0.236294623875718f, 0.236217882826687f, 0.236141118388246f,
+ 0.236064330624927f, 0.235987519601219f, 0.235910685381566f,
+ 0.235833828030369f, 0.235756947611985f, 0.235680044190727f,
+ 0.235603117830866f, 0.235526168596627f, 0.235449196552193f,
+ 0.235372201761703f, 0.235295184289252f, 0.235218144198891f,
+ 0.235141081554629f, 0.235063996420428f, 0.234986888860210f,
+ 0.234909758937852f, 0.234832606717186f, 0.234755432262002f,
+ 0.234678235636045f, 0.234601016903018f, 0.234523776126579f,
+ 0.234446513370342f, 0.234369228697878f, 0.234291922172716f,
+ 0.234214593858338f, 0.234137243818184f, 0.234059872115652f,
+ 0.233982478814092f, 0.233905063976814f, 0.233827627667084f,
+ 0.233750169948123f, 0.233672690883109f, 0.233595190535175f,
+ 0.233517668967413f, 0.233440126242869f, 0.233362562424547f,
+ 0.233284977575406f, 0.233207371758361f, 0.233129745036284f,
+ 0.233052097472005f, 0.232974429128308f, 0.232896740067933f,
+ 0.232819030353578f, 0.232741300047898f, 0.232663549213500f,
+ 0.232585777912953f, 0.232507986208779f, 0.232430174163456f,
+ 0.232352341839420f, 0.232274489299062f, 0.232196616604730f,
+ 0.227756995811151f, 0.227679082995740f, 0.227601150213137f,
+ 0.227523197525514f, 0.227445224995001f, 0.227367232683684f,
+ 0.227289220653605f, 0.227211188966762f, 0.227133137685110f,
+ 0.227055066870559f, 0.226976976584978f, 0.226898866890189f,
+ 0.226820737847972f, 0.226742589520064f, 0.226664421968157f,
+ 0.226586235253900f, 0.226508029438898f, 0.226429804584713f,
+ 0.226351560752862f, 0.226273298004819f, 0.226195016402015f,
+ 0.226116716005836f, 0.226038396877625f, 0.225960059078682f,
+ 0.225881702670262f, 0.225803327713576f, 0.225724934269794f,
+ 0.225646522400040f, 0.225568092165393f, 0.225489643626893f,
+ 0.225411176845531f, 0.225332691882258f, 0.225254188797980f,
+ 0.225175667653558f, 0.225097128509812f, 0.225018571427517f,
+ 0.224939996467403f, 0.224861403690158f, 0.224782793156426f,
+ 0.224704164926808f, 0.224625519061859f, 0.224546855622093f,
+ 0.224468174667977f, 0.224389476259939f, 0.224310760458359f,
+ 0.224232027323575f, 0.224153276915881f, 0.224074509295528f,
+ 0.223995724522724f, 0.223916922657630f, 0.223838103760367f,
+ 0.223759267891009f, 0.223680415109590f, 0.223601545476097f,
+ 0.223522659050475f, 0.223443755892626f, 0.223364836062405f,
+ 0.223285899619627f, 0.223206946624062f, 0.223127977135435f,
+ 0.223048991213429f, 0.222969988917683f, 0.222890970307791f,
+ 0.222811935443306f, 0.222732884383734f, 0.222653817188539f,
+ 0.222574733917142f, 0.222495634628919f, 0.222416519383203f,
+ 0.222337388239283f, 0.222258241256404f, 0.222179078493767f,
+ 0.217738172002954f, 0.217658977858233f, 0.217579768111098f,
+ 0.217500542820575f, 0.217421302045647f, 0.217342045845254f,
+ 0.217262774278292f, 0.217183487403611f, 0.217104185280022f,
+ 0.217024867966288f, 0.216945535521129f, 0.216866188003224f,
+ 0.216786825471206f, 0.216707447983663f, 0.216628055599143f,
+ 0.216548648376148f, 0.216469226373135f, 0.216389789648521f,
+ 0.216310338260676f, 0.216230872267927f, 0.216151391728558f,
+ 0.216071896700810f, 0.215992387242879f, 0.215912863412916f,
+ 0.215833325269032f, 0.215753772869290f, 0.215674206271714f,
+ 0.215594625534279f, 0.215515030714921f, 0.215435421871530f,
+ 0.215355799061952f, 0.215276162343990f, 0.215196511775403f,
+ 0.215116847413907f, 0.215037169317173f, 0.214957477542830f,
+ 0.214877772148461f, 0.214798053191607f, 0.214718320729766f,
+ 0.214638574820389f, 0.214558815520887f, 0.214479042888626f,
+ 0.214399256980927f, 0.214319457855068f, 0.214239645568284f,
+ 0.214159820177766f, 0.214079981740661f, 0.214000130314072f,
+ 0.213920265955059f, 0.213840388720638f, 0.213760498667781f,
+ 0.213680595853417f, 0.213600680334430f, 0.213520752167662f,
+ 0.213440811409909f, 0.213360858117925f, 0.213280892348421f,
+ 0.213200914158062f, 0.213120923603470f, 0.208679192733648f,
+ 0.208599177620285f, 0.208519150312294f, 0.208439110866124f,
+ 0.208359059338178f, 0.208278995784815f, 0.208198920262354f,
+ 0.208118832827066f, 0.208038733535179f, 0.207958622442880f,
+ 0.207878499606310f, 0.207798365081566f, 0.207718218924703f,
+ 0.207638061191730f, 0.207557891938615f, 0.207477711221280f,
+ 0.207397519095604f, 0.207317315617423f, 0.207237100842528f,
+ 0.207156874826667f, 0.207076637625544f, 0.206996389294820f,
+ 0.206916129890112f, 0.206835859466992f, 0.206755578080990f,
+ 0.206675285787592f, 0.206594982642238f, 0.206514668700328f,
+ 0.206434344017215f, 0.206354008648211f, 0.206273662648582f,
+ 0.206193306073550f, 0.206112938978297f, 0.206032561417956f,
+ 0.205952173447621f, 0.205871775122340f, 0.205791366497116f,
+ 0.205710947626911f, 0.205630518566642f, 0.205550079371182f,
+ 0.205469630095361f, 0.205389170793964f, 0.205308701521733f,
+ 0.205228222333368f, 0.205147733283521f, 0.205067234426806f,
+ 0.204986725817787f, 0.204906207510990f, 0.204825679560893f,
+ 0.204745142021933f, 0.204664594948501f, 0.200222310387369f,
+ 0.200141744407996f, 0.200061169057067f, 0.199980584388797f,
+ 0.199899990457361f, 0.199819387316889f, 0.199738775021466f,
+ 0.199658153625134f, 0.199577523181893f, 0.199496883745697f,
+ 0.199416235370458f, 0.199335578110042f, 0.199254912018273f,
+ 0.199174237148931f, 0.199093553555752f, 0.199012861292428f,
+ 0.198932160412609f, 0.198851450969898f, 0.198770733017858f,
+ 0.198690006610006f, 0.198609271799814f, 0.198528528640714f,
+ 0.198447777186091f, 0.198367017489288f, 0.198286249603603f,
+ 0.198205473582292f, 0.198124689478565f, 0.198043897345590f,
+ 0.197963097236492f, 0.197882289204349f, 0.197801473302198f,
+ 0.197720649583031f, 0.197639818099798f, 0.197558978905402f,
+ 0.197478132052707f, 0.197397277594528f, 0.197316415583640f,
+ 0.197235546072773f, 0.197154669114612f, 0.197073784761801f,
+ 0.196992893066939f, 0.196911994082579f, 0.196831087861235f,
+ 0.196750174455372f, 0.196669253917415f, 0.196588326299744f,
+ 0.196507391654695f, 0.196426450034560f, 0.191983773484012f,
+ 0.191902818070409f, 0.191821855838336f, 0.191740886839911f,
+ 0.191659911127206f, 0.191578928752253f, 0.191497939767036f,
+ 0.191416944223500f, 0.191335942173542f, 0.191254933669018f,
+ 0.191173918761738f, 0.191092897503471f, 0.191011869945940f,
+ 0.190930836140826f, 0.190849796139763f, 0.190768749994346f,
+ 0.190687697756122f, 0.190606639476596f, 0.190525575207231f,
+ 0.190444504999443f, 0.190363428904605f, 0.190282346974049f,
+ 0.190201259259060f, 0.190120165810881f, 0.190039066680710f,
+ 0.189957961919703f, 0.189876851578970f, 0.189795735709579f,
+ 0.189714614362553f, 0.189633487588873f, 0.189552355439475f,
+ 0.189471217965251f, 0.189390075217050f, 0.189308927245676f,
+ 0.189227774101891f, 0.189146615836412f, 0.189065452499913f,
+ 0.188984284143024f, 0.188903110816330f, 0.188821932570374f,
+ 0.188740749455655f, 0.188659561522627f, 0.188578368821702f,
+ 0.188497171403246f, 0.188415969317584f, 0.188334762614995f,
+ 0.183891823338137f, 0.183810607552358f, 0.183729387300228f,
+ 0.183648162631853f, 0.183566933597294f, 0.183485700246567f,
+ 0.183404462629646f, 0.183323220796461f, 0.183241974796898f,
+ 0.183160724680798f, 0.183079470497960f, 0.182998212298140f,
+ 0.182916950131046f, 0.182835684046347f, 0.182754414093667f,
+ 0.182673140322583f, 0.182591862782633f, 0.182510581523309f,
+ 0.182429296594058f, 0.182348008044284f, 0.182266715923350f,
+ 0.182185420280571f, 0.182104121165221f, 0.182022818626530f,
+ 0.181941512713682f, 0.181860203475820f, 0.181778890962042f,
+ 0.181697575221403f, 0.181616256302912f, 0.181534934255536f,
+ 0.181453609128199f, 0.181372280969781f, 0.181290949829115f,
+ 0.181209615754995f, 0.181128278796168f, 0.181046939001338f,
+ 0.180965596419165f, 0.180884251098267f, 0.180802903087216f,
+ 0.180721552434541f, 0.180640199188728f, 0.180558843398218f,
+ 0.180477485111409f, 0.180396124376655f, 0.175953033234688f,
+ 0.175871667748931f, 0.175790299960027f, 0.175708929916157f,
+ 0.175627557665455f, 0.175546183256013f, 0.175464806735878f,
+ 0.175383428153054f, 0.175302047555502f, 0.175220664991136f,
+ 0.175139280507831f, 0.175057894153414f, 0.174976505975670f,
+ 0.174895116022341f, 0.174813724341124f, 0.174732330979673f,
+ 0.174650935985597f, 0.174569539406463f, 0.174488141289793f,
+ 0.174406741683064f, 0.174325340633713f, 0.174243938189129f,
+ 0.174162534396661f, 0.174081129303611f, 0.173999722957239f,
+ 0.173918315404761f, 0.173836906693349f, 0.173755496870131f,
+ 0.173674085982191f, 0.173592674076571f, 0.173511261200267f,
+ 0.173429847400233f, 0.173348432723377f, 0.173267017216566f,
+ 0.173185600926620f, 0.173104183900319f, 0.173022766184396f,
+ 0.172941347825542f, 0.172859928870403f, 0.172778509365582f,
+ 0.172697089357639f, 0.172615668893089f, 0.172534248018403f,
+ 0.172452826780009f, 0.168009677216713f, 0.167928255390010f,
+ 0.167846833338621f, 0.167765411108796f, 0.167683988746745f,
+ 0.167602566298634f, 0.167521143810582f, 0.167439721328668f,
+ 0.167358298898926f, 0.167276876567344f, 0.167195454379870f,
+ 0.167114032382406f, 0.167032610620809f, 0.166951189140896f,
+ 0.166869767988436f, 0.166788347209158f, 0.166706926848743f,
+ 0.166625506952833f, 0.166544087567022f, 0.166462668736864f,
+ 0.166381250507865f, 0.166299832925490f, 0.166218416035160f,
+ 0.166136999882253f, 0.166055584512100f, 0.165974169969991f,
+ 0.165892756301173f, 0.165811343550845f, 0.165729931764167f,
+ 0.165648520986252f, 0.165567111262170f, 0.165485702636949f,
+ 0.165404295155570f, 0.165322888862974f, 0.165241483804054f,
+ 0.165160080023663f, 0.165078677566607f, 0.164997276477651f,
+ 0.164915876801515f, 0.164834478582874f, 0.164753081866361f,
+ 0.164671686696565f, 0.164590293118030f, 0.164508901175258f,
+ 0.164427510912706f, 0.159984394367208f, 0.159903007598291f,
+ 0.159821622642703f, 0.159740239544724f, 0.159658858348595f,
+ 0.159577479098508f, 0.159496101838615f, 0.159414726613023f,
+ 0.159333353465794f, 0.159251982440948f, 0.159170613582459f,
+ 0.159089246934261f, 0.159007882540240f, 0.158926520444241f,
+ 0.158845160690063f, 0.158763803321464f, 0.158682448382156f,
+ 0.158601095915807f, 0.158519745966043f, 0.158438398576445f,
+ 0.158357053790551f, 0.158275711651853f, 0.158194372203803f,
+ 0.158113035489805f, 0.158031701553222f, 0.157950370437373f,
+ 0.157869042185531f, 0.157787716840929f, 0.157706394446753f,
+ 0.157625075046145f, 0.157543758682207f, 0.157462445397992f,
+ 0.157381135236513f, 0.157299828240738f, 0.157218524453592f,
+ 0.157137223917953f, 0.157055926676660f, 0.156974632772505f,
+ 0.156893342248236f, 0.156812055146560f, 0.156730771510136f,
+ 0.156649491381584f, 0.156568214803476f, 0.156486941818342f,
+ 0.156405672468670f, 0.156324406796900f, 0.151881416837854f,
+ 0.151800158649042f, 0.151718904265197f, 0.151637653728586f,
+ 0.151556407081432f, 0.151475164365916f, 0.151393925624172f,
+ 0.151312690898293f, 0.151231460230326f, 0.151150233662276f,
+ 0.151069011236103f, 0.150987792993723f, 0.150906578977011f,
+ 0.150825369227794f, 0.150744163787857f, 0.150662962698943f,
+ 0.150581766002748f, 0.150500573740927f, 0.150419385955089f,
+ 0.150338202686801f, 0.150257023977585f, 0.150175849868919f,
+ 0.150094680402237f, 0.150013515618932f, 0.149932355560350f,
+ 0.149851200267794f, 0.149770049782523f, 0.149688904145754f,
+ 0.149607763398657f, 0.149526627582362f, 0.149445496737952f,
+ 0.149364370906467f, 0.149283250128905f, 0.149202134446217f,
+ 0.149121023899313f, 0.149039918529058f, 0.148958818376273f,
+ 0.148877723481736f, 0.148796633886180f, 0.148715549630295f,
+ 0.148634470754727f, 0.148553397300078f, 0.148472329306907f,
+ 0.148391266815728f, 0.148310209867012f, 0.148229158501186f,
+ 0.148148112758633f, 0.148067072679692f, 0.143624310297081f,
+ 0.143543281666207f, 0.143462258819700f, 0.143381241797725f,
+ 0.143300230640400f, 0.143219225387804f, 0.143138226079968f,
+ 0.143057232756881f, 0.142976245458488f, 0.142895264224689f,
+ 0.142814289095343f, 0.142733320110263f, 0.142652357309218f,
+ 0.142571400731934f, 0.142490450418093f, 0.142409506407333f,
+ 0.142328568739249f, 0.142247637453390f, 0.142166712589265f,
+ 0.142085794186334f, 0.142004882284019f, 0.141923976921693f,
+ 0.141843078138688f, 0.141762185974292f, 0.141681300467748f,
+ 0.141600421658257f, 0.141519549584974f, 0.141438684287012f,
+ 0.141357825803438f, 0.141276974173279f, 0.141196129435513f,
+ 0.141115291629079f, 0.141034460792870f, 0.140953636965734f,
+ 0.140872820186478f, 0.140792010493862f, 0.140711207926605f,
+ 0.140630412523381f, 0.140549624322819f, 0.140468843363506f,
+ 0.140388069683985f, 0.140307303322754f, 0.140226544318269f,
+ 0.140145792708939f, 0.140065048533132f, 0.139984311829173f,
+ 0.139903582635339f, 0.139822860989867f, 0.139742146930948f,
+ 0.139661440496731f, 0.139580741725320f, 0.139500050654775f,
+ 0.139419367323112f, 0.134976963760728f, 0.134896296020705f,
+ 0.134815636133351f, 0.134734984136508f, 0.134654340067973f,
+ 0.134573703965498f, 0.134493075866795f, 0.134412455809528f,
+ 0.134331843831321f, 0.134251239969750f, 0.134170644262351f,
+ 0.134090056746613f, 0.134009477459984f, 0.133928906439867f,
+ 0.133848343723620f, 0.133767789348558f, 0.133687243351954f,
+ 0.133606705771033f, 0.133526176642981f, 0.133445656004937f,
+ 0.133365143893997f, 0.133284640347212f, 0.133204145401592f,
+ 0.133123659094101f, 0.133043181461659f, 0.132962712541143f,
+ 0.132882252369387f, 0.132801800983178f, 0.132721358419264f,
+ 0.132640924714344f, 0.132560499905078f, 0.132480084028077f,
+ 0.132399677119913f, 0.132319279217111f, 0.132238890356154f,
+ 0.132158510573480f, 0.132078139905483f, 0.131997778388515f,
+ 0.131917426058882f, 0.131837082952847f, 0.131756749106629f,
+ 0.131676424556405f, 0.131596109338304f, 0.131515803488415f,
+ 0.131435507042783f, 0.131355220037405f, 0.131274942508240f,
+ 0.131194674491199f, 0.131114416022150f, 0.131034167136919f,
+ 0.130953927871285f, 0.130873698260986f, 0.130793478341716f,
+ 0.130713268149122f, 0.130633067718811f, 0.130552877086344f,
+ 0.130472696287238f, 0.130392525356968f, 0.130312364330964f,
+ 0.130232213244611f, 0.130152072133252f, 0.130071941032186f,
+ 0.125630091969089f, 0.125549980994328f, 0.125469880135491f,
+ 0.125389789427702f, 0.125309708906040f, 0.125229638605540f,
+ 0.125149578561194f, 0.125069528807950f, 0.124989489380710f,
+ 0.124909460314337f, 0.124829441643644f, 0.124749433403405f,
+ 0.124669435628348f, 0.124589448353158f, 0.124509471612476f,
+ 0.124429505440897f, 0.124349549872976f, 0.124269604943221f,
+ 0.124189670686097f, 0.124109747136027f, 0.124029834327388f,
+ 0.123949932294513f, 0.123870041071692f, 0.123790160693172f,
+ 0.123710291193154f, 0.123630432605798f, 0.123550584965217f,
+ 0.123470748305482f, 0.123390922660619f, 0.123311108064613f,
+ 0.123231304551401f, 0.123151512154880f, 0.123071730908899f,
+ 0.122991960847268f, 0.122912202003750f, 0.122832454412063f,
+ 0.122752718105885f, 0.122672993118847f, 0.122593279484538f,
+ 0.122513577236502f, 0.122433886408239f, 0.122354207033207f,
+ 0.122274539144817f, 0.122194882776439f, 0.122115237961398f,
+ 0.122035604732975f, 0.121955983124407f, 0.121876373168889f,
+ 0.121796774899569f, 0.121717188349554f, 0.121637613551904f,
+ 0.121558050539640f, 0.121478499345734f, 0.121398960003116f,
+ 0.121319432544675f, 0.121239917003251f, 0.121160413411644f,
+ 0.121080921802609f, 0.121001442208857f, 0.120921974663054f,
+ 0.120842519197825f, 0.120763075845748f, 0.120683644639360f,
+ 0.120604225611152f, 0.120524818793572f, 0.120445424219023f,
+ 0.120366041919867f, 0.120286671928419f, 0.120207314276951f,
+ 0.120127968997693f, 0.120048636122829f, 0.119969315684500f,
+ 0.119890007714802f, 0.119810712245789f, 0.119731429309471f,
+ 0.119652158937811f, 0.119572901162733f, 0.119493656016113f,
+ 0.119414423529785f, 0.119335203735540f, 0.114894268657545f,
+ 0.114815074342658f, 0.114735892814960f, 0.114656724106065f,
+ 0.114577568247544f, 0.114498425270923f, 0.114419295207686f,
+ 0.114340178089270f, 0.114261073947072f, 0.114181982812442f,
+ 0.114102904716688f, 0.114023839691073f, 0.113944787766818f,
+ 0.113865748975096f, 0.113786723347042f, 0.113707710913742f,
+ 0.113628711706240f, 0.113549725755538f, 0.113470753092591f,
+ 0.113391793748312f, 0.113312847753569f, 0.113233915139187f,
+ 0.113154995935948f, 0.113076090174588f, 0.112997197885800f,
+ 0.112918319100234f, 0.112839453848495f, 0.112760602161145f,
+ 0.112681764068701f, 0.112602939601637f, 0.112524128790383f,
+ 0.112445331665325f, 0.112366548256805f, 0.112287778595122f,
+ 0.112209022710530f, 0.112130280633239f, 0.112051552393417f,
+ 0.111972838021186f, 0.111894137546624f, 0.111815450999768f,
+ 0.111736778410607f, 0.111658119809091f, 0.111579475225121f,
+ 0.111500844688557f, 0.111422228229216f, 0.111343625876868f,
+ 0.111265037661242f, 0.111186463612022f, 0.111107903758848f,
+ 0.111029358131316f, 0.110950826758979f, 0.110872309671346f,
+ 0.110793806897879f, 0.110715318468002f, 0.110636844411090f,
+ 0.110558384756477f, 0.110479939533451f, 0.110401508771258f,
+ 0.110323092499099f, 0.110244690746132f, 0.110166303541471f,
+ 0.110087930914184f, 0.110009572893298f, 0.109931229507795f,
+ 0.109852900786614f, 0.109774586758647f, 0.109696287452746f,
+ 0.109618002897716f, 0.109539733122321f, 0.109461478155279f,
+ 0.109383238025265f, 0.109305012760910f, 0.109226802390801f,
+ 0.109148606943480f, 0.109070426447448f, 0.108992260931160f,
+ 0.108914110423026f, 0.108835974951415f, 0.108757854544651f,
+ 0.108679749231012f, 0.108601659038736f, 0.108523583996014f,
+ 0.108445524130995f, 0.108367479471782f, 0.108289450046436f,
+ 0.108211435882973f, 0.108133437009367f, 0.108055453453546f,
+ 0.107977485243395f, 0.107899532406754f, 0.107821594971421f,
+ 0.107743672965149f, 0.107665766415648f, 0.107587875350582f,
+ 0.107509999797574f, 0.107432139784200f, 0.107354295337995f,
+ 0.107276466486449f, 0.107198653257007f, 0.107120855677072f,
+ 0.107043073774001f, 0.106965307575109f, 0.106887557107667f,
+ 0.106809822398901f, 0.106732103475993f, 0.106654400366083f,
+ 0.106576713096265f, 0.106499041693590f, 0.106421386185065f,
+ 0.106343746597653f, 0.106266122958274f, 0.106188515293803f,
+ 0.106110923631072f, 0.106033347996867f, 0.105955788417934f,
+ 0.105878244920971f, 0.105800717532635f, 0.105723206279537f,
+ 0.105645711188245f, 0.105568232285285f, 0.105490769597136f,
+ 0.105413323150235f, 0.105335892970973f, 0.105258479085701f,
+ 0.105181081520723f, 0.105103700302298f, 0.105026335456646f,
+ 0.104948987009938f, 0.104871654988304f, 0.104794339417828f,
+ 0.104717040324553f, 0.104639757734476f, 0.104562491673551f,
+ 0.104485242167686f, 0.104408009242749f, 0.104330792924560f,
+ 0.104253593238899f, 0.104176410211498f, 0.104099243868048f,
+ 0.104022094234196f, 0.103944961335543f, 0.103867845197649f,
+ 0.103790745846028f, 0.103713663306151f, 0.103636597603444f,
+ 0.103559548763290f, 0.103482516811028f, 0.103405501771954f,
+ 0.103328503671319f, 0.103251522534330f, 0.103174558386149f,
+ 0.103097611251898f, 0.103020681156651f, 0.102943768125441f,
+ 0.102866872183254f, 0.102789993355035f, 0.102713131665683f,
+ 0.102636287140056f, 0.102559459802964f, 0.102482649679177f,
+ 0.102405856793418f, 0.102329081170369f, 0.102252322834665f,
+ 0.102175581810900f, 0.102098858123621f, 0.102022151797335f,
+ 0.101945462856502f, 0.101868791325538f, 0.101792137228817f,
+ 0.101715500590669f, 0.101638881435378f, 0.101562279787186f,
+ 0.101485695670291f, 0.101409129108846f, 0.101332580126960f,
+ 0.101256048748701f, 0.101179534998088f, 0.101103038899101f,
+ 0.101026560475674f, 0.100950099751696f, 0.100873656751014f,
+ 0.100797231497430f, 0.100720824014703f, 0.100644434326547f,
+ 0.100568062456633f, 0.100491708428587f, 0.100415372265992f,
+ 0.100339053992388f, 0.100262753631269f, 0.100186471206086f,
+ 0.100110206740246f, 0.100033960257113f, 0.0999577317800052f,
+ 0.0998815213321992f, 0.0998053289369260f, 0.0997291546173731f,
+ 0.0996529983966844f, 0.0995768602979594f, 0.0995007403442542f,
+ 0.0994246385585807f, 0.0993485549639068f, 0.0992724895831567f,
+ 0.0991964424392106f, 0.0991204135549048f, 0.0990444029530317f,
+ 0.0989684106563397f, 0.0988924366875334f, 0.0988164810692734f,
+ 0.0987405438241764f, 0.0986646249748153f, 0.0985887245437189f,
+ 0.0985128425533722f, 0.0984369790262163f, 0.0983611339846482f,
+ 0.0982853074510214f, 0.0982094994476449f, 0.0981337099967844f,
+ 0.0980579391206610f, 0.0979821868414527f, 0.0979064531812928f,
+ 0.0978307381622712f, 0.0977550418064336f, 0.0976793641357820f,
+ 0.0976037051722743f, 0.0975280649378246f, 0.0974524434543029f,
+ 0.0973768407435356f, 0.0973012568273050f, 0.0972256917273493f,
+ 0.0971501454653631f, 0.0970746180629969f, 0.0969991095418574f,
+ 0.0969236199235071f, 0.0968481492294650f, 0.0967726974812058f,
+ 0.0966972647001606f, 0.0966218509077163f, 0.0965464561252159f,
+ 0.0964710803739588f, 0.0963957236752001f, 0.0963203860501512f,
+ 0.0962450675199795f, 0.0961697681058085f, 0.0960944878287176f,
+ 0.0960192267097427f, 0.0959439847698754f, 0.0958687620300635f,
+ 0.0957935585112108f, 0.0957183742341774f, 0.0956432092197791f,
+ 0.0955680634887883f, 0.0954929370619329f, 0.0954178299598974f,
+ 0.0953427422033218f, 0.0952676738128029f, 0.0951926248088929f,
+ 0.0951175952121004f, 0.0950425850428900f, 0.0949675943216825f,
+ 0.0948926230688546f, 0.0948176713047392f, 0.0947427390496252f,
+ 0.0946678263237576f, 0.0945929331473374f, 0.0945180595405218f,
+ 0.0944432055234240f, 0.0943683711161133f, 0.0942935563386150f,
+ 0.0942187612109106f, 0.0941439857529376f, 0.0940692299845894f,
+ 0.0939944939257160f, 0.0939197775961227f, 0.0938450810155716f,
+ 0.0937704042037804f, 0.0936957471804231f, 0.0936211099651296f,
+ 0.0935464925774860f, 0.0934718950370345f, 0.0933973173632733f,
+ 0.0933227595756565f, 0.0932482216935947f, 0.0931737037364541f,
+ 0.0930992057235573f, 0.0930247276741828f, 0.0929502696075653f,
+ 0.0928758315428953f, 0.0928014134993197f, 0.0927270154959413f,
+ 0.0926526375518190f, 0.0925782796859676f, 0.0925039419173584f,
+ 0.0924296242649182f, 0.0923553267475303f, 0.0922810493840339f,
+ 0.0922067921932243f, 0.0921325551938528f, 0.0920583384046269f,
+ 0.0919841418442100f, 0.0919099655312217f, 0.0918358094842375f,
+ 0.0917616737217892f, 0.0916875582623645f, 0.0916134631244072f,
+ 0.0915393883263172f, 0.0914653338864503f, 0.0913912998231188f,
+ 0.0913172861545903f, 0.0912432928990893f, 0.0911693200747959f,
+ 0.0910953676998462f, 0.0910214357923326f, 0.0909475243703036f,
+ 0.0908736334517634f, 0.0907997630546726f, 0.0907259131969478f,
+ 0.0906520838964616f, 0.0905782751710426f, 0.0905044870384756f,
+ 0.0904307195165015f, 0.0903569726228168f, 0.0902832463750749f,
+ 0.0902095407908844f, 0.0901358558878106f, 0.0900621916833744f,
+ 0.0899885481950531f, 0.0899149254402798f, 0.0898413234364439f,
+ 0.0897677422008906f, 0.0896941817509215f, 0.0896206421037937f,
+ 0.0895471232767210f, 0.0894736252868730f, 0.0894001481513751f,
+ 0.0893266918873091f, 0.0892532565117127f, 0.0891798420415798f,
+ 0.0891064484938601f, 0.0890330758854596f, 0.0889597242332403f,
+ 0.0888863935540201f, 0.0888130838645731f, 0.0887397951816295f,
+ 0.0886665275218755f, 0.0885932809019533f, 0.0885200553384611f,
+ 0.0884468508479535f, 0.0883736674469406f, 0.0883005051518891f,
+ 0.0882273639792214f, 0.0881542439453161f, 0.0880811450665078f,
+ 0.0880080673590872f, 0.0879350108393010f, 0.0878619755233519f,
+ 0.0877889614273989f, 0.0877159685675568f, 0.0876429969598966f,
+ 0.0875700466204452f, 0.0874971175651857f, 0.0874242098100572f,
+ 0.0873513233709547f, 0.0872784582637296f, 0.0872056145041890f,
+ 0.0871327921080962f, 0.0870599910911707f, 0.0869872114690878f,
+ 0.0869144532574789f, 0.0868417164719315f, 0.0867690011279892f,
+ 0.0866963072411517f, 0.0866236348268744f, 0.0865509839005692f,
+ 0.0864783544776037f, 0.0864057465733018f, 0.0863331602029433f,
+ 0.0862605953817640f, 0.0861880521249560f, 0.0861155304476673f,
+ 0.0860430303650017f, 0.0859705518920195f, 0.0858980950437366f,
+ 0.0858256598351254f, 0.0857532462811140f, 0.0856808543965868f,
+ 0.0856084841963839f, 0.0855361356953018f, 0.0854638089080928f,
+ 0.0853915038494655f, 0.0853192205340843f, 0.0852469589765697f,
+ 0.0851747191914984f, 0.0851025011934029f, 0.0850303049967719f,
+ 0.0849581306160502f, 0.0848859780656385f, 0.0848138473598935f,
+ 0.0847417385131283f, 0.0846696515396116f, 0.0845975864535684f,
+ 0.0845255432691795f, 0.0844535220005823f, 0.0843815226618695f,
+ 0.0843095452670904f, 0.0842375898302500f, 0.0841656563653097f,
+ 0.0840937448861865f, 0.0840218554067539f, 0.0839499879408410f,
+ 0.0838781425022332f, 0.0838063191046720f, 0.0837345177618548f,
+ 0.0836627384874350f, 0.0835909812950222f, 0.0835192461981819f,
+ 0.0834475332104356f, 0.0833758423452611f, 0.0833041736160921f,
+ 0.0832325270363181f, 0.0831609026192851f, 0.0830893003782947f,
+ 0.0830177203266048f, 0.0829461624774291f, 0.0828746268439379f,
+ 0.0828031134392568f, 0.0827316222764678f, 0.0826601533686091f,
+ 0.0825887067286746f, 0.0825172823696144f, 0.0824458803043347f,
+ 0.0823745005456977f, 0.0823031431065214f, 0.0822318079995802f,
+ 0.0821604952376043f, 0.0820892048332801f, 0.0820179367992499f,
+ 0.0819466911481119f, 0.0818754678924208f, 0.0818042670446868f,
+ 0.0817330886173767f, 0.0816619326229126f, 0.0815907990736732f,
+ 0.0815196879819933f, 0.0814485993601633f, 0.0813775332204299f,
+ 0.0813064895749958f, 0.0812354684360197f, 0.0811644698156163f,
+ 0.0810934937258564f, 0.0810225401787669f, 0.0809516091863306f,
+ 0.0808807007604862f, 0.0808098149131290f, 0.0807389516561096f,
+ 0.0806681110012352f, 0.0805972929602686f, 0.0805264975449289f,
+ 0.0804557247668912f, 0.0803849746377866f, 0.0803142471692022f,
+ 0.0802435423726812f, 0.0801728602597226f, 0.0801022008417819f,
+ 0.0800315641302702f, 0.0799609501365547f, 0.0798903588719589f,
+ 0.0798197903477619f, 0.0797492445751992f, 0.0796787215654622f,
+ 0.0796082213296982f, 0.0795377438790107f, 0.0794672892244592f,
+ 0.0793968573770592f, 0.0793264483477822f, 0.0792560621475557f,
+ 0.0791856987872634f, 0.0791153582777449f, 0.0790450406297956f,
+ 0.0789747458541675f, 0.0789044739615680f, 0.0788342249626610f,
+ 0.0787639988680661f, 0.0786937956883591f, 0.0786236154340719f,
+ 0.0785534581156922f, 0.0784833237436637f, 0.0784132123283866f,
+ 0.0783431238802165f, 0.0782730584094654f, 0.0782030159264013f,
+ 0.0781329964412480f, 0.0780629999641855f, 0.0779930265053501f,
+ 0.0779230760748334f, 0.0778531486826837f, 0.0777832443389050f,
+ 0.0777133630534574f, 0.0776435048362570f, 0.0775736696971760f,
+ 0.0775038576460424f, 0.0774340686926407f, 0.0773643028467107f,
+ 0.0772945601179488f, 0.0772248405160073f, 0.0771551440504945f,
+ 0.0770854707309746f, 0.0770158205669677f, 0.0769461935679505f,
+ 0.0768765897433551f, 0.0768070091025700f, 0.0767374516549394f,
+ 0.0766679174097639f, 0.0765984063762999f, 0.0765289185637596f,
+ 0.0764594539813117f, 0.0763900126380807f, 0.0763205945431470f,
+ 0.0762511997055470f, 0.0761818281342734f, 0.0761124798382747f,
+ 0.0760431548264555f, 0.0759738531076762f, 0.0759045746907537f,
+ 0.0758353195844604f, 0.0757660877975250f, 0.0756968793386320f,
+ 0.0756276942164223f, 0.0755585324394925f, 0.0754893940163953f,
+ 0.0754202789556392f, 0.0753511872656892f, 0.0752821189549660f,
+ 0.0752130740318462f, 0.0751440525046627f, 0.0750750543817043f,
+ 0.0750060796712157f, 0.0749371283813978f, 0.0748682005204073f,
+ 0.0747992960963572f, 0.0747304151173163f, 0.0746615575913094f,
+ 0.0745927235263174f, 0.0745239129302773f, 0.0744551258110818f,
+ 0.0743863621765800f, 0.0699558940269990f, 0.0698871773852553f,
+ 0.0698184842514879f, 0.0697498146333701f, 0.0696811685385305f,
+ 0.0696125459745543f, 0.0695439469489825f, 0.0694753714693121f,
+ 0.0694068195429960f, 0.0693382911774433f, 0.0692697863800191f,
+ 0.0692013051580444f, 0.0691328475187962f, 0.0690644134695077f,
+ 0.0689960030173678f, 0.0689276161695217f, 0.0688592529330704f,
+ 0.0687909133150711f, 0.0687225973225368f, 0.0686543049624368f,
+ 0.0685860362416959f, 0.0685177911671956f, 0.0684495697457728f,
+ 0.0683813719842208f, 0.0683131978892886f, 0.0682450474676814f,
+ 0.0681769207260604f, 0.0681088176710428f, 0.0680407383092017f,
+ 0.0679726826470663f, 0.0679046506911219f, 0.0678366424478096f,
+ 0.0677686579235266f, 0.0677006971246261f, 0.0676327600574174f,
+ 0.0675648467281656f, 0.0674969571430920f, 0.0674290913083737f,
+ 0.0673612492301441f, 0.0672934309144923f, 0.0672256363674636f,
+ 0.0671578655950593f, 0.0670901186032365f, 0.0670223953979085f,
+ 0.0669546959849446f, 0.0668870203701700f, 0.0668193685593660f,
+ 0.0667517405582697f, 0.0666841363725746f, 0.0666165560079299f,
+ 0.0665489994699407f, 0.0664814667641684f, 0.0664139578961303f,
+ 0.0663464728712994f, 0.0662790116951054f, 0.0662115743729332f,
+ 0.0661441609101242f, 0.0660767713119756f, 0.0660094055837408f,
+ 0.0659420637306289f, 0.0658747457578054f, 0.0658074516703914f,
+ 0.0657401814734641f, 0.0656729351720569f, 0.0656057127711590f,
+ 0.0655385142757157f, 0.0654713396906282f, 0.0654041890207538f,
+ 0.0653370622709057f, 0.0652699594458532f, 0.0652028805503216f,
+ 0.0651358255889920f, 0.0650687945665018f, 0.0650017874874442f,
+ 0.0649348043563684f, 0.0648678451777795f, 0.0648009099561390f,
+ 0.0647339986958640f, 0.0646671114013277f, 0.0646002480768593f,
+ 0.0645334087267441f, 0.0644665933552233f, 0.0643998019664940f,
+ 0.0643330345647095f, 0.0642662911539789f, 0.0641995717383674f,
+ 0.0641328763218963f, 0.0640662049085427f, 0.0639995575022397f,
+ 0.0639329341068766f, 0.0638663347262984f, 0.0637997593643063f,
+ 0.0637332080246576f, 0.0636666807110652f, 0.0636001774271983f,
+ 0.0635336981766821f, 0.0634672429630977f, 0.0634008117899821f,
+ 0.0633344046608284f, 0.0632680215790859f, 0.0632016625481595f,
+ 0.0631353275714102f, 0.0630690166521551f, 0.0630027297936674f,
+ 0.0629364669991760f, 0.0628702282718661f, 0.0628040136148784f,
+ 0.0627378230313101f, 0.0626716565242143f, 0.0626055140965999f,
+ 0.0625393957514318f, 0.0624733014916310f, 0.0624072313200746f,
+ 0.0623411852395954f, 0.0622751632529823f, 0.0622091653629804f,
+ 0.0621431915722904f, 0.0620772418835694f, 0.0620113162994302f,
+ 0.0619454148224416f, 0.0618795374551285f, 0.0618136841999718f,
+ 0.0617478550594083f, 0.0616820500358309f, 0.0616162691315882f,
+ 0.0615505123489852f, 0.0614847796902825f, 0.0614190711576971f,
+ 0.0613533867534016f, 0.0612877264795248f, 0.0612220903381512f,
+ 0.0611564783313218f, 0.0610908904610332f, 0.0610253267292380f,
+ 0.0609597871378448f, 0.0608942716887185f, 0.0608287803836795f,
+ 0.0607633132245045f, 0.0606978702129261f, 0.0606324513506329f,
+ 0.0605670566392694f, 0.0605016860804361f, 0.0604363396756896f,
+ 0.0603710174265425f, 0.0603057193344631f, 0.0602404454008760f,
+ 0.0601751956271616f, 0.0601099700146564f, 0.0600447685646528f,
+ 0.0599795912783991f, 0.0599144381570998f, 0.0598493092019153f,
+ 0.0597842044139619f, 0.0597191237943117f, 0.0596540673439933f,
+ 0.0595890350639909f, 0.0595240269552448f, 0.0594590430186511f,
+ 0.0593940832550622f, 0.0593291476652861f, 0.0592642362500872f,
+ 0.0591993490101854f, 0.0591344859462571f, 0.0590696470589343f,
+ 0.0590048323488052f, 0.0589400418164136f, 0.0588752754622598f,
+ 0.0588105332867999f, 0.0587458152904457f, 0.0586811214735651f,
+ 0.0586164518364824f, 0.0585518063794773f, 0.0584871851027858f,
+ 0.0584225880065996f, 0.0583580150910669f, 0.0582934663562913f,
+ 0.0582289418023328f, 0.0581644414292068f, 0.0580999652368856f,
+ 0.0580355132252967f, 0.0579710853943238f, 0.0579066817438065f,
+ 0.0578423022735407f, 0.0577779469832779f, 0.0577136158727258f,
+ 0.0576493089415478f, 0.0575850261893637f, 0.0575207676157490f,
+ 0.0574565332202351f, 0.0573923230023095f, 0.0573281369614158f,
+ 0.0572639750969533f, 0.0571998374082775f, 0.0571357238946997f,
+ 0.0570716345554873f, 0.0570075693898635f, 0.0569435283970079f,
+ 0.0568795115760553f, 0.0568155189260974f, 0.0567515504461811f,
+ 0.0566876061353098f, 0.0566236859924424f, 0.0565597900164942f,
+ 0.0564959182063363f, 0.0564320705607957f, 0.0563682470786554f,
+ 0.0563044477586545f, 0.0562406725994879f, 0.0561769215998064f,
+ 0.0561131947582173f, 0.0560494920732831f, 0.0559858135435229f,
+ 0.0559221591674113f, 0.0558585289433793f, 0.0557949228698135f,
+ 0.0557313409450567f, 0.0556677831674074f, 0.0556042495351205f,
+ 0.0555407400464066f, 0.0554772546994322f, 0.0554137934923198f,
+ 0.0553503564231481f, 0.0552869434899515f, 0.0552235546907205f,
+ 0.0551601900234014f, 0.0550968494858967f, 0.0550335330760648f,
+ 0.0549702407917199f, 0.0549069726306323f, 0.0548437285905283f,
+ 0.0547805086690902f, 0.0547173128639560f, 0.0546541411727200f,
+ 0.0545909935929322f, 0.0545278701220988f, 0.0544647707576817f,
+ 0.0544016954970989f, 0.0543386443377246f, 0.0542756172768886f,
+ 0.0542126143118766f, 0.0541496354399307f, 0.0540866806582487f,
+ 0.0540237499639842f, 0.0539608433542472f, 0.0538979608261031f,
+ 0.0538351023765739f, 0.0537722680026371f, 0.0537094577012262f,
+ 0.0536466714692308f, 0.0535839093034965f, 0.0535211712008247f,
+ 0.0534584571579729f, 0.0533957671716543f, 0.0533331012385385f,
+ 0.0532704593552508f, 0.0532078415183724f, 0.0531452477244405f,
+ 0.0530826779699484f, 0.0530201322513452f, 0.0529576105650360f,
+ 0.0528951129073819f, 0.0528326392746999f, 0.0527701896632632f,
+ 0.0527077640693004f, 0.0526453624889966f, 0.0525829849184928f,
+ 0.0525206313538857f, 0.0524583017912279f, 0.0523959962265285f,
+ 0.0523337146557520f, 0.0522714570748190f, 0.0522092234796062f,
+ 0.0521470138659462f, 0.0520848282296275f, 0.0520226665663946f,
+ 0.0519605288719478f, 0.0518984151419437f, 0.0518363253719946f,
+ 0.0517742595576687f, 0.0517122176944903f, 0.0516501997779397f,
+ 0.0515882058034530f, 0.0515262357664224f, 0.0514642896621959f,
+ 0.0514023674860775f, 0.0513404692333274f, 0.0512785948991613f,
+ 0.0512167444787511f, 0.0511549179672250f, 0.0510931153596663f,
+ 0.0510313366511152f, 0.0509695818365671f, 0.0509078509109738f,
+ 0.0508461438692430f, 0.0507844607062381f, 0.0507228014167787f,
+ 0.0506611659956403f, 0.0505995544375543f, 0.0505379667372080f,
+ 0.0504764028892448f, 0.0504148628882640f, 0.0503533467288209f,
+ 0.0502918544054265f, 0.0502303859125480f, 0.0501689412446085f,
+ 0.0501075203959871f, 0.0500461233610187f, 0.0499847501339942f,
+ 0.0499234007091606f, 0.0498620750807206f, 0.0498007732428331f,
+ 0.0497394951896126f, 0.0496782409151302f, 0.0496170104134122f,
+ 0.0495558036784412f, 0.0494946207041559f, 0.0494334614844505f,
+ 0.0493723260131757f, 0.0493112142841377f, 0.0492501262910988f,
+ 0.0491890620277774f, 0.0491280214878476f, 0.0490670046649396f,
+ 0.0490060115526395f, 0.0489450421444893f, 0.0488840964339871f,
+ 0.0488231744145867f, 0.0487622760796981f, 0.0487014014226872f,
+ 0.0486405504368756f, 0.0485797231155411f, 0.0485189194519174f,
+ 0.0484581394391941f, 0.0483973830705168f, 0.0483366503389869f,
+ 0.0482759412376620f, 0.0482152557595553f, 0.0481545938976364f,
+ 0.0480939556448302f, 0.0480333409940183f, 0.0479727499380377f,
+ 0.0479121824696815f, 0.0478516385816988f, 0.0477911182667946f,
+ 0.0477306215176298f, 0.0476701483268213f, 0.0476096986869419f,
+ 0.0475492725905205f, 0.0474888700300417f, 0.0474284909979461f,
+ 0.0473681354866303f, 0.0473078034884470f, 0.0472474949957045f,
+ 0.0471872100006673f, 0.0471269484955557f, 0.0470667104725461f,
+ 0.0470064959237706f, 0.0469463048413175f, 0.0468861372172308f,
+ 0.0468259930435107f, 0.0467658723121130f, 0.0467057750149499f,
+ 0.0466457011438889f, 0.0465856506907542f, 0.0465256236473254f,
+ 0.0464656200053382f, 0.0464056397564841f, 0.0463456828924109f,
+ 0.0462857494047220f, 0.0462258392849768f, 0.0461659525246906f,
+ 0.0461060891153350f, 0.0460462490483370f, 0.0459864323150799f,
+ 0.0459266389069028f, 0.0458668688151008f, 0.0458071220309249f,
+ 0.0457473985455820f, 0.0456876983502350f, 0.0456280214360027f,
+ 0.0455683677939599f, 0.0455087374151372f, 0.0454491302905213f,
+ 0.0453895464110547f, 0.0453299857676360f, 0.0452704483511194f,
+ 0.0452109341523154f, 0.0451514431619903f, 0.0450919753708663f,
+ 0.0450325307696215f, 0.0449731093488901f, 0.0449137110992621f,
+ 0.0448543360112835f, 0.0447949840754560f, 0.0447356552822377f,
+ 0.0446763496220422f, 0.0446170670852392f, 0.0445578076621543f,
+ 0.0444985713430691f, 0.0444393581182212f, 0.0443801679778039f,
+ 0.0443210009119664f, 0.0442618569108143f, 0.0442027359644086f,
+ 0.0441436380627666f, 0.0440845631958612f, 0.0440255113536215f,
+ 0.0439664825259325f, 0.0439074767026350f, 0.0438484938735257f,
+ 0.0437895340283575f, 0.0437305971568391f, 0.0436716832486349f,
+ 0.0436127922933654f, 0.0435539242806073f, 0.0434950791998928f,
+ 0.0434362570407102f, 0.0433774577925038f, 0.0433186814446737f,
+ 0.0432599279865760f, 0.0432011974075228f, 0.0431424896967819f,
+ 0.0430838048435774f, 0.0430251428370888f, 0.0429665036664521f,
+ 0.0429078873207587f, 0.0428492937890565f, 0.0427907230603488f,
+ 0.0427321751235950f, 0.0426736499677106f, 0.0426151475815668f,
+ 0.0425566679539909f, 0.0424982110737659f, 0.0424397769296309f,
+ 0.0423813655102811f, 0.0423229768043671f, 0.0422646108004960f,
+ 0.0422062674872303f, 0.0421479468530891f, 0.0420896488865467f,
+ 0.0420313735760336f, 0.0419731209099364f, 0.0419148908765975f,
+ 0.0418566834643152f, 0.0417984986613437f, 0.0417403364558930f,
+ 0.0416821968361295f, 0.0416240797901751f, 0.0415659853061075f,
+ 0.0415079133719608f, 0.0414498639757248f, 0.0413918371053450f,
+ 0.0413338327487231f, 0.0412758508937167f, 0.0412178915281392f,
+ 0.0411599546397599f, 0.0411020402163043f, 0.0410441482454534f,
+ 0.0409862787148446f, 0.0409284316120707f, 0.0408706069246808f,
+ 0.0408128046401798f, 0.0407550247460286f, 0.0406972672296439f,
+ 0.0406395320783983f, 0.0405818192796204f, 0.0405241288205948f,
+ 0.0404664606885619f, 0.0404088148707178f, 0.0403511913542151f,
+ 0.0402935901261618f, 0.0402360111736221f, 0.0401784544836158f,
+ 0.0401209200431191f, 0.0400634078390637f, 0.0400059178583375f,
+ 0.0399484500877839f, 0.0398910045142029f, 0.0398335811243497f,
+ 0.0397761799049359f, 0.0397188008426288f, 0.0396614439240516f,
+ 0.0396041091357837f, 0.0395467964643600f, 0.0394895058962715f,
+ 0.0394322374179654f, 0.0393749910158443f, 0.0393177666762671f,
+ 0.0392605643855483f, 0.0392033841299587f, 0.0391462258957247f,
+ 0.0390890896690288f, 0.0390319754360092f, 0.0389748831827604f,
+ 0.0389178128953323f, 0.0388607645597312f, 0.0388037381619189f,
+ 0.0387467336878135f, 0.0386897511232887f, 0.0386327904541742f,
+ 0.0385758516662558f, 0.0385189347452749f, 0.0384620396769291f,
+ 0.0384051664468718f, 0.0383483150407121f, 0.0382914854440155f,
+ 0.0382346776423029f, 0.0381778916210515f, 0.0381211273656940f,
+ 0.0380643848616195f, 0.0380076640941726f, 0.0379509650486541f,
+ 0.0378942877103206f, 0.0378376320643845f, 0.0377809980960143f,
+ 0.0377243857903341f, 0.0376677951324245f, 0.0376112261073215f,
+ 0.0375546787000170f, 0.0374981528954590f, 0.0374416486785515f,
+ 0.0373851660341542f, 0.0373287049470828f, 0.0372722654021088f,
+ 0.0372158473839598f, 0.0371594508773193f, 0.0371030758668266f,
+ 0.0370467223370766f, 0.0369903902726209f, 0.0369340796579662f,
+ 0.0368777904775757f, 0.0368215227158680f, 0.0367652763572181f,
+ 0.0367090513859566f, 0.0366528477863700f, 0.0365966655427007f,
+ 0.0365405046391474f, 0.0364843650598642f, 0.0364282467889613f,
+ 0.0363721498105048f, 0.0363160741085168f, 0.0362600196669752f,
+ 0.0362039864698137f, 0.0361479745009222f, 0.0360919837441462f,
+ 0.0360360141832873f, 0.0359800658021030f, 0.0359241385843064f,
+ 0.0358682325135671f, 0.0358123475735100f, 0.0357564837477163f,
+ 0.0357006410197228f, 0.0356448193730226f, 0.0355890187910643f,
+ 0.0355332392572527f, 0.0354774807549481f, 0.0354217432674673f,
+ 0.0353660267780825f, 0.0353103312700220f, 0.0352546567264699f,
+ 0.0351990031305666f, 0.0351433704654078f, 0.0350877587140454f,
+ 0.0350321678594873f, 0.0349765978846971f, 0.0349210487725945f,
+ 0.0348655205060549f, 0.0348100130679097f, 0.0347545264409461f,
+ 0.0346990606079075f, 0.0346436155514929f, 0.0345881912543573f,
+ 0.0345327876991115f, 0.0344774048683225f, 0.0344220427445127f,
+ 0.0343667013101609f, 0.0343113805477016f, 0.0342560804395250f,
+ 0.0342008009679776f, 0.0341455421153614f, 0.0340903038639347f,
+ 0.0340350861959113f, 0.0339798890934610f, 0.0339247125387098f,
+ 0.0338695565137393f, 0.0338144210005871f, 0.0337593059812464f,
+ 0.0337042114376669f, 0.0336491373517538f, 0.0335940837053681f,
+ 0.0335390504803269f, 0.0334840376584033f, 0.0334290452213260f,
+ 0.0333740731507797f, 0.0333191214284051f, 0.0332641900357988f,
+ 0.0332092789545132f, 0.0331543881660566f, 0.0330995176518931f,
+ 0.0330446673934429f, 0.0329898373720821f, 0.0329350275691426f,
+ 0.0328802379659119f, 0.0328254685436341f, 0.0327707192835085f,
+ 0.0327159901666906f, 0.0326612811742918f, 0.0326065922873795f,
+ 0.0325519234869767f, 0.0324972747540625f, 0.0324426460695717f,
+ 0.0323880374143954f, 0.0323334487693801f, 0.0322788801153286f,
+ 0.0322243314329991f, 0.0321698027031064f, 0.0321152939063204f,
+ 0.0320608050232677f, 0.0320063360345299f, 0.0319518869206454f,
+ 0.0318974576621078f, 0.0318430482393670f, 0.0317886586328284f,
+ 0.0317342888228537f, 0.0316799387897604f, 0.0316256085138217f,
+ 0.0315712979752667f, 0.0315170071542806f, 0.0314627360310044f,
+ 0.0314084845855349f, 0.0313542527979248f, 0.0313000406481829f,
+ 0.0312458481162737f, 0.0311916751821174f, 0.0311375218255906f,
+ 0.0310833880265254f, 0.0310292737647098f, 0.0309751790198877f,
+ 0.0309211037717592f, 0.0308670479999799f, 0.0308130116841615f,
+ 0.0307589948038714f, 0.0307049973386331f, 0.0306510192679259f,
+ 0.0305970605711850f, 0.0305431212278014f, 0.0304892012171220f,
+ 0.0304353005184498f, 0.0303814191110434f, 0.0303275569741174f,
+ 0.0302737140868423f, 0.0302198904283447f, 0.0301660859777065f,
+ 0.0301123007139661f, 0.0300585346161174f, 0.0300047876631105f,
+ 0.0299510598338510f, 0.0298973511072006f, 0.0298436614619771f,
+ 0.0297899908769537f, 0.0297363393308599f, 0.0296827068023808f,
+ 0.0296290932701575f, 0.0295754987127871f, 0.0295219231088224f,
+ 0.0294683664367722f, 0.0294148286751011f, 0.0293613098022296f,
+ 0.0293078097965342f, 0.0292543286363469f, 0.0292008662999562f,
+ 0.0291474227656060f, 0.0290939980114962f, 0.0290405920157826f,
+ 0.0289872047565770f, 0.0289338362119469f, 0.0288804863599157f,
+ 0.0288271551784627f, 0.0287738426455233f, 0.0287205487389884f,
+ 0.0286672734367052f, 0.0286140167164761f, 0.0285607785560604f,
+ 0.0285075589331723f, 0.0284543578254825f, 0.0284011752106171f,
+ 0.0283480110661588f, 0.0282948653696453f, 0.0282417380985708f,
+ 0.0281886292303851f, 0.0281355387424940f, 0.0280824666122592f,
+ 0.0280294128169981f, 0.0279763773339841f, 0.0279233601404466f,
+ 0.0278703612135707f, 0.0278173805304972f, 0.0277644180683234f,
+ 0.0277114738041018f, 0.0276585477148412f, 0.0276056397775058f,
+ 0.0275527499690165f, 0.0274998782662493f, 0.0274470246460364f,
+ 0.0273941890851657f, 0.0273413715603814f, 0.0272885720483831f,
+ 0.0272357905258265f, 0.0271830269693230f, 0.0271302813554403f,
+ 0.0270775536607015f, 0.0270248438615858f, 0.0269721519345281f,
+ 0.0269194778559194f, 0.0268668216021066f, 0.0268141831493923f,
+ 0.0267615624740348f, 0.0267089595522488f, 0.0266563743602045f,
+ 0.0266038068740280f, 0.0265512570698012f, 0.0264987249235622f,
+ 0.0264462104113047f, 0.0263937135089783f, 0.0263412341924885f,
+ 0.0262887724376968f, 0.0262363282204203f, 0.0261839015164322f,
+ 0.0261314923014614f, 0.0260791005511929f, 0.0260267262412675f,
+ 0.0259743693472816f, 0.0259220298447877f, 0.0258697077092944f,
+ 0.0258174029162656f, 0.0257651154411217f, 0.0257128452592383f,
+ 0.0256605923459475f, 0.0256083566765370f, 0.0255561382262503f,
+ 0.0255039369702867f, 0.0254517528838017f, 0.0253995859419064f,
+ 0.0253474361196679f, 0.0252953033921090f, 0.0252431877342086f,
+ 0.0251910891209013f, 0.0251390075270776f, 0.0250869429275840f,
+ 0.0250348952972226f, 0.0249828646107516f, 0.0249308508428848f,
+ 0.0248788539682924f, 0.0248268739615999f, 0.0247749107973889f,
+ 0.0247229644501969f, 0.0246710348945172f, 0.0246191221047991f,
+ 0.0245672260554474f, 0.0245153467208232f, 0.0244634840752433f,
+ 0.0244116380929803f, 0.0243598087482627f, 0.0243079960152748f,
+ 0.0242561998681570f, 0.0242044202810054f, 0.0241526572278720f,
+ 0.0241009106827644f, 0.0240491806196466f, 0.0239974670124380f,
+ 0.0239457698350141f, 0.0238940890612061f, 0.0238424246648012f,
+ 0.0237907766195426f, 0.0237391448991289f, 0.0236875294772150f,
+ 0.0236359303274115f, 0.0235843474232849f, 0.0235327807383576f,
+ 0.0234812302461075f, 0.0234296959199691f, 0.0233781777333319f,
+ 0.0233266756595420f, 0.0232751896719008f, 0.0232237197436660f,
+ 0.0231722658480509f, 0.0231208279582248f, 0.0230694060473126f,
+ 0.0230180000883954f, 0.0229666100545100f, 0.0229152359186491f,
+ 0.0228638776537611f, 0.0228125352327506f, 0.0227612086284777f,
+ 0.0227098978137585f, 0.0226586027613650f, 0.0226073234440251f,
+ 0.0225560598344226f, 0.0225048119051967f, 0.0224535796289430f,
+ 0.0224023629782129f, 0.0223511619255133f, 0.0222999764433073f,
+ 0.0222488065040137f, 0.0221976520800073f, 0.0221465131436186f,
+ 0.0220953896671340f, 0.0220442816227956f, 0.0219931889828018f,
+ 0.0219421117193066f, 0.0218910498044196f, 0.0218400032102066f,
+ 0.0217889719086893f, 0.0217379558718450f, 0.0216869550716069f,
+ 0.0216359694798642f, 0.0215849990684619f, 0.0215340438092009f,
+ 0.0214831036738377f, 0.0214321786340850f, 0.0213812686616112f,
+ 0.0213303737280405f, 0.0212794938049529f, 0.0212286288638847f,
+ 0.0255395068839051f, 0.0254886718213066f, 0.0254378516550702f,
+ 0.0253870463565555f, 0.0253362558970777f, 0.0252854802479078f,
+ 0.0252347193802728f, 0.0251839732653554f, 0.0251332418742945f,
+ 0.0250825251781844f, 0.0250318231480754f, 0.0249811357549739f,
+ 0.0249304629698419f, 0.0248798047635972f, 0.0248291611071137f,
+ 0.0247785319712210f, 0.0247279173267045f, 0.0246773171443056f,
+ 0.0246267313947214f, 0.0245761600486050f, 0.0245256030765652f,
+ 0.0244750604491668f, 0.0244245321369303f, 0.0243740181103322f,
+ 0.0243235183398047f, 0.0242730327957360f, 0.0242225614484700f,
+ 0.0241721042683066f, 0.0241216612255015f, 0.0240712322902661f,
+ 0.0240208174327678f, 0.0239704166231300f, 0.0239200298314316f,
+ 0.0238696570277076f, 0.0238192981819486f, 0.0237689532641015f,
+ 0.0237186222440686f, 0.0236683050917082f, 0.0236180017768345f,
+ 0.0235677122692175f, 0.0235174365385832f, 0.0234671745546130f,
+ 0.0234169262869447f, 0.0233666917051716f, 0.0233164707788430f,
+ 0.0232662634774639f, 0.0232160697704954f, 0.0231658896273542f,
+ 0.0231157230174128f, 0.0230655699099999f, 0.0230154302743997f,
+ 0.0229653040798524f, 0.0229151912955540f, 0.0228650918906563f,
+ 0.0228150058342672f, 0.0227649330954500f, 0.0227148736432244f,
+ 0.0226648274465652f, 0.0226147944744040f, 0.0225647746956273f,
+ 0.0225147680790781f, 0.0224647745935549f, 0.0224147942078123f,
+ 0.0223648268905605f, 0.0223148726104657f, 0.0222649313361497f,
+ 0.0222150030361906f, 0.0221650876791220f, 0.0221151852334334f,
+ 0.0220652956675701f, 0.0220154189499333f, 0.0219655550488802f,
+ 0.0219157039327235f, 0.0218658655697320f, 0.0218160399281303f,
+ 0.0217662269760989f, 0.0217164266817738f, 0.0216666390132473f,
+ 0.0216168639385673f, 0.0215671014257376f, 0.0215173514427178f,
+ 0.0214676139574232f, 0.0214178889377253f, 0.0213681763514512f,
+ 0.0213184761663838f, 0.0212687883502620f, 0.0212191128707804f,
+ 0.0211694496955896f, 0.0211197987922958f, 0.0210701601284612f,
+ 0.0210205336716039f, 0.0209709193891977f, 0.0209213172486724f,
+ 0.0208717272174133f, 0.0208221492627619f, 0.0207725833520154f,
+ 0.0207230294524270f, 0.0206734875312053f, 0.0206239575555151f,
+ 0.0205744394924771f, 0.0205249333091677f, 0.0204754389726188f,
+ 0.0204259564498189f, 0.0203764857077117f, 0.0203270267131969f,
+ 0.0202775794331301f, 0.0202281438343228f, 0.0201787198835422f,
+ 0.0201293075475113f, 0.0200799067929092f, 0.0200305175863706f,
+ 0.0199811398944860f, 0.0199317736838018f, 0.0198824189208204f,
+ 0.0198330755720000f, 0.0197837436037543f, 0.0197344229824530f,
+ 0.0196851136744220f, 0.0196358156459426f, 0.0195865288632520f,
+ 0.0195372532925434f, 0.0194879888999657f, 0.0194387356516237f,
+ 0.0193894935135779f, 0.0193402624518448f, 0.0192910424323968f,
+ 0.0192418334211618f, 0.0191926353840239f, 0.0191434482868227f,
+ 0.0190942720953539f, 0.0190451067753690f, 0.0189959522925752f,
+ 0.0189468086126355f, 0.0188889190431616f, 0.0188228895446890f,
+ 0.0187568748920607f, 0.0186908750819390f, 0.0186248901109865f,
+ 0.0185589199758671f, 0.0184929646732450f, 0.0184270241997851f,
+ 0.0183610985521537f, 0.0182951877270173f, 0.0182292917210430f,
+ 0.0181634105308991f, 0.0180975441532544f, 0.0180316925847787f,
+ 0.0179658558221421f, 0.0179000338620157f, 0.0178342267010716f,
+ 0.0177684343359822f, 0.0177026567634210f, 0.0176368939800620f,
+ 0.0175711459825800f, 0.0175054127676507f, 0.0174396943319504f,
+ 0.0217357186797338f, 0.0216700297925235f, 0.0216043556745757f,
+ 0.0215386963225698f, 0.0214730517331858f, 0.0214074219031045f,
+ 0.0213418068290075f, 0.0212762065075772f, 0.0212106209354964f,
+ 0.0211450501094493f, 0.0210794940261201f, 0.0210139526821943f,
+ 0.0209484260743579f, 0.0208829141992977f, 0.0208174170537012f,
+ 0.0207519346342566f, 0.0206864669376530f, 0.0206210139605803f,
+ 0.0205555756997288f, 0.0204901521517898f, 0.0204247433134555f,
+ 0.0203593491814184f, 0.0202939697523721f, 0.0202286050230107f,
+ 0.0201632549900294f, 0.0200979196501238f, 0.0200325989999903f,
+ 0.0199672930363261f, 0.0199020017558292f, 0.0198367251551984f,
+ 0.0197714632311330f, 0.0197062159803331f, 0.0196409833994998f,
+ 0.0195757654853346f, 0.0195105622345400f, 0.0194453736438190f,
+ 0.0193801997098757f, 0.0193150404294146f, 0.0192498957991409f,
+ 0.0191847658157610f, 0.0191196504759815f, 0.0190545497765102f,
+ 0.0189894637140552f, 0.0189243922853257f, 0.0188593354870315f,
+ 0.0187942933158832f, 0.0187292657685919f, 0.0186642528418697f,
+ 0.0185992545324294f, 0.0185342708369846f, 0.0184693017522493f,
+ 0.0184043472749386f, 0.0183394074017683f, 0.0182744821294548f,
+ 0.0182095714547152f, 0.0181446753742675f, 0.0180797938848304f,
+ 0.0180149269831233f, 0.0179500746658664f, 0.0178852369297804f,
+ 0.0178204137715871f, 0.0177556051880087f, 0.0176908111757684f,
+ 0.0176260317315900f, 0.0175612668521982f, 0.0174965165343181f,
+ 0.0174317807746758f, 0.0173670595699981f, 0.0173023529170125f,
+ 0.0172376608124473f, 0.0171729832530314f, 0.0171083202354946f,
+ 0.0170436717565673f, 0.0169790378129807f, 0.0169144184014667f,
+ 0.0168498135187580f, 0.0167852231615880f, 0.0167206473266909f,
+ 0.0166560860108013f, 0.0165915392106550f, 0.0165270069229884f,
+ 0.0164624891445385f, 0.0163979858720428f, 0.0163334971022403f,
+ 0.0162690228318700f, 0.0162045630576720f, 0.0161401177763870f,
+ 0.0160756869847564f, 0.0160112706795224f, 0.0159468688574280f,
+ 0.0158824815152168f, 0.0158181086496333f, 0.0157537502574225f,
+ 0.0156894063353304f, 0.0156250768801034f, 0.0155607618884889f,
+ 0.0154964613572351f, 0.0154321752830905f, 0.0153679036628048f,
+ 0.0153036464931283f, 0.0152394037708118f, 0.0151751754926072f,
+ 0.0151109616552667f, 0.0150467622555436f, 0.0149825772901918f,
+ 0.0149184067559660f, 0.0148542506496213f, 0.0147901089679141f,
+ 0.0147259817076010f, 0.0146618688654396f, 0.0145977704381882f,
+ 0.0145336864226058f, 0.0144696168154521f, 0.0144055616134876f,
+ 0.0143415208134734f, 0.0142774944121715f, 0.0142134824063446f,
+ 0.0141494847927560f, 0.0140855015681696f, 0.0140215327293505f,
+ 0.0139575782730643f, 0.0182553662036547f, 0.0181914405027335f,
+ 0.0181275291746461f, 0.0180636322161610f, 0.0179997496240473f,
+ 0.0179358813950749f, 0.0178720275260146f, 0.0178081880136376f,
+ 0.0177443628547161f, 0.0176805520460228f, 0.0176167555843314f,
+ 0.0175529734664161f, 0.0174892056890518f, 0.0174254522490144f,
+ 0.0173617131430802f, 0.0172979883680264f, 0.0172342779206310f,
+ 0.0171705817976724f, 0.0171068999959302f, 0.0170432325121844f,
+ 0.0169795793432156f, 0.0169159404858055f, 0.0168523159367363f,
+ 0.0167887056927909f, 0.0167251097507530f, 0.0166615281074072f,
+ 0.0165979607595384f, 0.0165344077039326f, 0.0164708689373761f,
+ 0.0164073444566565f, 0.0163438342585618f, 0.0162803383398807f,
+ 0.0162168566974025f, 0.0161533893279177f, 0.0160899362282169f,
+ 0.0160264973950919f, 0.0159630728253350f, 0.0158996625157394f,
+ 0.0158362664630987f, 0.0157728846642075f, 0.0157095171158610f,
+ 0.0156461638148553f, 0.0155828247579869f, 0.0155194999420533f,
+ 0.0154561893638526f, 0.0153928930201836f, 0.0153296109078459f,
+ 0.0152663430236398f, 0.0152030893643661f, 0.0151398499268268f,
+ 0.0150766247078242f, 0.0150134137041614f, 0.0149502169126424f,
+ 0.0148870343300717f, 0.0148238659532546f, 0.0147607117789972f,
+ 0.0146975718041061f, 0.0146344460253890f, 0.0145713344396539f,
+ 0.0145082370437097f, 0.0144451538343661f, 0.0143820848084334f,
+ 0.0143190299627227f, 0.0142559892940457f, 0.0141929627992148f,
+ 0.0141299504750434f, 0.0140669523183453f, 0.0140039683259351f,
+ 0.0139409984946283f, 0.0138780428212408f, 0.0138151013025895f,
+ 0.0137521739354917f, 0.0136892607167660f, 0.0136263616432309f,
+ 0.0135634767117064f, 0.0135006059190125f, 0.0134377492619707f,
+ 0.0133749067374024f, 0.0133120783421303f, 0.0132492640729776f,
+ 0.0131864639267682f, 0.0131236779003268f, 0.0130609059904787f,
+ 0.0129981481940499f, 0.0129354045078674f, 0.0128726749287585f,
+ 0.0128099594535516f, 0.0127472580790754f, 0.0126845708021597f,
+ 0.0126218976196349f, 0.0125592385283320f, 0.0124965935250826f,
+ 0.0124339626067194f, 0.0123713457700756f, 0.0123087430119851f,
+ 0.0122461543292824f, 0.0121835797188031f, 0.0121210191773830f,
+ 0.0120584727018589f, 0.0119959402890683f, 0.0119334219358495f,
+ 0.0118709176390412f, 0.0118084273954832f, 0.0117459512020156f,
+ 0.0116834890554797f, 0.0116210409527169f, 0.0115586068905700f,
+ 0.0114961868658818f, 0.0114337808754965f, 0.0113713889162585f,
+ 0.0113090109850131f, 0.0112466470786063f, 0.0111842971938848f,
+ 0.0111219613276961f, 0.0110596394768881f, 0.0109973316383098f,
+ 0.0152967658163884f, 0.0152344859928189f, 0.0151722201720297f,
+ 0.0151099683508726f, 0.0150477305262000f, 0.0149855066948649f,
+ 0.0149232968537211f, 0.0148611009996230f, 0.0147989191294260f,
+ 0.0147367512399860f, 0.0146745973281595f, 0.0146124573908037f,
+ 0.0145503314247770f, 0.0144882194269378f, 0.0144261213941456f,
+ 0.0143640373232607f, 0.0143019672111439f, 0.0142399110546566f,
+ 0.0141778688506612f, 0.0141158405960207f, 0.0140538262875987f,
+ 0.0139918259222596f, 0.0139298394968685f, 0.0138678670082912f,
+ 0.0138059084533941f, 0.0137439638290446f, 0.0136820331321104f,
+ 0.0136201163594601f, 0.0135582135079633f, 0.0134963245744897f,
+ 0.0134344495559101f, 0.0133725884490961f, 0.0133107412509195f,
+ 0.0132489079582535f, 0.0131870885679713f, 0.0131252830769473f,
+ 0.0130634914820565f, 0.0130017137801745f, 0.0129399499681775f,
+ 0.0128782000429427f, 0.0128164640013478f, 0.0127547418402713f,
+ 0.0126930335565922f, 0.0126313391471905f, 0.0125696586089468f,
+ 0.0125079919387422f, 0.0124463391334587f, 0.0123847001899790f,
+ 0.0123230751051865f, 0.0122614638759652f, 0.0121998664991998f,
+ 0.0121382829717759f, 0.0120767132905796f, 0.0120151574524978f,
+ 0.0119536154544179f, 0.0118920872932283f, 0.0118305729658180f,
+ 0.0117690724690765f, 0.0117075857998943f, 0.0116461129551625f,
+ 0.0115846539317728f, 0.0115232087266176f, 0.0114617773365900f,
+ 0.0114003597585841f, 0.0113389559894942f, 0.0112775660262157f,
+ 0.0112161898656444f, 0.0111548275046771f, 0.0110934789402111f,
+ 0.0110321441691445f, 0.0109708231883759f, 0.0109095159948048f,
+ 0.0108482225853314f, 0.0107869429568564f, 0.0107256771062815f,
+ 0.0106644250305088f, 0.0106031867264412f, 0.0105419621909824f,
+ 0.0104807514210367f, 0.0104195544135091f, 0.0103583711653052f,
+ 0.0102972016733315f, 0.0102360459344952f, 0.0101749039457040f,
+ 0.0101137757038663f, 0.0100526612058913f, 0.00999156044868904f,
+ 0.00993047342916997f, 0.00986940014424537f, 0.00980834059082714f,
+ 0.00974729476582803f, 0.00968626266616141f, 0.00962524428874123f,
+ 0.00956423963048225f, 0.00950324868829994f, 0.00944227145911042f,
+ 0.00938130793983050f, 0.00932035812737764f, 0.00925942201867014f,
+ 0.00919849961062680f, 0.00913759090016730f, 0.00907669588421184f,
+ 0.00901581455968142f, 0.00895494692349780f, 0.00889409297258320f,
+ 0.00883325270386071f, 0.00877242611425416f, 0.00871161320068786f,
+ 0.00865081396008705f, 0.00859002838937745f, 0.00852925648548564f,
+ 0.00846849824533880f, 0.00840775366586477f, 0.00834702274399218f,
+ 0.00828630547665032f, 0.00822560186076915f, 0.00816491189327928f,
+ 0.00810423557111206f, 0.00804357289119956f, 0.00798292385047450f,
+ 0.00792228844587023f, 0.00786166667432087f, 0.00780105853276131f,
+ 0.00774046401812689f, 0.00767988312735390f, 0.00761931585737907f,
+ 0.00755876220514012f, 0.00749822216757512f, 0.0117994237492008f,
+ 0.0117389109318013f, 0.0116784117198946f, 0.0116179261104218f,
+ 0.0115574541003245f, 0.0114969956865451f, 0.0114365508660266f,
+ 0.0113761196357128f, 0.0113157019925481f, 0.0112552979334775f,
+ 0.0111949074554470f, 0.0111345305554030f, 0.0110741672302926f,
+ 0.0110138174770638f, 0.0109534812926650f, 0.0108931586740455f,
+ 0.0108328496181552f, 0.0107725541219447f, 0.0107122721823654f,
+ 0.0106520037963691f, 0.0105917489609085f, 0.0105315076729371f,
+ 0.0104712799294088f, 0.0104110657272783f, 0.0103508650635009f,
+ 0.0102906779350330f, 0.0102305043388311f, 0.0101703442718527f,
+ 0.0101101977310560f, 0.0100500647133998f, 0.00998994521584351f,
+ 0.00992983923534746f, 0.00986974676887237f, 0.00980966781338000f,
+ 0.00974960236583244f, 0.00968955042319258f, 0.00962951198242412f,
+ 0.00956948704049132f, 0.00950947559435911f, 0.00944947764099319f,
+ 0.00938949317735982f, 0.00932952220042610f, 0.00926956470715973f,
+ 0.00920962069452902f, 0.00914969015950301f, 0.00908977309905157f,
+ 0.00902986951014501f, 0.00896997938975452f, 0.00891010273485171f,
+ 0.00885023954240927f, 0.00879038980940028f, 0.00873055353279850f,
+ 0.00867073070957841f, 0.00861092133671532f, 0.00855112541118508f,
+ 0.00849134292996412f, 0.00843157389002974f, 0.00837181828835992f,
+ 0.00831207612193319f, 0.00825234738772879f, 0.00819263208272664f,
+ 0.00813293020390743f, 0.00807324174825247f, 0.00801356671274367f,
+ 0.00795390509436372f, 0.00789425689009604f, 0.00783462209692454f,
+ 0.00777500071183396f, 0.00771539273180966f, 0.00765579815383771f,
+ 0.00759621697490487f, 0.00753664919199842f, 0.00747709480210662f,
+ 0.00741755380221809f, 0.00735802618932235f, 0.00729851196040943f,
+ 0.00723901111247016f, 0.00717952364249613f, 0.00712004954747930f,
+ 0.00706058882441252f, 0.00700114147028941f, 0.00694170748210410f,
+ 0.00688228685685138f, 0.00682287959152672f, 0.00676348568312646f,
+ 0.00670410512864744f, 0.00664473792508719f, 0.00658538406944387f,
+ 0.00652604355871650f, 0.00646671638990454f, 0.00640740256000838f,
+ 0.00634810206602876f, 0.00628881490496747f, 0.00622954107382667f,
+ 0.00617028056960933f, 0.00611103338931901f, 0.00605179952996010f,
+ 0.00599257898853756f, 0.00593337176205699f, 0.00587417784752470f,
+ 0.00581499724194773f, 0.00575582994233370f, 0.00569667594569095f,
+ 0.00563753524902838f, 0.00557840784935593f, 0.00551929374368371f,
+ 0.00546019292902289f, 0.00540110540238498f, 0.00534203116078258f,
+ 0.00528297020122859f, 0.00522392252073672f, 0.00516488811632138f,
+ 0.00510586698499765f, 0.00504685912378122f, 0.00498786452968852f,
+ 0.00492888319973650f, 0.00486991513094304f, 0.00481096032032652f,
+ 0.00475201876490594f, 0.00469309046170108f, 0.00463417540773242f,
+ 0.00457527360002097f, 0.00451638503558854f, 0.00445750971145748f,
+ 0.00439864762465103f, 0.00433979877219282f, 0.00428096315110732f,
+ 0.00422214075841959f, 0.00416333159115556f, 0.00410453564634156f,
+ 0.00404575292100462f, 0.00398698341217263f, 0.00392822711687407f,
+ 0.00386948403213799f, 0.00381075415499416f, 0.00375203748247305f,
+ 0.00369333401160576f, 0.00363464373942418f, 0.00357596666296056f,
+ 0.00351730277924822f, 0.00345865208532087f, 0.00340001457821293f,
+ 0.00334139025495950f, 0.00328277911259650f, 0.00322418114816031f,
+ 0.00316559635868802f, 0.00310702474121738f, 0.00304846629278699f,
+ 0.00298992101043588f, 0.00293138889120381f, 0.00287286993213121f,
+ 0.00281436413025932f, 0.00275587148262979f, 0.00269739198628516f,
+ 0.00263892563826845f, 0.00258047243562354f, 0.00252203237539478f,
+ 0.00246360545462732f, 0.00240519167036685f, 0.00234679101965996f,
+ 0.00228840349955362f, 0.00223002910709563f, 0.00217166783933437f,
+ 0.00211331969331902f, 0.00205498466609932f, 0.00199666275472560f,
+ 0.00193835395624892f, 0.00188005826772120f, 0.00182177568619468f,
+ 0.00176350620872251f, 0.00170524983235837f, 0.00164700655415667f,
+ 0.00158877637117250f, 0.00153055928046153f, 0.00147235527908007f,
+ 0.00141416436408531f, 0.00135598653253494f, 0.00129782178148719f,
+ 0.00123967010800113f, 0.00118153150913658f, 0.00112340598195376f,
+ 0.00106529352351364f, 0.00100719413087796f, 0.000949107801109128f,
+ 0.000891034531269985f, 0.000832974318424273f, 0.000774927159636230f,
+ 0.000716893051970924f, 0.000658871992493926f, 0.000600863978271471f,
+ 0.000542869006370628f, 0.000484887073858964f, 0.000426918177804714f,
+ 0.00473069032285445f, 0.00467274749092250f, 0.00461481768665678f,
+ 0.00455690090712813f, 0.00449899714940810f, 0.00444110641056905f,
+ 0.00438322868768370f, 0.00432536397782568f, 0.00426751227806910f,
+ 0.00420967358548896f, 0.00415184789716061f, 0.00409403521016033f,
+ 0.00403623552156485f, 0.00397844882845178f, 0.00392067512789918f,
+ 0.00386291441698589f, 0.00380516669279124f, 0.00374743195239552f,
+ 0.00368971019287939f, 0.00363200141132430f, 0.00357430560481226f,
+ 0.00351662277042614f, 0.00345895290524928f, 0.00340129600636568f,
+ 0.00334365207086007f, 0.00328602109581783f, 0.00322840307832495f,
+ 0.00317079801546810f, 0.00311320590433462f, 0.00305562674201249f,
+ 0.00299806052559032f, 0.00294050725215744f, 0.00288296691880374f,
+ 0.00282543952261988f, 0.00276792506069706f, 0.00271042353012718f,
+ 0.00265293492800284f, 0.00259545925141724f, 0.00253799649746422f,
+ 0.00248054666323838f, 0.00242310974583471f, 0.00236568574234924f,
+ 0.00230827464987843f, 0.00225087646551925f, 0.00219349118636958f,
+ 0.00213611880952797f, 0.00207875933209334f, 0.00202141275116546f,
+ 0.00196407906384477f, 0.00190675826723236f, 0.00184945035842982f,
+ 0.00179215533453958f, 0.00173487319266458f, 0.00167760392990851f,
+ 0.00162034754337570f, 0.00156310403017101f, 0.00150587338740016f,
+ 0.00144865561216939f, 0.00139145070158553f, 0.00133425865275616f,
+ 0.00127707946278949f, 0.00121991312879444f, 0.00116275964788048f,
+ 0.00110561901715772f, 0.00104849123373701f, 0.000991376294729840f,
+ 0.000934274197248231f, 0.000877184938404996f, 0.000820108515313556f,
+ 0.000763044925087941f, 0.000705994164842849f, 0.000648956231693587f,
+ 0.000591931122756240f, 0.000534918835147447f, 0.000477919365984458f,
+ 0.000420932712385191f, 0.000363958871468284f, 0.000306997840353040f,
+ 0.000250049616159320f, 0.000193114196007482f, 0.000136191577018996f,
+ 7.92817563154968e-05f, 2.23847310195091e-05f, 0.000000000000000000f,
+ 0.000000000000000000f, 0.000000000000000000f, 0.000000000000000000f,
+ 0.000000000000000000f, 0.000000000000000000f, 0.000000000000000000f,
+ 0.000000000000000000f, 0.000000000000000000f, 0.000000000000000000f,
+ 0.000000000000000000f, 0.000000000000000000f, 0.000000000000000000f,
+ 0.000000000000000000f, 0.000000000000000000f, 0.000000000000000000f,
+ 0.000000000000000000f, 0.000000000000000000f, 0.000000000000000000f,
+ 0.000000000000000000f, 0.000000000000000000f, 0.000000000000000000f,
+ 0.000000000000000000f, 0.000000000000000000f, 0.000000000000000000f,
+ 0.000000000000000000f, 0.000000000000000000f, 0.000000000000000000f,
+ 0.000000000000000000f, 0.000000000000000000f, 0.000000000000000000f};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_SPECTRAL_REVERB_CONSTANTS_AND_TABLES_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb_test.cc
new file mode 100644
index 000000000..015159fa1
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/spectral_reverb_test.cc
@@ -0,0 +1,324 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/spectral_reverb.h"
+
+#include <algorithm>
+#include <cmath>
+#include <numeric>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "dsp/biquad_filter.h"
+#include "dsp/fft_manager.h"
+#include "dsp/filter_coefficient_generators.h"
+#include "utils/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+const size_t kFramesPerBuffer512 = 512;
+const size_t kFramesPerBuffer2048 = 2048;
+const size_t kFramesPerBuffer713 = 713;
+const size_t kNumReverbOverlap = 4;
+const size_t kReverbFftSize = 4096;
+const int kSampleFrequency16 = 16000;
+const int kSampleFrequency24 = 24000;
+
+// A set of 9 RT60 values for each of the octave bands with center frequencies
+// between 31.25Hz and 8kHz.
+const float kRt60s[kNumReverbOctaveBands] = {0.8f, 0.8f, 0.7f, 0.7f, 0.65f,
+ 0.65f, 0.6f, 0.6f, 0.5f};
+
+void ImpulseResponse(float length_sec, int sample_frequency,
+ size_t frames_per_buffer, SpectralReverb* reverb,
+ std::vector<float>* left_response,
+ std::vector<float>* right_response) {
+ AudioBuffer input(kNumMonoChannels, frames_per_buffer);
+ input.Clear();
+ input[0][0] = 1.0f;
+ AudioBuffer output(kNumStereoChannels, frames_per_buffer);
+
+ reverb->Process(input[0], &output[0], &output[1]);
+
+ // The number of iterations required so that we will have no more than a few
+ // zeros following each of our impulse responses.
+ const float tail_length_samples =
+ kReverbFftSize + length_sec * static_cast<float>(sample_frequency);
+ const size_t num_iterations =
+ 100 + 1 +
+ static_cast<size_t>(std::ceil(tail_length_samples /
+ static_cast<float>(frames_per_buffer)));
+
+ for (size_t i = 0; i < num_iterations; ++i) {
+ left_response->insert(left_response->end(), output[0].begin(),
+ output[0].end());
+ right_response->insert(right_response->end(), output[1].begin(),
+ output[1].end());
+
+ input.Clear();
+ reverb->Process(input[0], &output[0], &output[1]);
+ }
+}
+
+void StereoOutputTestHelper(int sample_frequency, size_t frames_per_buffer) {
+ SpectralReverb reverb(sample_frequency, frames_per_buffer);
+ reverb.SetRt60PerOctaveBand(kRt60s);
+
+ std::vector<float> output_collect_left;
+ std::vector<float> output_collect_right;
+ ImpulseResponse(1.0f /*length [sec]*/, sample_frequency, frames_per_buffer,
+ &reverb, &output_collect_left, &output_collect_right);
+
+ // First test that there are kReverbFftSize / kNumReverbOverlap zeros at the
+ // beginning of the system impulse responses ONLY if this value is greater
+ // than |frames_per_buffer|, otherwise expect no onset zeros.
+ const size_t kOverlapLength = kReverbFftSize / kNumReverbOverlap;
+ const size_t onset = kOverlapLength > frames_per_buffer ? kOverlapLength : 0;
+ for (size_t i = 0; i < onset; ++i) {
+ EXPECT_NEAR(0.0f, output_collect_left[i], kEpsilonFloat);
+ EXPECT_NEAR(0.0f, output_collect_right[i], kEpsilonFloat);
+ }
+ // Test the sample five samples later is non zero. i.e. the tail has begun.
+ EXPECT_NE(0.0f, output_collect_left[onset + 5]);
+ EXPECT_NE(0.0f, output_collect_right[onset + 5]);
+}
+
+void ZeroRt60TestHelper(int sample_frequency, float rt_60) {
+ SpectralReverb reverb(sample_frequency, kFramesPerBuffer512);
+ reverb.SetRt60PerOctaveBand(kRt60s);
+
+ std::vector<float> output_collect_left;
+ std::vector<float> output_collect_right;
+ ImpulseResponse(1.0f /*length [sec]*/, sample_frequency, kFramesPerBuffer512,
+ &reverb, &output_collect_left, &output_collect_right);
+
+ // Test that all the frames of the tail buffer are zeros.
+ for (size_t i = 0; i < kFramesPerBuffer512; ++i) {
+ EXPECT_NEAR(0.0f, output_collect_left[i], kEpsilonFloat);
+ EXPECT_NEAR(0.0f, output_collect_right[i], kEpsilonFloat);
+ }
+}
+
+void TailDecayTestHelper(int sample_frequency, size_t frames_per_buffer) {
+ // Butterworth Lowpass filter with cutoff frequency at 3Hz.
+ BiquadCoefficients low_pass_coefficients(
+ 1.0f, -1.999444639647f, 0.999444793816755f, 1e-5f, 2e-5f, 1e-5f);
+ BiquadFilter low_pass_filter(low_pass_coefficients, frames_per_buffer);
+ const std::vector<float> kUniformRt60s(kNumReverbOctaveBands, kRt60s[0]);
+ SpectralReverb reverb(sample_frequency, frames_per_buffer);
+ reverb.SetRt60PerOctaveBand(kUniformRt60s.data());
+
+ std::vector<float> output_collect_left;
+ std::vector<float> output_collect_right;
+ ImpulseResponse(2.0f /*length [sec]*/, sample_frequency, frames_per_buffer,
+ &reverb, &output_collect_left, &output_collect_right);
+
+ for (size_t i = 0; i < output_collect_left.size(); ++i) {
+ output_collect_left[i] = std::abs(output_collect_left[i]);
+ output_collect_right[i] = std::abs(output_collect_right[i]);
+ }
+
+ const size_t response_length = output_collect_left.size();
+
+ AudioBuffer test_buffer(kNumMonoChannels, response_length);
+ test_buffer[0] = output_collect_left;
+
+ // Very low frequency content of tail. This should essentially just preserve
+ // the decay.
+ low_pass_filter.Filter(test_buffer[0], &test_buffer[0]);
+
+ const size_t max_location = static_cast<size_t>(
+ std::max_element(test_buffer[0].begin(), test_buffer[0].end()) -
+ test_buffer[0].begin());
+
+ // Stop before the very end of the tail as it goes to zero.
+ const size_t end_point = max_location + kReverbFftSize;
+ const size_t step_size = (end_point - max_location) / 20;
+
+ // Test for decay.
+ for (size_t i = max_location + step_size; i < end_point; i += step_size) {
+ EXPECT_GT(std::abs(test_buffer[0][i - step_size]),
+ std::abs(test_buffer[0][i]));
+ }
+}
+
+void DecorrelatedTailsTestHelper(int sample_frequency,
+ size_t frames_per_buffer) {
+ // This value has been found empirically in MATLAB.
+ const float kMaxCrossCorrelation = 12.0f;
+ const std::vector<float> kUniformRt60s(kNumReverbOctaveBands, 0.7f);
+ SpectralReverb reverb(sample_frequency, frames_per_buffer);
+ reverb.SetRt60PerOctaveBand(kUniformRt60s.data());
+
+ std::vector<float> output_collect_left;
+ std::vector<float> output_collect_right;
+ ImpulseResponse(0.7f /*length [sec]*/, sample_frequency, frames_per_buffer,
+ &reverb, &output_collect_left, &output_collect_right);
+
+ // Find the absolute maximum elements of each vector.
+ auto min_max_left = std::minmax_element(output_collect_left.begin(),
+ output_collect_left.end());
+ size_t left_max_index =
+ std::abs(*min_max_left.first) > std::abs(*min_max_left.second)
+ ? (min_max_left.first - output_collect_left.begin())
+ : (min_max_left.second - output_collect_left.begin());
+ auto min_max_right = std::minmax_element(output_collect_right.begin(),
+ output_collect_right.end());
+ size_t right_max_index =
+ std::abs(*min_max_right.first) > std::abs(*min_max_right.second)
+ ? (min_max_right.first - output_collect_right.begin())
+ : (min_max_right.second - output_collect_right.begin());
+
+ // Take a sample of the tails for cross correlation.
+ AudioBuffer pair(kNumStereoChannels, kReverbFftSize);
+ for (size_t i = 0; i < kReverbFftSize; ++i) {
+ pair[0][i] = output_collect_left[i + left_max_index] /
+ output_collect_left[left_max_index];
+ pair[1][i] = output_collect_right[i + right_max_index] /
+ output_collect_right[right_max_index];
+ }
+
+ // The cross correlation is not normalized. Thus we can expect a very small
+ // value. Naturally, if the RT60 inputs are changed the expected value would
+ // thus be different.
+ const float max_xcorr = MaxCrossCorrelation(pair[0], pair[1]);
+ EXPECT_LT(max_xcorr, kMaxCrossCorrelation);
+}
+
+} // namespace
+
+// Tests that the stereo output from the Reverbs Process fuction has the
+// expected properties of predelay and length.
+TEST(SpectralReverbTest, StereoOutputTest) {
+ StereoOutputTestHelper(kSampleFrequency24, kFramesPerBuffer512);
+ StereoOutputTestHelper(kSampleFrequency24, kFramesPerBuffer2048);
+ StereoOutputTestHelper(kSampleFrequency24, kFramesPerBuffer713);
+ StereoOutputTestHelper(kSampleFrequency16, kFramesPerBuffer512);
+ StereoOutputTestHelper(kSampleFrequency16, kFramesPerBuffer2048);
+ StereoOutputTestHelper(kSampleFrequency16, kFramesPerBuffer713);
+}
+
+// Tests that the stereo output from the Reverb's Process function has the
+// output of all zeros when the RT60 values are all zero.
+TEST(SpectralReverbTest, ZeroRt60Test) {
+ const float kZeroRt = 0.0f;
+ const float kBelowMinRt = 0.12f;
+ ZeroRt60TestHelper(kSampleFrequency24, kZeroRt);
+ ZeroRt60TestHelper(kSampleFrequency16, kBelowMinRt);
+ ZeroRt60TestHelper(kSampleFrequency16, kZeroRt);
+ ZeroRt60TestHelper(kSampleFrequency16, kBelowMinRt);
+}
+
+// Tests that the tail is decaying over time.
+TEST(SpectralReverbTest, TailDecayTest) {
+ TailDecayTestHelper(kSampleFrequency24, kFramesPerBuffer512);
+ TailDecayTestHelper(kSampleFrequency24, kFramesPerBuffer2048);
+ TailDecayTestHelper(kSampleFrequency16, kFramesPerBuffer512);
+ TailDecayTestHelper(kSampleFrequency16, kFramesPerBuffer2048);
+}
+
+// Tests that the stereo tail pairs are highy decorrelated.
+TEST(SpectralReverbTest, DecorrelatedTailsTest) {
+ DecorrelatedTailsTestHelper(kSampleFrequency24, kFramesPerBuffer512);
+ DecorrelatedTailsTestHelper(kSampleFrequency24, kFramesPerBuffer2048);
+ DecorrelatedTailsTestHelper(kSampleFrequency24, kFramesPerBuffer713);
+ DecorrelatedTailsTestHelper(kSampleFrequency16, kFramesPerBuffer512);
+ DecorrelatedTailsTestHelper(kSampleFrequency16, kFramesPerBuffer2048);
+ DecorrelatedTailsTestHelper(kSampleFrequency16, kFramesPerBuffer713);
+}
+
+// Tests that the gain parameter behaves as expected.
+TEST(SpecralReverbTest, GainTest) {
+ const float kReverbLength = 0.5f;
+ const float kGain = 100.0f;
+ const float kGainEpsilon = 0.32f;
+ const std::vector<float> kUniformRt60s(kNumReverbOctaveBands, kReverbLength);
+ SpectralReverb reverb(kSampleFrequency24, kFramesPerBuffer512);
+ reverb.SetRt60PerOctaveBand(kUniformRt60s.data());
+
+ // Calculate scaled and unscaled impulse responses.
+ std::vector<float> output_left;
+ std::vector<float> output_right;
+ ImpulseResponse(kReverbLength, kSampleFrequency24, kFramesPerBuffer512,
+ &reverb, &output_left, &output_right);
+ std::vector<float> output_left_scaled;
+ reverb.SetGain(kGain);
+ ImpulseResponse(kReverbLength, kSampleFrequency24, kFramesPerBuffer512,
+ &reverb, &output_left_scaled, &output_right);
+
+ // Determine the max absolute entry in each impulse response.
+ std::transform(output_left.begin(), output_left.end(), output_left.begin(),
+ static_cast<float (*)(float)>(&std::abs));
+ std::transform(output_left_scaled.begin(), output_left_scaled.end(),
+ output_left_scaled.begin(),
+ static_cast<float (*)(float)>(&std::abs));
+ const float max_unscaled =
+ *std::max_element(output_left.begin(), output_left.end());
+ const float max_scaled =
+ *std::max_element(output_left_scaled.begin(), output_left_scaled.end());
+ EXPECT_GT(max_scaled, max_unscaled);
+ EXPECT_NEAR((max_unscaled / max_scaled) / (1.0f / kGain), 1.0f, kGainEpsilon);
+}
+
+// Tests that when the feedback values are all ~0.0f, no processing is
+// performed (output is all zero). Also tests that if even one of the rt60s
+// result in a non zero feedback that the result will be non zero.
+TEST(SpectralReverbTest, DisabledProcessingTest) {
+ const float kReverbLength = 0.1f;
+ const std::vector<float> kUniformRt60s(kNumReverbOctaveBands, kReverbLength);
+ SpectralReverb reverb(kSampleFrequency24, kFramesPerBuffer512);
+ reverb.SetRt60PerOctaveBand(kUniformRt60s.data());
+ std::vector<float> output_left;
+ std::vector<float> output_right;
+ ImpulseResponse(kReverbLength, kSampleFrequency24, kFramesPerBuffer512,
+ &reverb, &output_left, &output_right);
+ for (size_t i = 0; i < output_left.size(); ++i) {
+ EXPECT_FLOAT_EQ(output_left[i], 0.0f);
+ EXPECT_FLOAT_EQ(output_right[i], 0.0f);
+ }
+
+ // Test a non zero case.
+ const float kLongerReverbLength = 0.4f;
+ std::vector<float> rt60s(kNumReverbOctaveBands, 0.0f);
+ rt60s[0] = kLongerReverbLength;
+ output_left.resize(0);
+ output_right.resize(0);
+ reverb.SetRt60PerOctaveBand(rt60s.data());
+ ImpulseResponse(kReverbLength, kSampleFrequency24, kFramesPerBuffer512,
+ &reverb, &output_left, &output_right);
+ const float sum_left =
+ std::accumulate(output_left.begin(), output_left.end(), 0.0f);
+ EXPECT_NE(sum_left, 0.0f);
+ const float sum_right =
+ std::accumulate(output_right.begin(), output_right.end(), 0.0f);
+ EXPECT_NE(sum_right, 0.0f);
+
+ // Set gain to zero and test again.
+ output_left.resize(0);
+ output_right.resize(0);
+ reverb.SetGain(0.0f);
+ ImpulseResponse(kReverbLength, kSampleFrequency24, kFramesPerBuffer512,
+ &reverb, &output_left, &output_right);
+ for (size_t i = 0; i < output_left.size(); ++i) {
+ EXPECT_FLOAT_EQ(output_left[i], 0.0f);
+ EXPECT_FLOAT_EQ(output_right[i], 0.0f);
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/stereo_panner.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/stereo_panner.cc
new file mode 100644
index 000000000..373db9989
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/stereo_panner.cc
@@ -0,0 +1,46 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/stereo_panner.h"
+
+#include <cmath>
+
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+const float kStereoLeftRadians = kRadiansFromDegrees * kStereoLeftDegrees;
+const float kStereoRightRadians = kRadiansFromDegrees * kStereoRightDegrees;
+
+void CalculateStereoPanGains(const SphericalAngle& source_direction,
+ std::vector<float>* stereo_gains) {
+ // Note this applies the same panning law as was applied by the ambisonic
+ // equivalent panner to ensure consistency.
+ DCHECK(stereo_gains);
+ stereo_gains->resize(kNumStereoChannels);
+
+ const float cos_direction_elevation = std::cos(source_direction.elevation());
+
+ (*stereo_gains)[0] =
+ 0.5f * (1.0f + std::cos(kStereoLeftRadians - source_direction.azimuth()) *
+ cos_direction_elevation);
+ (*stereo_gains)[1] =
+ 0.5f *
+ (1.0f + std::cos(kStereoRightRadians - source_direction.azimuth()) *
+ cos_direction_elevation);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/stereo_panner.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/stereo_panner.h
new file mode 100644
index 000000000..bf4cf1708
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/stereo_panner.h
@@ -0,0 +1,35 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_STEREO_PANNER_H_
+#define RESONANCE_AUDIO_DSP_STEREO_PANNER_H_
+
+#include <vector>
+
+#include "base/spherical_angle.h"
+
+namespace vraudio {
+
+// Computes a pair of stereo panner gains based on the |source_direction|.
+//
+// @param source_direction Azimuth and elevation of the sound source.
+// @param stereo_gains A pointer to vector of stereo loudspeaker gains.
+void CalculateStereoPanGains(const SphericalAngle& source_direction,
+ std::vector<float>* stereo_gains);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_STEREO_PANNER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/stereo_panner_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/stereo_panner_test.cc
new file mode 100644
index 000000000..d24230241
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/stereo_panner_test.cc
@@ -0,0 +1,88 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/stereo_panner.h"
+
+#include <cmath>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+const float kMinusThreeDecibels = kInverseSqrtTwo;
+
+// Tests that the |CalculateStereoPanGains| method will generate correct stereo
+// pan gains.
+TEST(StereoPannerTest, StereoTest) {
+ std::vector<float> speaker_gains;
+
+ SphericalAngle source_direction = SphericalAngle::FromDegrees(-90.0f, 0.0f);
+ CalculateStereoPanGains(source_direction, &speaker_gains);
+ EXPECT_NEAR(0.0f, speaker_gains[0], kEpsilonFloat);
+ EXPECT_NEAR(1.0f, speaker_gains[1], kEpsilonFloat);
+
+ source_direction = SphericalAngle::FromDegrees(-45.0f, 0.0f);
+ CalculateStereoPanGains(source_direction, &speaker_gains);
+ EXPECT_NEAR(kMinusThreeDecibels, speaker_gains[1] - speaker_gains[0],
+ kEpsilonFloat);
+
+ source_direction = SphericalAngle::FromDegrees(0.0f, 0.0f);
+ CalculateStereoPanGains(source_direction, &speaker_gains);
+ EXPECT_NEAR(speaker_gains[0], speaker_gains[1], kEpsilonFloat);
+
+ source_direction = SphericalAngle::FromDegrees(45.0f, 0.0f);
+ CalculateStereoPanGains(source_direction, &speaker_gains);
+ EXPECT_NEAR(kMinusThreeDecibels, speaker_gains[0] - speaker_gains[1],
+ kEpsilonFloat);
+
+ source_direction = SphericalAngle::FromDegrees(90.0f, 0.0f);
+ CalculateStereoPanGains(source_direction, &speaker_gains);
+ EXPECT_NEAR(1.0f, speaker_gains[0], kEpsilonFloat);
+ EXPECT_NEAR(0.0f, speaker_gains[1], kEpsilonFloat);
+
+ source_direction = SphericalAngle::FromDegrees(0.0f, 45.0f);
+ CalculateStereoPanGains(source_direction, &speaker_gains);
+ EXPECT_NEAR(0.5f, speaker_gains[0], kEpsilonFloat);
+ EXPECT_NEAR(0.5f, speaker_gains[1], kEpsilonFloat);
+
+ source_direction = SphericalAngle::FromDegrees(0.0f, -60.0f);
+ CalculateStereoPanGains(source_direction, &speaker_gains);
+ EXPECT_NEAR(0.5f, speaker_gains[0], kEpsilonFloat);
+ EXPECT_NEAR(0.5f, speaker_gains[1], kEpsilonFloat);
+
+ source_direction = SphericalAngle::FromDegrees(0.0f, 90.0f);
+ CalculateStereoPanGains(source_direction, &speaker_gains);
+ EXPECT_NEAR(0.5f, speaker_gains[0], kEpsilonFloat);
+ EXPECT_NEAR(0.5f, speaker_gains[1], kEpsilonFloat);
+
+ source_direction = SphericalAngle::FromDegrees(0.0f, -90.0f);
+ CalculateStereoPanGains(source_direction, &speaker_gains);
+ EXPECT_NEAR(0.5f, speaker_gains[0], kEpsilonFloat);
+ EXPECT_NEAR(0.5f, speaker_gains[1], kEpsilonFloat);
+
+ source_direction = SphericalAngle::FromDegrees(45.0f, 45.0f);
+ CalculateStereoPanGains(source_direction, &speaker_gains);
+ EXPECT_NEAR(0.75f, speaker_gains[0], kEpsilonFloat);
+ EXPECT_NEAR(0.25f, speaker_gains[1], kEpsilonFloat);
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/utils.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/utils.cc
new file mode 100644
index 000000000..e1d8396a0
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/utils.cc
@@ -0,0 +1,190 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/utils.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+#include <random>
+#include <vector>
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/misc_math.h"
+
+#include "dsp/biquad_filter.h"
+#include "dsp/filter_coefficient_generators.h"
+
+namespace {
+
+// The mean and standard deviation of the normal distribution for bandlimited
+// Gaussian noise.
+const float kMean = 0.0f;
+const float kStandardDeviation = 1.0f;
+
+// Maximum group delay in seconds for each filter. In order to avoid audible
+// distortion, the maximum phase shift of a re-combined stereo sequence should
+// not exceed 5ms at high frequencies. That is why, maximum phase shift of
+// each filter is set to 1/2 of that value.
+const float kMaxGroupDelaySeconds = 0.0025f;
+
+// Phase modulation depth, chosen so that for a given max group delay filters
+// provide the lowest cross-correlation coefficient.
+const float kPhaseModulationDepth = 1.18f;
+
+// Constants used in the generation of uniform random number distributions.
+// https://en.wikipedia.org/wiki/Linear_congruential_generator
+const uint64 kMultiplier = 1664525L;
+const uint64 kIncrement = 1013904223L;
+const float kInt32ToFloat =
+ 1.0f / static_cast<float>(std::numeric_limits<uint32>::max());
+
+} // namespace
+
+namespace vraudio {
+
+void GenerateGaussianNoise(float mean, float std_deviation, unsigned seed,
+ AudioBuffer::Channel* noise_channel) {
+ DCHECK(noise_channel);
+ // First generate uniform noise.
+ GenerateUniformNoise(0.0f, 1.0f, seed, noise_channel);
+ const size_t length = noise_channel->size();
+
+ // Gaussian distribution with mean and standard deviation in pairs via the
+ // box-muller transform
+ // https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform.
+ for (size_t i = 0; i < length - 1; i += 2) {
+ const float part_one = std::sqrt(-2.0f * std::log((*noise_channel)[i]));
+ const float part_two = kTwoPi * (*noise_channel)[i + 1];
+ const float z0 = part_one * std::cos(part_two);
+ const float z1 = part_one * std::sin(part_two);
+ (*noise_channel)[i] = std_deviation * z0 + mean;
+ (*noise_channel)[i + 1] = std_deviation * z1 + mean;
+ }
+ // Handle the odd buffer length case cheaply.
+ if (length % 2 > 0) {
+ (*noise_channel)[length - 1] = (*noise_channel)[0];
+ }
+}
+
+void GenerateUniformNoise(float min, float max, unsigned seed,
+ AudioBuffer::Channel* noise_channel) {
+ // Simple random generator to avoid the use of std::uniform_real_distribution
+ // affected by https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56202
+ DCHECK(noise_channel);
+ DCHECK_LT(min, max);
+ const float scaled_conversion_factor = kInt32ToFloat * (max - min);
+ uint32 state = static_cast<uint32>(seed);
+ for (float& sample : *noise_channel) {
+ state = static_cast<uint32>(state * kMultiplier + kIncrement);
+ sample = min + static_cast<float>(state) * scaled_conversion_factor;
+ }
+}
+
+void GenerateBandLimitedGaussianNoise(float center_frequency, int sampling_rate,
+ unsigned seed,
+ AudioBuffer* noise_buffer) {
+
+
+ DCHECK(noise_buffer);
+ DCHECK_GT(sampling_rate, 0);
+ DCHECK_LT(center_frequency, static_cast<float>(sampling_rate) / 2.0f);
+ const size_t num_frames = noise_buffer->num_frames();
+
+ BiquadCoefficients bandpass_coefficients = ComputeBandPassBiquadCoefficients(
+ sampling_rate, center_frequency, /*bandwidth=*/1);
+ BiquadFilter bandpass_filter(bandpass_coefficients, num_frames);
+
+ for (auto& channel : *noise_buffer) {
+ GenerateGaussianNoise(kMean, kStandardDeviation, seed, &channel);
+ bandpass_filter.Filter(channel, &channel);
+ bandpass_filter.Clear();
+ }
+}
+
+std::unique_ptr<AudioBuffer> GenerateDecorrelationFilters(int sampling_rate) {
+
+ const int kMaxGroupDelaySamples = static_cast<int>(
+ roundf(kMaxGroupDelaySeconds * static_cast<float>(sampling_rate)));
+
+ // Filter coefficients according to:
+ // [1] F. Zotter, M. Frank, "Efficient Phantom Source Widening", Archives of
+ // Acoustics, Vol. 38, No. 1, pp. 27–37 (2013).
+ const float g0 = 1.0f - 0.25f * IntegerPow(kPhaseModulationDepth, 2);
+ const float g1 = 0.5f * kPhaseModulationDepth -
+ 0.0625f * IntegerPow(kPhaseModulationDepth, 3);
+ const float g2 = 0.1250f * IntegerPow(kPhaseModulationDepth, 2);
+ std::vector<float> filter1_coefficients{g2, g1, g0, -g1, g2};
+ std::vector<float> filter2_coefficients{g2, -g1, g0, g1, g2};
+
+ const size_t filter_length =
+ filter1_coefficients.size() * kMaxGroupDelaySamples;
+ std::unique_ptr<AudioBuffer> decorrelation_filters(
+ new AudioBuffer(kNumStereoChannels, filter_length));
+ decorrelation_filters->Clear();
+
+ for (size_t coefficient = 0; coefficient < filter1_coefficients.size();
+ ++coefficient) {
+ (*decorrelation_filters)[0][coefficient * kMaxGroupDelaySamples] =
+ filter1_coefficients[coefficient];
+ (*decorrelation_filters)[1][coefficient * kMaxGroupDelaySamples] =
+ filter2_coefficients[coefficient];
+ }
+
+ return decorrelation_filters;
+}
+
+size_t GetNumReverbOctaveBands(int sampling_rate) {
+ DCHECK_GT(sampling_rate, 0);
+
+ const float max_band =
+ log2f(0.5f * static_cast<float>(sampling_rate) / kLowestOctaveBandHz);
+ return std::min(kNumReverbOctaveBands, static_cast<size_t>(roundf(max_band)));
+}
+
+size_t GetNumSamplesFromMilliseconds(float milliseconds, int sampling_rate) {
+ DCHECK_GE(milliseconds, 0.0f);
+ DCHECK_GT(sampling_rate, 0);
+ return static_cast<size_t>(milliseconds * kSecondsFromMilliseconds *
+ static_cast<float>(sampling_rate));
+}
+
+size_t CeilToMultipleOfFramesPerBuffer(size_t size, size_t frames_per_buffer) {
+ DCHECK_NE(frames_per_buffer, 0U);
+ const size_t remainder = size % frames_per_buffer;
+ return remainder == 0 ? std::max(size, frames_per_buffer)
+ : size + frames_per_buffer - remainder;
+}
+
+void GenerateHannWindow(bool full_window, size_t window_length,
+ AudioBuffer::Channel* buffer) {
+
+ DCHECK(buffer);
+ DCHECK_LE(window_length, buffer->size());
+ const float full_window_scaling_factor =
+ kTwoPi / (static_cast<float>(window_length) - 1.0f);
+ const float half_window_scaling_factor =
+ kTwoPi / (2.0f * static_cast<float>(window_length) - 1.0f);
+ const float scaling_factor =
+ (full_window) ? full_window_scaling_factor : half_window_scaling_factor;
+ for (size_t i = 0; i < window_length; ++i) {
+ (*buffer)[i] =
+ 0.5f * (1.0f - std::cos(scaling_factor * static_cast<float>(i)));
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/utils.h b/src/3rdparty/resonance-audio/resonance_audio/dsp/utils.h
new file mode 100644
index 000000000..ceb3060de
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/utils.h
@@ -0,0 +1,93 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_DSP_UTILS_H_
+#define RESONANCE_AUDIO_DSP_UTILS_H_
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Generates a gaussian white noise signal.
+//
+// @param mean The mean distribution parameter.
+// @param std_deviation The standard deviation distribution parameter.
+// @param seed A seed for the random generator.
+// @param noise_channel Buffer channel in which to store the noise.
+void GenerateGaussianNoise(float mean, float std_deviation, unsigned seed,
+ AudioBuffer::Channel* noise_channel);
+
+// Generates a gaussian white noise signal.
+//
+// @param min The lowest value in the distribution.
+// @param max The highest value in te distribution, must be > min.
+// @param seed A seed for the random generator.
+// @param noise_channel Buffer channel in which to store the noise.
+void GenerateUniformNoise(float min, float max, unsigned seed,
+ AudioBuffer::Channel* noise_channel);
+
+// Generates a band limited gaussian white noise signal, one octave band wide.
+//
+// @param center_frequency Center frequency of the given octave band in Hz.
+// @param sampling_rate System sampling rate in Hz.
+// @param seed A seed for the random generator.
+// @param noise_buffer Buffer in which to store the band limited noise.
+void GenerateBandLimitedGaussianNoise(float center_frequency, int sampling_rate,
+ unsigned seed, AudioBuffer* noise_buffer);
+
+// Genarates a pair of decorrelation filters (for use in low quality/high
+// effiency mode reverb).
+//
+// @param sampling_rate System sampling rate in Hz.
+// @return Buffer containing the stereo filters.
+std::unique_ptr<AudioBuffer> GenerateDecorrelationFilters(int sampling_rate);
+
+// Returns the number of octave bands necessary for the given |sampling_rate|.
+//
+// @param sampling_rate Sampling rate in Hertz.
+// @return Number of reverb octave bands.
+size_t GetNumReverbOctaveBands(int sampling_rate);
+
+// Converts the given |milliseconds| to number of samples with the given
+// |sampling_rate|. This method should *not* be used when more precise
+// (double-precission) value is desired.
+//
+// @param milliseconds Milliseconds in single-precission floating point.
+// @param sampling_rate Sampling rate in Hertz.
+// @return Number of samples.
+size_t GetNumSamplesFromMilliseconds(float milliseconds, int sampling_rate);
+
+// Ceils the given |size| to the next multiple of given |frames_per_buffer|.
+//
+// @param size Input size in frames.
+// @param frames_per_buffer Frames per buffer.
+// @return Ceiled size in frames.
+size_t CeilToMultipleOfFramesPerBuffer(size_t size, size_t frames_per_buffer);
+
+// Generates a Hann window (used for smooth onset and tapering of the generated
+// reverb response tails).
+//
+// @param full_window True to generate a full window, false to generate a half.
+// @param window_length Length of the window to be generated. Must be less than
+// or equal to the number of frames in the |buffer|.
+// @param buffer AudioBuffer::Channel to which the window is written, the number
+// of frames will be the length in samples of the generated Hann window.
+void GenerateHannWindow(bool full_window, size_t window_length,
+ AudioBuffer::Channel* buffer);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_DSP_UTILS_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/dsp/utils_test.cc b/src/3rdparty/resonance-audio/resonance_audio/dsp/utils_test.cc
new file mode 100644
index 000000000..dffe991ec
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/dsp/utils_test.cc
@@ -0,0 +1,194 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "dsp/utils.h"
+
+#include <cmath>
+#include <limits>
+#include <memory>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "dsp/fft_manager.h"
+#include "dsp/partitioned_fft_filter.h"
+#include "utils/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+const int kSamplingRate = 48000;
+
+const size_t kHalfHannWindowLength = 8;
+const float kExpectedHalfHannWindow[] = {0.0000000f, 0.04322727f, 0.1654347f,
+ 0.3454915f, 0.55226423f, 0.7500000f,
+ 0.9045085f, 0.98907380f};
+
+const size_t kHannWindowLength = 15;
+const float kExpectedHannWindow[] = {
+ 0.0000000f, 0.0495156f, 0.1882551f, 0.3887395f, 0.6112605f,
+ 0.8117449f, 0.9504844f, 1.0000000f, 0.9504844f, 0.8117449f,
+ 0.6112605f, 0.3887395f, 0.1882551f, 0.0495156f, 0.0000000f};
+
+// Test that the noise generation functions create noise vectors with the
+// expected means etc.
+TEST(DspUtilsTest, NoiseTest) {
+ const size_t kNoiseBufferLength = 1e5;
+ // A high epsilon is used to determine if mean, std dev etc. are acurate as it
+ // would be infeasible to have enough samples to take advantage of the central
+ // limit theorem.
+ const float kEpsilon = 1e-1f;
+
+ AudioBuffer gaussian(kNumMonoChannels, kNoiseBufferLength);
+ AudioBuffer uniform(kNumMonoChannels, kNoiseBufferLength);
+
+ // Generate Gaussian Noise with mean 0 and std deviation 2.
+ const float kGaussianMean = 0.0f;
+ const float kStdDeviation = 2.0f;
+ GenerateGaussianNoise(kGaussianMean, kStdDeviation, /*seed=*/1U,
+ &gaussian[0]);
+
+ // Calculate the mean and compare to that specified.
+ float mean = 0.0f;
+ for (auto& sample : gaussian[0]) {
+ mean += sample;
+ }
+ mean /= static_cast<float>(gaussian.num_frames());
+ EXPECT_NEAR(mean, kGaussianMean, kEpsilon);
+
+ // Calculate the std deviation and compare to that specified.
+ float std_dev = 0.0f;
+ for (auto& sample : gaussian[0]) {
+ std_dev += (sample - mean) * (sample - mean);
+ }
+ std_dev /= static_cast<float>(gaussian.num_frames());
+ std_dev = std::sqrt(std_dev);
+ EXPECT_NEAR(std_dev, kStdDeviation, kEpsilon);
+
+ // Genarate uniformly distributed noise min -1, max 1 and thus mean 0.
+ const float kMin = -1.0f;
+ const float kMax = 1.0f;
+ GenerateUniformNoise(kMin, kMax, /*seed=*/1U, &uniform[0]);
+ // Calculate the mean and min/max values, compare to expected values.
+ mean = 0.0f;
+ float min = std::numeric_limits<float>::max();
+ float max = std::numeric_limits<float>::min();
+ for (auto& sample : uniform[0]) {
+ mean += sample;
+ min = sample < min ? sample : min;
+ max = sample > max ? sample : max;
+ }
+ mean /= static_cast<float>(uniform.num_frames());
+ EXPECT_NEAR(mean, (kMax + kMin) / 2.0f, kEpsilon);
+ EXPECT_GE(kMax, max);
+ EXPECT_LE(kMin, min);
+}
+
+// Tests that the ceiled input size in frames matches the expected multiple of
+// frames per buffer for arbitrary inputs.
+TEST(DspUtilsTest, CeilToMultipleOfFramesPerBufferTest) {
+ const size_t kFramesPerBuffer = 512;
+ const std::vector<size_t> kInput = {0, 100, 512, 1000, 5000, 10240};
+ const std::vector<size_t> kExpectedOutput = {512, 512, 512,
+ 1024, 5120, 10240};
+
+ for (size_t i = 0; i < kInput.size(); ++i) {
+ EXPECT_EQ(kExpectedOutput[i],
+ CeilToMultipleOfFramesPerBuffer(kInput[i], kFramesPerBuffer));
+ }
+}
+
+// Tests that on filtering a noise sample with a pair of decorrelation filters,
+// the correlation between those outputs is less than the result of an
+// autocorrelation.
+TEST(DspUtilsTest, GenerateDecorrelationFiltersTest) {
+ // Size of FFT to be used in |GenerateDecorrelationFiltersTest|.
+ const size_t kBufferSize = 512;
+ // Centre frequency for noise used in |GenerateDecorrelationFiltersTest|.
+ const float kNoiseCenter = 1000.0f;
+ std::unique_ptr<AudioBuffer> kernels =
+ GenerateDecorrelationFilters(kSamplingRate);
+ AudioBuffer noise(kNumMonoChannels, kBufferSize);
+ GenerateBandLimitedGaussianNoise(kNoiseCenter, kSamplingRate, /*seed=*/1U,
+ &noise);
+ FftManager fft_manager(kBufferSize);
+ PartitionedFftFilter fft_filter(kernels->num_frames(), kBufferSize,
+ &fft_manager);
+ fft_filter.SetTimeDomainKernel((*kernels)[0]);
+ AudioBuffer output_one(kNumMonoChannels, kBufferSize);
+
+ PartitionedFftFilter::FreqDomainBuffer freq_domain_buffer(kNumMonoChannels,
+ kBufferSize * 2);
+ fft_manager.FreqFromTimeDomain(noise[0], &freq_domain_buffer[0]);
+ fft_filter.Filter(freq_domain_buffer[0]);
+ fft_filter.GetFilteredSignal(&output_one[0]);
+ fft_filter.SetTimeDomainKernel((*kernels)[1]);
+ AudioBuffer output_two(kNumMonoChannels, kBufferSize);
+ fft_manager.FreqFromTimeDomain(noise[0], &freq_domain_buffer[0]);
+ fft_filter.Filter(freq_domain_buffer[0]);
+ fft_filter.GetFilteredSignal(&output_two[0]);
+ const float auto_correlation = MaxCrossCorrelation(noise[0], noise[0]);
+ const float decorrelated = MaxCrossCorrelation(output_one[0], output_two[0]);
+ EXPECT_LT(decorrelated, auto_correlation);
+}
+
+// Tests half-Hann window calculation against values returned by MATLAB's hann()
+// function.
+TEST(DspUtilsTest, GenerateHalfHannWindowTest) {
+ AudioBuffer half_hann_window(kNumMonoChannels, kHalfHannWindowLength);
+ GenerateHannWindow(false, kHalfHannWindowLength, &half_hann_window[0]);
+ for (size_t i = 0; i < kHalfHannWindowLength; ++i) {
+ EXPECT_NEAR(half_hann_window[0][i], kExpectedHalfHannWindow[i],
+ kEpsilonFloat);
+ }
+}
+
+// Tests Hann window generation for odd window lengths.
+TEST(DspUtilsTest, GenerateHannWindowOddLengthTest) {
+ AudioBuffer hann_window(kNumMonoChannels, kHannWindowLength);
+ GenerateHannWindow(true, kHannWindowLength, &hann_window[0]);
+ for (size_t i = 0; i < kHannWindowLength; ++i) {
+ EXPECT_NEAR(hann_window[0][i], kExpectedHannWindow[i], kEpsilonFloat);
+ }
+}
+
+// Tests that the calculated number of reverb octave bands matches the
+// pre-computed results with arbitrary sampling rates.
+TEST(DspUtilsTest, GetNumReverbOctaveBandsTest) {
+ const std::vector<int> kSamplingRates = {8000, 22050, 44100, 48000, 96000};
+ const std::vector<size_t> kExpectedOutput = {7, 8, 9, 9, 9};
+
+ for (size_t i = 0; i < kSamplingRates.size(); ++i) {
+ EXPECT_EQ(kExpectedOutput[i], GetNumReverbOctaveBands(kSamplingRates[i]));
+ }
+}
+
+// Tests that the calculated number of samples for arbitrary milliseconds values
+// matches the pre-computed results with a specific sampling rate.
+TEST(DspUtilsTest, GetNumSamplesFromMillisecondsTest) {
+ const std::vector<float> kInput = {0.0f, 2.5f, 50.0f, 123.45f, 1000.0f};
+ const std::vector<size_t> kExpectedOutput = {0, 120, 2400, 5925, 48000};
+
+ for (size_t i = 0; i < kInput.size(); ++i) {
+ EXPECT_EQ(kExpectedOutput[i],
+ GetNumSamplesFromMilliseconds(kInput[i], kSamplingRate));
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_listener.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_listener.h
new file mode 100644
index 000000000..7ae5c7a3f
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_listener.h
@@ -0,0 +1,57 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_ACOUSTIC_LISTENER_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_ACOUSTIC_LISTENER_H_
+
+#include <array>
+#include <cstddef>
+#include <vector>
+
+#include "Eigen/Core"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+struct AcousticListener {
+ // Constructor.
+ //
+ // @param listener_position Position of the listener.
+ // @param impulse_response_length Number of samples in the energy impulse
+ // response for each frequency band.
+ AcousticListener(const Eigen::Vector3f& listener_position,
+ size_t impulse_response_length)
+ : position(listener_position) {
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ energy_impulse_responses[i] =
+ std::vector<float>(impulse_response_length, 0.0f);
+ }
+ }
+
+ const Eigen::Vector3f position;
+
+ // Impulse responses in terms of energies for all frequency bands.
+ // Need to take square roots before applying it to an input signal, which
+ // models pressure. The reason we store response in energy instead of in
+ // pressure is because the geometrical acoustics computation is energy-based,
+ // and the following kind of operations are performed extensively:
+ // energy_impulse_responses[i][t] += <energy contribution from a ray>.
+ std::array<std::vector<float>, kNumReverbOctaveBands>
+ energy_impulse_responses;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_ACOUSTIC_LISTENER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_ray.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_ray.cc
new file mode 100644
index 000000000..6385f639b
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_ray.cc
@@ -0,0 +1,27 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/acoustic_ray.h"
+
+#include <limits>
+
+namespace vraudio {
+
+// Static member initialization.
+const float AcousticRay::kInfinity = std::numeric_limits<float>::infinity();
+const float AcousticRay::kRayEpsilon = 1e-4f;
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_ray.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_ray.h
new file mode 100644
index 000000000..ffe910270
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_ray.h
@@ -0,0 +1,195 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_ACOUSTIC_RAY_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_ACOUSTIC_RAY_H_
+
+#include <array>
+#include <vector>
+
+#include "embree2/rtcore.h"
+#include "embree2/rtcore_ray.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+// A class extending Embree's RTCRay (https://embree.github.io/api.html) with
+// data needed for acoustic computations.
+// It exposes useful fields through accessors.
+class RTCORE_ALIGN(16) AcousticRay : public RTCRay {
+ public:
+ enum class RayType {
+ kSpecular,
+ kDiffuse,
+ };
+
+ // A constant used to indicate that the ray extends to infinity, if
+ // ray.t_far() == AcousticRay::kInfinity.
+ static const float kInfinity;
+
+ // Used to offset a ray's origin slightly so that it will not
+ // intersect with the same geometry/primitive that it was generated from
+ // (by reflection, transmission, diffraction, etc.).
+ static const float kRayEpsilon;
+
+ // Default constructor. Constructs a ray whose origin is at (0, 0, 0) and
+ // points in the +x direction.
+ AcousticRay()
+ : energies_(), type_(RayType::kSpecular), prior_distance_(0.0f) {
+ org[0] = 0.0f;
+ org[1] = 0.0f;
+ org[2] = 0.0f;
+ dir[0] = 1.0f;
+ dir[1] = 0.0f;
+ dir[2] = 0.0f;
+ tnear = 0.0f;
+ tfar = kInfinity;
+ Ng[0] = 0.0f;
+ Ng[1] = 0.0f;
+ Ng[2] = 0.0f;
+ geomID = RTC_INVALID_GEOMETRY_ID;
+
+ // Members in RTCRay that we do not use (or whose initial values we do not
+ // care) are not initialized:
+ // align0, align1, align2, time, mask, u, v, primID, instID.
+ }
+
+ // Constructor.
+ //
+ // @param origin Origin of the ray.
+ // @param direction Direction of the ray.
+ // @param t_near Ray parameter corresponding to the start of the ray.
+ // @param t_far Ray parameter corresponding to the end of the ray. Pass in
+ // AcousticRay::kInfinity if there is no end point.
+ // @param energies Ray energies for all frequency bands.
+ // @param ray_type Type of ray.
+ // @param prior_distance Distance traveled before this ray.
+ AcousticRay(const float origin[3], const float direction[3], float t_near,
+ float t_far,
+ const std::array<float, kNumReverbOctaveBands>& energies,
+ RayType ray_type, float prior_distance)
+ : energies_(energies), type_(ray_type), prior_distance_(prior_distance) {
+ org[0] = origin[0];
+ org[1] = origin[1];
+ org[2] = origin[2];
+ dir[0] = direction[0];
+ dir[1] = direction[1];
+ dir[2] = direction[2];
+ tnear = t_near;
+ tfar = t_far;
+ Ng[0] = 0.0f;
+ Ng[1] = 0.0f;
+ Ng[2] = 0.0f;
+ geomID = RTC_INVALID_GEOMETRY_ID;
+
+ // Members in RTCRay that we do not use (or whose initial values we do not
+ // care) are not initialized:
+ // align0, align1, align2, time, mask, u, v, primID, instID.
+ }
+
+ // Ray origin.
+ const float* origin() const { return org; }
+ void set_origin(const float origin[3]) {
+ org[0] = origin[0];
+ org[1] = origin[1];
+ org[2] = origin[2];
+ }
+
+ // Ray direction.
+ const float* direction() const { return dir; }
+ void set_direction(const float direction[3]) {
+ dir[0] = direction[0];
+ dir[1] = direction[1];
+ dir[2] = direction[2];
+ }
+
+ // Ray parameter t corresponding to the start of the ray segment.
+ const float t_near() const { return tnear; }
+ void set_t_near(float t_near) { tnear = t_near; }
+
+ // Ray parameter t corresponding to the end of the ray segment.
+ const float t_far() const { return tfar; }
+ void set_t_far(float t_far) { tfar = t_far; }
+
+ // Functions intersected_*() will only return meaningful results after
+ // Intersect() is called, otherwise they return default values as
+ // described below.
+ //
+ // Not normalized geometry normal at the intersection point.
+ // Default value: Vec3fa(0, 0, 0).
+ const float* intersected_geometry_normal() const { return Ng; }
+ void set_intersected_geometry_normal(
+ const float intersected_geometry_normal[3]) {
+ Ng[0] = intersected_geometry_normal[0];
+ Ng[1] = intersected_geometry_normal[1];
+ Ng[2] = intersected_geometry_normal[2];
+ }
+
+ // Id of the intersected geometry.
+ // Default value: kInvalidGeometryId.
+ const unsigned int intersected_geometry_id() const { return geomID; }
+
+ // Id of the intersected primitive.
+ // Default value: kInvalidPrimitiveId.
+ const unsigned int intersected_primitive_id() const { return primID; }
+
+ // Ray energies for all frequency bands.
+ const std::array<float, kNumReverbOctaveBands>& energies() const {
+ return energies_;
+ }
+ void set_energies(const std::array<float, kNumReverbOctaveBands>& energies) {
+ energies_ = energies;
+ }
+
+ // Ray type.
+ const RayType type() const { return type_; }
+ void set_type(const RayType type) { type_ = type; }
+
+ // Prior distance.
+ const float prior_distance() const { return prior_distance_; }
+ void set_prior_distance(float prior_distance) {
+ prior_distance_ = prior_distance;
+ }
+
+ // Finds the first intersection between this ray and a scene. Some fields
+ // will be filled/mutated, which can be examined by the following functions:
+ // - t_far()
+ // - intersected_geometry_normal()
+ // - intersected_geometry_id()
+ // - intersected_primitive_id()
+ //
+ // @param scene An RTCScene to test the intersection.
+ // @return True if an intersection is found.
+ bool Intersect(RTCScene scene) {
+ rtcIntersect(scene, *this);
+ return geomID != RTC_INVALID_GEOMETRY_ID;
+ }
+
+ private:
+ // Used to determine early-termination of rays. May also be used to model
+ // source strength.
+ std::array<float, kNumReverbOctaveBands> energies_;
+
+ // Ray type.
+ RayType type_ = RayType::kSpecular;
+
+ // Accumulated distance traveled on the same path before this ray starts.
+ float prior_distance_ = 0.0f;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_ACOUSTIC_RAY_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_ray_test.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_ray_test.cc
new file mode 100644
index 000000000..29ef269ed
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_ray_test.cc
@@ -0,0 +1,243 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/acoustic_ray.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/logging.h"
+#include "geometrical_acoustics/test_util.h"
+
+namespace vraudio {
+
+namespace {
+const float kFloatErrorTolerance = 5e-7f;
+
+class AcousticRayTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ // Use a single RTCDevice for all tests.
+ static RTCDevice device = rtcNewDevice(nullptr);
+ CHECK_NOTNULL(device);
+ scene_ = rtcDeviceNewScene(
+ device, RTC_SCENE_STATIC | RTC_SCENE_HIGH_QUALITY, RTC_INTERSECT1);
+ }
+
+ void TearDown() override { rtcDeleteScene(scene_); }
+
+ // Normalizes the vector in place.
+ void NormalizeVector(float* vector) {
+ const float norm = std::sqrt(vector[0] * vector[0] + vector[1] * vector[1] +
+ vector[2] * vector[2]);
+ ASSERT_GT(norm, 0.0f);
+ vector[0] /= norm;
+ vector[1] /= norm;
+ vector[2] /= norm;
+ }
+
+ const std::array<float, kNumReverbOctaveBands> kZeroEnergies{
+ {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}};
+ RTCScene scene_ = nullptr;
+};
+
+TEST_F(AcousticRayTest, DefaultConstructorTest) {
+ AcousticRay ray;
+ const float default_origin[3] = {0.0f, 0.0f, 0.0f};
+ const float default_direction[3] = {1.0f, 0.0f, 0.0f};
+ ExpectFloat3Close(ray.origin(), default_origin);
+ ExpectFloat3Close(ray.direction(), default_direction);
+ EXPECT_FLOAT_EQ(ray.t_near(), 0.0f);
+ EXPECT_FLOAT_EQ(ray.t_far(), AcousticRay::kInfinity);
+ for (const float energy : ray.energies()) {
+ EXPECT_FLOAT_EQ(energy, 0.0f);
+ }
+ EXPECT_EQ(ray.type(), AcousticRay::RayType::kSpecular);
+ EXPECT_FLOAT_EQ(ray.prior_distance(), 0.0f);
+}
+
+TEST_F(AcousticRayTest, AccessorsTest) {
+ const float origin[3] = {0.0f, 0.0f, 0.25f};
+ const float direction[3] = {0.0f, 0.0f, 1.0f};
+
+ const float t_near = 0.0f;
+ const float t_far = AcousticRay::kInfinity;
+ const std::array<float, kNumReverbOctaveBands> energies = {
+ {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f}};
+ const AcousticRay::RayType ray_type = AcousticRay::RayType::kDiffuse;
+ const float prior_distance = 0.0f;
+ AcousticRay ray(origin, direction, t_near, t_far, energies, ray_type,
+ prior_distance);
+
+ // Validate fields passed to the constructor.
+ ExpectFloat3Close(ray.origin(), origin);
+ ExpectFloat3Close(ray.direction(), direction);
+ EXPECT_FLOAT_EQ(ray.t_near(), t_near);
+ EXPECT_FLOAT_EQ(ray.t_far(), t_far);
+ for (size_t i = 0; i < energies.size(); ++i) {
+ EXPECT_FLOAT_EQ(ray.energies().at(i), energies.at(i));
+ }
+ EXPECT_EQ(ray.type(), ray_type);
+ EXPECT_FLOAT_EQ(ray.prior_distance(), prior_distance);
+
+ // Validate explicit setters.
+ const float new_origin[3] = {1.0f, 2.0f, 3.0f};
+ ray.set_origin(new_origin);
+ ExpectFloat3Close(ray.origin(), new_origin);
+
+ const float new_direction[3] = {0.0f, 1.0f, 0.0f};
+ ray.set_direction(new_direction);
+ ExpectFloat3Close(ray.direction(), new_direction);
+
+ const float new_t_near = 2.0f;
+ ray.set_t_near(new_t_near);
+ EXPECT_FLOAT_EQ(ray.t_near(), new_t_near);
+
+ const float new_t_far = 4.0f;
+ ray.set_t_far(new_t_far);
+ EXPECT_FLOAT_EQ(ray.t_far(), new_t_far);
+
+ const float intersected_geometry_normal[3] = {0.0f, 1.0f, 2.0f};
+ ray.set_intersected_geometry_normal(intersected_geometry_normal);
+ ExpectFloat3Close(ray.intersected_geometry_normal(),
+ intersected_geometry_normal);
+
+ const std::array<float, kNumReverbOctaveBands> new_energies = {
+ {2.0f, 4.0f, 6.0f, 8.0f, 10.0f, 12.0f, 14.0f, 16.0f, 18.0f}};
+ ray.set_energies(new_energies);
+ for (size_t i = 0; i < energies.size(); ++i) {
+ EXPECT_FLOAT_EQ(ray.energies().at(i), new_energies.at(i));
+ }
+
+ const AcousticRay::RayType new_ray_type = AcousticRay::RayType::kSpecular;
+ ray.set_type(new_ray_type);
+ EXPECT_EQ(ray.type(), new_ray_type);
+
+ const float new_prior_distance = 7.0f;
+ ray.set_prior_distance(new_prior_distance);
+ EXPECT_FLOAT_EQ(ray.prior_distance(), new_prior_distance);
+}
+
+TEST_F(AcousticRayTest, IntersectTriangleInGroundSceneTest) {
+ // Add a ground to the scene and commit.
+ AddTestGround(scene_);
+ rtcCommit(scene_);
+
+ const float t_near = 0.0f;
+ const float t_far = AcousticRay::kInfinity;
+ const float prior_distance = 0.0f;
+ {
+ // This ray should intersect the ground geometry (id: 0) and the first
+ // triangle (id: 0).
+ const float origin[3] = {0.0f, 0.0f, 0.25f};
+ float direction[3] = {1.0f, 1.0f, -1.0f};
+ NormalizeVector(direction);
+ AcousticRay ray(origin, direction, t_near, t_far, kZeroEnergies,
+ AcousticRay::RayType::kSpecular, prior_distance);
+ EXPECT_TRUE(ray.Intersect(scene_));
+ EXPECT_EQ(ray.intersected_geometry_id(), 0u);
+ EXPECT_EQ(ray.intersected_primitive_id(), 0u);
+
+ // Check the intersection point.
+ EXPECT_NEAR(0.4330127f, ray.t_far(), 1e-7);
+ float intersection_point[3];
+ intersection_point[0] = ray.origin()[0] + ray.t_far() * ray.direction()[0];
+ intersection_point[1] = ray.origin()[1] + ray.t_far() * ray.direction()[1];
+ intersection_point[2] = ray.origin()[2] + ray.t_far() * ray.direction()[2];
+
+ const float expected_intersection_point[3] = {0.25f, 0.25f, 0.0f};
+ ExpectFloat3Close(intersection_point, expected_intersection_point,
+ kFloatErrorTolerance);
+
+ // Normal at the intersection point.
+ float normal[3];
+ normal[0] = ray.intersected_geometry_normal()[0];
+ normal[1] = ray.intersected_geometry_normal()[1];
+ normal[2] = ray.intersected_geometry_normal()[2];
+ NormalizeVector(normal);
+ const float expected_normal[3] = {0.0f, 0.0f, 1.0f};
+ ExpectFloat3Close(normal, expected_normal, kFloatErrorTolerance);
+ }
+ {
+ // This ray should intersect the ground geometry (id: 0) and the second
+ // triangle (id: 1).
+ const float origin[3] = {0.0f, 0.0f, 0.75f};
+ const float direction[3] = {1.0f, 1.0f, -1.0f};
+ AcousticRay ray(origin, direction, t_near, t_far, kZeroEnergies,
+ AcousticRay::RayType::kSpecular, prior_distance);
+ EXPECT_TRUE(ray.Intersect(scene_));
+ EXPECT_EQ(ray.intersected_geometry_id(), 0u);
+ EXPECT_EQ(ray.intersected_primitive_id(), 1u);
+ }
+ {
+ // This ray shoots upward (away from the ground) and therefore should not
+ // intersect anything.
+ const float origin[3] = {0.0f, 0.0f, 0.25};
+ const float direction[3] = {1.0f, 1.0f, 1.0f};
+ AcousticRay ray(origin, direction, t_near, t_far, kZeroEnergies,
+ AcousticRay::RayType::kSpecular, prior_distance);
+ EXPECT_FALSE(ray.Intersect(scene_));
+ }
+}
+
+TEST_F(AcousticRayTest, IntersectNothingBackfaceTest) {
+ // Add a ground to the scene and commit.
+ AddTestGround(scene_);
+ rtcCommit(scene_);
+
+ // This ray is on the "back side" of the ground. So even if the ray passes
+ // through a triangle, the intersection should not be reported.
+ const float origin[3] = {0.0f, 0.0f, -0.25f};
+ const float direction[3] = {1.0f, 1.0f, 1.0f};
+ const float t_near = 0.0f;
+ const float t_far = AcousticRay::kInfinity;
+ const float prior_distance = 0.0f;
+ AcousticRay ray(origin, direction, t_near, t_far, kZeroEnergies,
+ AcousticRay::RayType::kSpecular, prior_distance);
+ EXPECT_FALSE(ray.Intersect(scene_));
+}
+
+TEST_F(AcousticRayTest, IntersectNothingInEmptySceneTest) {
+ // Commit without adding any geometry.
+ rtcCommit(scene_);
+
+ // This ray should not intersect anything.
+ const float origin[3] = {0.0f, 0.0f, 0.25f};
+ const float direction[3] = {1.0f, 1.0f, -1.0f};
+ const float t_near = 0.0f;
+ const float t_far = AcousticRay::kInfinity;
+ const float prior_distance = 0.0f;
+ AcousticRay ray(origin, direction, t_near, t_far, kZeroEnergies,
+ AcousticRay::RayType::kSpecular, prior_distance);
+ EXPECT_FALSE(ray.Intersect(scene_));
+}
+
+TEST_F(AcousticRayTest, IntersectNothingInUnCommitedSceneTest) {
+ // Add a ground to the scene but forget to commit.
+ AddTestGround(scene_);
+
+ // This ray should not intersect anything.
+ const float origin[3] = {0.0f, 0.0f, 0.25f};
+ const float direction[3] = {1.0f, 1.0f, -1.0f};
+ const float t_near = 0.0f;
+ const float t_far = AcousticRay::kInfinity;
+ const float prior_distance = 0.0f;
+ AcousticRay ray(origin, direction, t_near, t_far, kZeroEnergies,
+ AcousticRay::RayType::kSpecular, prior_distance);
+ EXPECT_FALSE(ray.Intersect(scene_));
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_source.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_source.h
new file mode 100644
index 000000000..b119df76b
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_source.h
@@ -0,0 +1,111 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_ACOUSTIC_SOURCE_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_ACOUSTIC_SOURCE_H_
+
+#include <array>
+#include <functional>
+#include <utility>
+#include <vector>
+
+#include "Eigen/Core"
+#include "base/constants_and_types.h"
+#include "geometrical_acoustics/acoustic_ray.h"
+#include "geometrical_acoustics/sampling.h"
+
+namespace vraudio {
+
+// A class modeling a sound source. Currently we only support point source
+// that emits rays uniformly in all directions.
+class AcousticSource {
+ public:
+ // Constructor.
+ //
+ // @param position Source position.
+ // @param energies Source energies. This will be the initial energies for
+ // all frequency bands shared by all rays generated from this source.
+ // @param random_number_generator Random number generator used to sample
+ // ray directions. It should implement operator() that returns a
+ // random value in [0.0, 1.0).
+ AcousticSource(const Eigen::Vector3f& position,
+ const std::array<float, kNumReverbOctaveBands>& energies,
+ std::function<float()> random_number_generator)
+ : position_(position),
+ energies_(energies),
+ random_number_generator_(std::move(random_number_generator)) {}
+
+ // Generates one ray.
+ //
+ // @return AcousticRay starting from this source.
+ AcousticRay GenerateRay() const {
+ const Eigen::Vector3f& direction = UniformSampleSphere(
+ random_number_generator_(), random_number_generator_());
+ return AcousticRay(position_.data(), direction.data(), 0.0f,
+ AcousticRay::kInfinity, energies_,
+ AcousticRay::RayType::kSpecular, 0.0f);
+ }
+
+ // Generates a vector of rays at once, using stratified sampling. The rays
+ // generated this way are more "evenly spaced", with fewer holes and clusters.
+ //
+ // One caveat: only the whole set of the returned rays is uniformly
+ // distributed (the expected number of rays found in a finite solid angle
+ // is the same in any direction), while any subset with fewer than
+ // |num_rays| rays is not.
+ //
+ // In contrast, the GenerateRay() above guarantees any subset is a uniformly
+ // distributed set of rays. This is why the function is designed to return
+ // a vector of rays, which are meant to be used as a whole and not partially.
+ //
+ // @param num_rays Number of rays; must be equal to |sqrt_num_rays|^2.
+ // @param sqrt_num_rays The square root of number of rays to emit.
+ // @return A vector of |sqrt_num_rays|^2 AcousticRays starting from this
+ // source.
+ std::vector<AcousticRay> GenerateStratifiedRays(size_t num_rays,
+ size_t sqrt_num_rays) const {
+ // To save computation time, it is the caller's responsibility to make sure
+ // that |num_rays| is equal to |sqrt_num_rays|^2.
+ DCHECK_EQ(sqrt_num_rays * sqrt_num_rays, num_rays);
+
+ std::vector<AcousticRay> rays;
+ rays.reserve(num_rays);
+ for (size_t ray_index = 0; ray_index < num_rays; ++ray_index) {
+ const Eigen::Vector3f& direction = StratifiedSampleSphere(
+ random_number_generator_(), random_number_generator_(), sqrt_num_rays,
+ ray_index);
+ rays.push_back(AcousticRay(position_.data(), direction.data(),
+ 0.0f /* t_near */, AcousticRay::kInfinity,
+ energies_, AcousticRay::RayType::kSpecular,
+ 0.0f /* prior_distance */));
+ }
+ return rays;
+ }
+
+ private:
+ // The position of this point source.
+ const Eigen::Vector3f position_;
+
+ // The energy of this source.
+ const std::array<float, kNumReverbOctaveBands> energies_;
+
+ // Randon number generator for sampling ray directions.
+ std::function<float()> random_number_generator_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_ACOUSTIC_SOURCE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_source_test.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_source_test.cc
new file mode 100644
index 000000000..aa05a8112
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/acoustic_source_test.cc
@@ -0,0 +1,136 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/acoustic_source.h"
+
+#include <cmath>
+#include <random>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "geometrical_acoustics/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+TEST(AcousticSource, GeneratedRayNonDirectionalDataTest) {
+ std::default_random_engine engine(0);
+ std::uniform_real_distribution<float> distribution(0.0f, 1.0f);
+ const std::array<float, kNumReverbOctaveBands> energies{
+ {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f}};
+ AcousticSource source({0.1f, 0.2f, 0.3f}, energies, [&engine, &distribution] {
+ return distribution(engine);
+ });
+ AcousticRay ray = source.GenerateRay();
+
+ EXPECT_EQ(ray.type(), AcousticRay::RayType::kSpecular);
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ EXPECT_FLOAT_EQ(ray.energies().at(i), energies.at(i));
+ }
+ EXPECT_FLOAT_EQ(ray.t_near(), 0.0f);
+ const float expected_origin[3] = {0.1f, 0.2f, 0.3f};
+ ExpectFloat3Close(ray.origin(), expected_origin);
+}
+
+TEST(AcousticSource, GeneratedRayDirectionDistributionTest) {
+ std::default_random_engine engine(0);
+ std::uniform_real_distribution<float> distribution(0.0f, 1.0f);
+ const std::array<float, kNumReverbOctaveBands> energies{
+ {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}};
+ AcousticSource source({0.0f, 0.0f, 0.0f}, energies, [&engine, &distribution] {
+ return distribution(engine);
+ });
+
+ // Check that the direction vectors on generated rays are all normalized,
+ // i.e., of unit lengths.
+ for (size_t i = 0; i < 1000; ++i) {
+ AcousticRay ray = source.GenerateRay();
+ const float direction_squared_norm =
+ ray.direction()[0] * ray.direction()[0] +
+ ray.direction()[1] * ray.direction()[1] +
+ ray.direction()[2] * ray.direction()[2];
+ EXPECT_FLOAT_EQ(direction_squared_norm, 1.0f);
+ }
+
+ // For a ray whose direction is uniformly distributed over a sphere:
+ // - The PDF of theta is sin(theta), and the CDF is (1 - cos(theta)) / 2.
+ // - The PDF of phi is 1 / 2 pi, and the CDF is 0.5 + phi / 2 pi.
+ ValidateDistribution(100000, 100, [&source]() {
+ AcousticRay ray = source.GenerateRay();
+ const float cos_theta = ray.direction()[2];
+ return 0.5f * (1.0f - cos_theta);
+ });
+
+ ValidateDistribution(100000, 100, [&source]() {
+ AcousticRay ray = source.GenerateRay();
+ const float* direction = ray.direction();
+ const float phi = std::atan2(direction[1], direction[0]);
+ return 0.5f + phi / 2.0f / static_cast<float>(M_PI);
+ });
+}
+
+// Similar to GeneratedRayDirectionDistributionTest, but tests on the whole
+// set of rays generated by AcousticSource::GenerateStratifiedRays() that they
+// are uniformly distributed.
+TEST(AcousticSource, GeneratedStratifiedRaysDirectionDistributionTest) {
+ std::default_random_engine engine(0);
+ std::uniform_real_distribution<float> distribution(0.0f, 1.0f);
+ const std::array<float, kNumReverbOctaveBands> energies{
+ {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}};
+ AcousticSource source({0.0f, 0.0f, 0.0f}, energies, [&engine, &distribution] {
+ return distribution(engine);
+ });
+
+ // Check that the direction vectors on generated rays are all normalized,
+ // i.e., of unit lengths.
+ const size_t num_rays = 10000;
+ const size_t sqrt_num_rays = 100;
+ std::vector<AcousticRay> rays =
+ source.GenerateStratifiedRays(num_rays, sqrt_num_rays);
+ for (const AcousticRay& ray : rays) {
+ const float direction_squared_norm =
+ ray.direction()[0] * ray.direction()[0] +
+ ray.direction()[1] * ray.direction()[1] +
+ ray.direction()[2] * ray.direction()[2];
+ EXPECT_FLOAT_EQ(direction_squared_norm, 1.0f);
+ }
+
+ // For a ray whose direction is uniformly distributed over a sphere:
+ // - The PDF of theta is sin(theta), and the CDF is (1 - cos(theta)) / 2.
+ // - The PDF of phi is 1 / 2 pi, and the CDF is 0.5 + phi / 2 pi.
+ size_t ray_index = 0;
+ ValidateDistribution(num_rays, 100, [&rays, &ray_index]() {
+ const AcousticRay& ray = rays[ray_index];
+ ++ray_index;
+ const float cos_theta = ray.direction()[2];
+ return 0.5f * (1.0f - cos_theta);
+ });
+
+ ray_index = 0;
+ ValidateDistribution(num_rays, 100, [&rays, &ray_index]() {
+ const AcousticRay& ray = rays[ray_index];
+ ++ray_index;
+ const float* direction = ray.direction();
+ const float phi = std::atan2(direction[1], direction[0]);
+ return 0.5f + phi / kTwoPi;
+ });
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/collection_kernel.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/collection_kernel.cc
new file mode 100644
index 000000000..86b4888b2
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/collection_kernel.cc
@@ -0,0 +1,128 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/collection_kernel.h"
+
+#include <array>
+#include <cmath>
+#include <vector>
+
+#include "Eigen/Core"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+using Eigen::Vector3f;
+
+namespace {
+
+// Adds a response to the output |energy_impulse_responses| array at an index
+// computed based on |total_source_listener_distance| and
+// |distance_to_impulse_response_index|. The values to be added are |energies|
+// multiplied by |energy_factor|.
+void AddResponse(float total_source_listener_distance,
+ float distance_to_impulse_response_index, float energy_factor,
+ const std::array<float, kNumReverbOctaveBands>& energies,
+ std::array<std::vector<float>, kNumReverbOctaveBands>*
+ energy_impulse_responses) {
+ const size_t impulse_response_index = static_cast<size_t>(
+ total_source_listener_distance * distance_to_impulse_response_index);
+
+ // It is OK if |impulse_response_index| exceeds the size of the listener's
+ // impulse response array (e.g. if the user only cares about the first second
+ // of the response), in which case we simply discard the contribution.
+ if (impulse_response_index >= (*energy_impulse_responses)[0].size()) {
+ return;
+ }
+
+ for (size_t i = 0; i < energy_impulse_responses->size(); ++i) {
+ (*energy_impulse_responses)[i][impulse_response_index] +=
+ energy_factor * energies.at(i);
+ }
+}
+
+} // namespace
+
+// In our implementation, |sphere_size_energy_factor_| is defined such that a
+// listener 1.0 meter away from the source would give an attenuation of 1.0.
+// Therefore the value is computed by:
+// 4.0 * PI * 1.0^2 / (PI * R^2) = 4.0 / R^2.
+CollectionKernel::CollectionKernel(float listener_sphere_radius,
+ float sampling_rate)
+ : sphere_size_energy_factor_(4.0f / listener_sphere_radius /
+ listener_sphere_radius),
+ distance_to_impulse_response_index_(sampling_rate / kSpeedOfSound) {}
+
+void CollectionKernel::Collect(const AcousticRay& ray, float weight,
+ AcousticListener* listener) const {
+ CHECK_EQ(ray.energies().size(), listener->energy_impulse_responses.size());
+
+ // Collect the energy contribution to the listener's impulse response at the
+ // arrival time.
+ // The distance to listener on this ray is approximated by projecting
+ // (listener.position - sub_ray's starting point) onto the ray direction.
+ const Vector3f ray_direction(ray.direction());
+ const Vector3f ray_starting_point =
+ Vector3f(ray.origin()) + ray.t_near() * ray_direction;
+ const float distance_to_listener_on_ray =
+ (listener->position - ray_starting_point).dot(ray_direction.normalized());
+ AddResponse(ray.prior_distance() + distance_to_listener_on_ray,
+ distance_to_impulse_response_index_,
+ weight * sphere_size_energy_factor_, ray.energies(),
+ &listener->energy_impulse_responses);
+}
+
+// In a diffuse rain algorithm, instead of relying on the Monte Carlo process
+// to estimate expected energy gathered by the sphere, we directly multiply
+// the probability of a ray intersecting the sphere to the energy to be
+// collected, thus ensuring the expected gathered energies are the same (see
+// also [internal ref]
+//
+// <Energy by Monte Carlo process> = <Energy by diffuse rain>
+// sum_i (Prob[ray_i intersects sphere] * Energy_i) =
+// sum_i (factor_i * Energy_i)
+//
+// So factor_i = Prob[ray_i intersects sphere]
+// ~ PDF(ray_i in the direction pointing to the listener) *
+// (projected solid angle of the listener sphere)
+// ~ PDF * PI * R^2 / (4.0 * PI * D^2)
+// = PDF * R^2 / (4.0 * D^2),
+//
+// where PDF is the probability density function, R the radius of the
+// listener sphere, and D the distance between the listener and the
+// reflection point.
+//
+// Combining |factor_i| with |sphere_size_energy_factor_| = 4.0 / R^2,
+// the total energy factor that needs to be multiplied to the energies on a
+// diffuse-rain ray is therefore (PDF / D^2).
+void CollectionKernel::CollectDiffuseRain(const AcousticRay& diffuse_rain_ray,
+ float weight, float direction_pdf,
+ AcousticListener* listener) const {
+ // Since a diffuse-rain ray already connects to the listener, and its
+ // direction already normalized, the distance to listener is its
+ // t_far - t_near.
+ const float distance_to_listener_on_ray =
+ diffuse_rain_ray.t_far() - diffuse_rain_ray.t_near();
+ const float diffuse_rain_energy_factor =
+ direction_pdf / distance_to_listener_on_ray / distance_to_listener_on_ray;
+ AddResponse(diffuse_rain_ray.prior_distance() + distance_to_listener_on_ray,
+ distance_to_impulse_response_index_,
+ weight * diffuse_rain_energy_factor, diffuse_rain_ray.energies(),
+ &listener->energy_impulse_responses);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/collection_kernel.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/collection_kernel.h
new file mode 100644
index 000000000..970cc8dd4
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/collection_kernel.h
@@ -0,0 +1,79 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_COLLECTION_KERNEL_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_COLLECTION_KERNEL_H_
+
+#include "geometrical_acoustics/acoustic_listener.h"
+#include "geometrical_acoustics/acoustic_ray.h"
+
+namespace vraudio {
+
+// A class modeling the collection of energy contributions from an acoustic ray.
+class CollectionKernel {
+ public:
+ // Constructor.
+ //
+ // @param listener_sphere_radius Radius of listener spheres (m).
+ // @param sampling_rate Sampling rate (Hz). Together with speed of sound we
+ // can convert a physical time to an index in an impulse response array.
+ CollectionKernel(float listener_sphere_radius, float sampling_rate);
+
+ // Collects contributions from a ray to a listener's impulse response.
+ //
+ // @param ray AcousticRay whose contribution is to be collected.
+ // @param weight Weight of the contribution on |ray|.
+ // @param listener AcousticListener to which the contribution from |ray| is
+ // added in-place.
+ void Collect(const AcousticRay& ray, float weight,
+ AcousticListener* listener) const;
+
+ // Collects contributions from a diffuse-rain ray to a listener's impulse
+ // response.
+ //
+ // @param diffuse_rain_ray Diffuse-rain ray whose contribution is to be
+ // collected.
+ // @param weight Weight of the contribution on |diffuse_rain_ray|.
+ // @param direction_pdf The probability density function that a ray is in
+ // the direction of |diffuse_rain_ray|, with density taken over the solid
+ // angle. Used to scale the energy contributions. The unit is
+ // 1 / steradian.
+ // @param listener AcousticListener to which the contribution from |ray| is
+ // added in-place.
+ void CollectDiffuseRain(const AcousticRay& diffuse_rain_ray, float weight,
+ float direction_pdf,
+ AcousticListener* listener) const;
+
+ private:
+ // Because we use a sphere to collect rays, the number of rays (and therefore
+ // the amount of energy) collected is proportional to the cross section area
+ // of the sphere, i.e. energy ~ R^2, where R is the sphere radius. But
+ // because the energy at a listener position should be independent of the
+ // sphere radius (which is an artificial construct), we need to factor out
+ // the radius-dependency, by multiplying the sum of energy with a constant,
+ // |sphere_size_energy_factor_|.
+ //
+ // In our implementation, this constant is defined such that a listener
+ // 1.0 meter away from the source would give an attenuation of 1.0.
+ const float sphere_size_energy_factor_;
+
+ // Converting traveled distance in meter to an index in the impulse response
+ // array: index = distance (m) / speed_of_sound (m/s) * sampling_rate (1/s).
+ const float distance_to_impulse_response_index_;
+};
+
+} // namespace vraudio
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_COLLECTION_KERNEL_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/collection_kernel_test.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/collection_kernel_test.cc
new file mode 100644
index 000000000..369c55691
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/collection_kernel_test.cc
@@ -0,0 +1,353 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/collection_kernel.h"
+
+#include <cmath>
+#include <random>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "Eigen/Core"
+#include "base/constants_and_types.h"
+#include "geometrical_acoustics/acoustic_listener.h"
+#include "geometrical_acoustics/acoustic_ray.h"
+#include "geometrical_acoustics/acoustic_source.h"
+#include "geometrical_acoustics/sphere.h"
+#include "geometrical_acoustics/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+using Eigen::Vector3f;
+
+const float kSamplingRateHz = 1000.0f;
+const size_t kImpulseResponseNumSamples = 1000;
+
+class CollectionKernelTest : public testing::Test {
+ protected:
+ const std::array<float, kNumReverbOctaveBands> kUnitEnergies{
+ {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}};
+};
+
+TEST_F(CollectionKernelTest, CollectOneRayTest) {
+ // A listener at (5, 0, 0).
+ AcousticListener listener({5.0f, 0.0f, 0.0f}, kImpulseResponseNumSamples);
+
+ // One ray.
+ const Vector3f origin(0.0f, 0.0f, 0.0f);
+ const Vector3f direction(1.0f, 0.0f, 0.0f);
+ const float t_near = 0.0f;
+ const float t_far = 100.0f;
+ const float prior_distance = 0.0f;
+ const AcousticRay ray(origin.data(), direction.data(), t_near, t_far,
+ kUnitEnergies, AcousticRay::RayType::kSpecular,
+ prior_distance);
+
+ // Collect impulse response.
+ const float listener_sphere_radius = 0.1f;
+ CollectionKernel collection_kernel(
+ listener_sphere_radius, kSamplingRateHz);
+ collection_kernel.Collect(ray, 1.0f, &listener);
+
+ // Validate the impulse response.
+ // The theoretical index of the single non-zero element is:
+ // floor(distance (m) / kSpeedOfSound (m/s) * kSamplingRate (1/s)).
+ const float distance = (listener.position - origin).norm();
+ const size_t expected_index = static_cast<size_t>(
+ std::floor(distance / kSpeedOfSound * kSamplingRateHz));
+
+ // The theoretical energy value is energy * sphere_size_energy_factor.
+ const float expected_sphere_size_energy_factor =
+ 4.0f / listener_sphere_radius / listener_sphere_radius;
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ ValidateSparseFloatArray(
+ listener.energy_impulse_responses.at(i), {expected_index},
+ {kUnitEnergies.at(i) * expected_sphere_size_energy_factor},
+ kEpsilonFloat);
+ }
+}
+
+TEST_F(CollectionKernelTest, DiscardContributionTest) {
+ // A listener far away that it takes 2 seconds for the ray to arrive, which
+ // is longer than the impulse response of interest, i.e.
+ // kImpulseResponseNumSamples / kSamplingRateHz = 1 second.
+ // The contribution should be discarded.
+ AcousticListener listener({kSpeedOfSound * 2.0f, 0.0f, 0.0f},
+ kImpulseResponseNumSamples);
+
+ // One ray.
+ const Vector3f origin(0.0f, 0.0f, 0.0f);
+ const Vector3f direction(1.0f, 0.0f, 0.0f);
+ const float t_near = 0.0f;
+ const float t_far = 100.0f;
+ const float prior_distance = 0.0f;
+ const AcousticRay ray(origin.data(), direction.data(), t_near, t_far,
+ kUnitEnergies, AcousticRay::RayType::kSpecular,
+ prior_distance);
+
+ // Collect impulse response.
+ const float listener_sphere_radius = 0.1f;
+ CollectionKernel collection_kernel(
+ listener_sphere_radius, kSamplingRateHz);
+ collection_kernel.Collect(ray, 1.0f, &listener);
+
+ // Validate that the impulse responses are all zero.
+ for (const std::vector<float>& energy_impulse_response :
+ listener.energy_impulse_responses) {
+ ValidateSparseFloatArray(energy_impulse_response, {}, {}, kEpsilonFloat);
+ }
+}
+
+TEST_F(CollectionKernelTest, CollectOneRayWithPriorDistanceTest) {
+ // A listener at (5, 0, 0).
+ AcousticListener listener({5.0f, 0.0f, 0.0f}, kImpulseResponseNumSamples);
+
+ // One ray.
+ const Vector3f origin(0.0f, 0.0f, 0.0f);
+ const Vector3f direction(1.0f, 0.0f, 0.0f);
+ const float t_near = 0.0f;
+ const float t_far = 100.0f;
+ const float prior_distance = 10.0f;
+ const AcousticRay ray(origin.data(), direction.data(), t_near, t_far,
+ kUnitEnergies, AcousticRay::RayType::kSpecular,
+ prior_distance);
+
+ // Collect impulse response.
+ const float listener_sphere_radius = 0.1f;
+ CollectionKernel collection_kernel(
+ listener_sphere_radius, kSamplingRateHz);
+ collection_kernel.Collect(ray, 1.0f, &listener);
+
+ // Validate the impulse response.
+ // The theoretical index of the single non-zero element is:
+ // floor(distance (m) / kSpeedOfSound (m/s) * kSamplingRate (1/s)).
+ // In this test distance = prior_distance + <distance on this ray>.
+ const float distance = prior_distance + (listener.position - origin).norm();
+ const size_t expected_index = static_cast<size_t>(
+ std::floor(distance / kSpeedOfSound * kSamplingRateHz));
+
+ // The theoretical energy value is energy * sphere_size_energy_factor.
+ const float expected_sphere_size_energy_factor =
+ 4.0f / listener_sphere_radius / listener_sphere_radius;
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ ValidateSparseFloatArray(
+ listener.energy_impulse_responses.at(i), {expected_index},
+ {kUnitEnergies.at(i) * expected_sphere_size_energy_factor},
+ kEpsilonFloat);
+ }
+}
+
+TEST_F(CollectionKernelTest, SphereSizeEnergyFactorTest) {
+ // All listeners are at (0, 1, 0).
+ const Vector3f listener_position(0.0f, 1.0f, 0.0f);
+
+ // One ray.
+ const Vector3f origin(0.0f, 0.0f, 0.0f);
+ const Vector3f direction(0.0f, 1.0f, 0.0f);
+ const float t_near = 0.0f;
+ const float t_far = 100.0f;
+ const float prior_distance = 0.0f;
+ const AcousticRay ray(origin.data(), direction.data(), t_near, t_far,
+ kUnitEnergies, AcousticRay::RayType::kSpecular,
+ prior_distance);
+
+ // All listeners' impulse responses should have a non-zero element at index
+ // floor(distance (m) / kSpeedOfSound (m/s) * kSamplingRate (1/s)).
+ const float distance = (listener_position - origin).norm();
+ const size_t expected_index = static_cast<size_t>(
+ std::floor(distance / kSpeedOfSound * kSamplingRateHz));
+
+ // Collect impulse response using spheres with different radii.
+ // Expect that the energy values are energy * (4 / radius^2).
+ for (const float listener_sphere_radius : {0.1f, 0.2f, 0.3f, 0.4f, 0.5f}) {
+ AcousticListener listener(listener_position, kImpulseResponseNumSamples);
+ CollectionKernel collection_kernel(
+ listener_sphere_radius, kSamplingRateHz);
+ collection_kernel.Collect(ray, 1.0f, &listener);
+ const float expected_sphere_size_energy_factor =
+ 4.0f / listener_sphere_radius / listener_sphere_radius;
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ ValidateSparseFloatArray(
+ listener.energy_impulse_responses.at(i), {expected_index},
+ {kUnitEnergies.at(i) * expected_sphere_size_energy_factor},
+ kEpsilonFloat);
+ }
+ }
+}
+
+TEST_F(CollectionKernelTest, CollectMultipleRaysWithWeightsTest) {
+ // A listener at (0, 0, 1).
+ AcousticListener listener({0.0f, 0.0f, 1.0f}, kImpulseResponseNumSamples);
+
+ // Add many rays with different energies.
+ const Vector3f origin(0.0f, 0.0f, 0.0f);
+ const Vector3f direction(0.0f, 0.0f, 1.0f);
+ const float t_near = 0.0f;
+ const float t_far = 100.0f;
+ const float prior_distance = 2.0f;
+ std::vector<AcousticRay> rays;
+ std::array<float, kNumReverbOctaveBands> energies = {};
+ for (const float energy : {1.0f, 2.0f, 3.0f, 4.0f, 5.0f}) {
+ energies.fill(energy);
+ rays.emplace_back(origin.data(), direction.data(), t_near, t_far, energies,
+ AcousticRay::RayType::kSpecular, prior_distance);
+ }
+
+ // Collect impulse responses with different weights.
+ const float listener_sphere_radius = 0.1f;
+ CollectionKernel collection_kernel(
+ listener_sphere_radius, kSamplingRateHz);
+ const std::vector<float> weights = {8.0f, 4.0f, 2.0f, 1.0f, 0.5f};
+ for (size_t i = 0; i < rays.size(); ++i) {
+ collection_kernel.Collect(rays[i], weights[i], &listener);
+ }
+
+ // Validate the impulse responses.
+ // The theoretical index of the single non-zero element is:
+ // floor(distance (m) / kSpeedOfSound (m/s) * kSamplingRate (1/s)).
+ const float distance = prior_distance + (listener.position - origin).norm();
+ const size_t expected_index = static_cast<size_t>(
+ std::floor(distance / kSpeedOfSound * kSamplingRateHz));
+
+ // The theoretical energy value is the sum of
+ // energy * sphere_size_energy_factor * weight for all rays.
+ const float expected_sphere_size_energy_factor =
+ 4.0f / listener_sphere_radius / listener_sphere_radius;
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ float expected_energy = 0.0f;
+ for (size_t j = 0; j < rays.size(); ++j) {
+ expected_energy += rays[j].energies().at(i) *
+ expected_sphere_size_energy_factor * weights[j];
+ }
+ ValidateSparseFloatArray(listener.energy_impulse_responses.at(i),
+ {expected_index}, {expected_energy},
+ kEpsilonFloat);
+ }
+}
+
+// This test emulates a physically meaningful setting:
+// 1. N rays are shot in uniformly distributed directions from a source.
+// 2. Some rays intersect with a listener sphere and their contributions are
+// collected, weighted by 1/N; others miss and do not contribute.
+// 3. The total effect of partial collections and the sphere size cancel out,
+// so that the energy collected is actually proportional to 1/distance^2.
+TEST_F(CollectionKernelTest, CollectRaysInMonteCarloIntegrationTest) {
+ // A listener at (0, 0, 2).
+ AcousticListener listener({0.0f, 0.0f, 2.0f}, kImpulseResponseNumSamples);
+
+ // Use AcousticSource to generate N = 100,000 rays with uniformly distributed
+ // directions.
+ const Vector3f source_position(0.0f, 0.0f, 0.0f);
+ const size_t total_num_rays = 100000;
+ std::vector<AcousticRay> rays;
+ std::default_random_engine engine(0);
+ std::uniform_real_distribution<float> distribution(0.0f, 1.0f);
+ AcousticSource source(
+ source_position, kUnitEnergies,
+ [&engine, &distribution] { return distribution(engine); });
+ for (size_t i = 0; i < total_num_rays; ++i) {
+ rays.push_back(source.GenerateRay());
+ }
+
+ // Collect impulse responses using 1/N as weights. Only those rays
+ // intersecting with the listener sphere is collected.
+ const float listener_sphere_radius = 0.1f;
+ Sphere listener_sphere;
+ listener_sphere.center[0] = listener.position[0];
+ listener_sphere.center[1] = listener.position[1];
+ listener_sphere.center[2] = listener.position[2];
+ listener_sphere.radius = listener_sphere_radius;
+ listener_sphere.geometry_id = 1;
+ CollectionKernel collection_kernel(
+ listener_sphere_radius, kSamplingRateHz);
+ const float monte_carlo_weight = 1.0f / static_cast<float>(total_num_rays);
+ for (AcousticRay& ray : rays) {
+ SphereIntersection(listener_sphere, &ray);
+ if (ray.intersected_geometry_id() == listener_sphere.geometry_id) {
+ collection_kernel.Collect(ray, monte_carlo_weight, &listener);
+ }
+ }
+
+ // Validate the impulse response.
+ // The theoretical index of the single non-zero element is:
+ // floor(distance (m) / kSpeedOfSound (m/s) * kSamplingRate (1/s)).
+ const float distance = (listener.position - source_position).norm();
+ const size_t expected_index = static_cast<size_t>(
+ std::floor(distance / kSpeedOfSound * kSamplingRateHz));
+
+ // The expected relative error of a Monte Carlo integration is O(1/sqrt(M)),
+ // where M is the expected number of samples. A listener sphere of radius R
+ // at a distance D away from the source is expected to intersect
+ // M = N * (PI * R^2) / (4 * PI * D^2) = 0.25 * N * R^2 /D^2 rays.
+ // We use 2 / sqrt(M) as the tolerance for relative errors.
+ const float expected_num_intersecting_rays =
+ 0.25f * static_cast<float>(total_num_rays) * listener_sphere_radius *
+ listener_sphere_radius / (distance * distance);
+ const float relative_error_tolerance =
+ 2.0f / std::sqrt(expected_num_intersecting_rays);
+
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ // The theoretical energy value is energy / distance^2.
+ const float expected_energy = kUnitEnergies.at(i) / (distance * distance);
+ ValidateSparseFloatArray(listener.energy_impulse_responses.at(i),
+ {expected_index}, {expected_energy},
+ relative_error_tolerance);
+ }
+}
+
+TEST_F(CollectionKernelTest, CollectOneDiffuseRainRayTest) {
+ // A listener at (5, 0, 0).
+ AcousticListener listener({5.0f, 0.0f, 0.0f}, kImpulseResponseNumSamples);
+
+ // A diffuse-rain ray.
+ const Vector3f origin(0.0f, 0.0f, 0.0f);
+ const Vector3f direction(1.0f, 0.0f, 0.0f);
+ const float t_near = 0.0f;
+ const float t_far = 5.0f;
+ const float prior_distance = 0.0f;
+ const AcousticRay diffuse_rain_ray(
+ origin.data(), direction.data(), t_near, t_far, kUnitEnergies,
+ AcousticRay::RayType::kDiffuse, prior_distance);
+
+ // Collect impulse response, assuming the ray is reflected with a PDF of 0.1.
+ const float direction_pdf = 0.1f;
+ const float listener_sphere_radius = 0.1f;
+ CollectionKernel collection_kernel(listener_sphere_radius, kSamplingRateHz);
+ collection_kernel.CollectDiffuseRain(diffuse_rain_ray, direction_pdf, 1.0f,
+ &listener);
+
+ // Validate the impulse response.
+ // The theoretical index of the single non-zero element is:
+ // floor(distance (m) / kSpeedOfSound (m/s) * kSamplingRate (1/s)).
+ const float distance = (listener.position - origin).norm();
+ const size_t expected_index =
+ static_cast<size_t>(distance / kSpeedOfSound * kSamplingRateHz);
+
+ // The theoretical energy value is energy * energy_factor, where
+ // energy_factor is PDF / distance^2.
+ const float expected_energy_factor = direction_pdf / distance / distance;
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ ValidateSparseFloatArray(
+ listener.energy_impulse_responses.at(i), {expected_index},
+ {kUnitEnergies.at(i) * expected_energy_factor}, kEpsilonFloat);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/estimating_rt60.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/estimating_rt60.cc
new file mode 100644
index 000000000..547f5c1ab
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/estimating_rt60.cc
@@ -0,0 +1,155 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/misc_math.h"
+#include "geometrical_acoustics/estimating_rt60.h"
+
+namespace vraudio {
+
+// The RT60 is estimated by fitting a line to the log of energy. We collect
+// energy from individual impulse responses into bins and use each bin's
+// midpoint and height (average energy inside the bin) as a data point (x, y)
+// for the line fitting algorithm. For example, for a bin that collects 100.0
+// units of energy from 0.0s to 2.0s has a midpoint of 1.0s and height of
+// log10(100.0 / 2.0).
+//
+// In general we have some liberty in how we divide the whole reverb tail as
+// bins. For example, for a reverb tail of 3.5 seconds long:
+// ----------------------------------------------------------------------->
+//
+// we may divide it into 7 bins of widths 0.5 seconds:
+// |---------|---------|---------|---------|---------|---------|---------|>
+// 0.0s 0.5s 1.0s 1.5s 2.0s 2.5s 3.0s 3.5s
+//
+// or we may divide it into 3 bins of widths {0.5s, 1.0s, 2.0s}:
+// |---------|-------------------|---------------------------------------|>
+// 0.0s 0.5s 1.5s 3.5s
+//
+// In this implementation, we adopted a scheme that lets the accumulated energy
+// determine the width. The first bin would contain 0.5 of the total energy of
+// the whole reverb tail, the second bin would contain 0.5 of the remaining
+// energy after the first bin, and so on. The number 0.5 can be replaced by any
+// value in [0.0, 1.0), which is defined as |kBinEnergyRatio| in our
+// implementation.
+//
+// The advantage of this scheme is that there will not be any empty bin (of
+// height zero), which might occur because the impulse responses can be sparse
+// at certain regions. An empty bin is a particularly bad data point and
+// will greatly reduce the efficacy of the linear fitting. (For a more
+// detailed discussion of the sparsity of impulse responses, see [internal ref]
+float EstimateRT60(const std::vector<float>& energy_impulse_responses,
+ const float sampling_rate) {
+ // First initialize the remaining energy to be the total energy.
+ float remaining_energy = 0.0f;
+ for (const float energy : energy_impulse_responses) {
+ remaining_energy += energy;
+ }
+
+ // The collected data points: the bin's midpoints and heights.
+ std::vector<float> bin_midpoints;
+ std::vector<float> bin_heights;
+
+ // The target energy (as a ratio of the remaining energy) that a bin should
+ // collect before closing.
+ const float kBinEnergyRatio = 0.5f;
+ float target_accumulated_energy = remaining_energy * kBinEnergyRatio;
+
+ // Stop when the remaining energy is below this threshold.
+ const float kRemainingThreshold = remaining_energy * 1e-3f;
+
+ // The iteration goes as follows:
+ // 1. A bin is opened.
+ // 2. Energy is accumulated from impulse responses.
+ // 3. When the accumulated energy exceeds the target, the bin is closed and
+ // its midpoint and height recorded.
+ // 4. Repeat 1-3 until the remaining energy is below a threshold.
+ float accumulated_energy = 0.0f;
+ float bin_begin = -1.0f;
+ for (size_t i = 0; i < energy_impulse_responses.size(); ++i) {
+ if (energy_impulse_responses[i] <= 0.0f) {
+ continue;
+ }
+
+ // The first non-zero response is found; set the |bin_begin|.
+ if (bin_begin < 0.0f) {
+ bin_begin = static_cast<float>(i) / sampling_rate;
+ }
+
+ accumulated_energy += energy_impulse_responses[i];
+
+ // Close the bin if the accumulated energy meets the target.
+ if (accumulated_energy > target_accumulated_energy) {
+ // Compute the bin's midpoint, in the unit of second.
+ const float bin_end = static_cast<float>(i + 1) / sampling_rate;
+ const float bin_midpoint = 0.5f * (bin_begin + bin_end);
+
+ // Compute the bin's height as the average energy inside the bin, in the
+ // unit of dB.
+ const float bin_width = bin_end - bin_begin;
+ const float bin_height =
+ 10.0f * std::log10(accumulated_energy / bin_width);
+
+ // Collect the data point.
+ bin_midpoints.push_back(bin_midpoint);
+ bin_heights.push_back(bin_height);
+
+ // Terminate the data point collection when the remaining energy is below
+ // threshold.
+ remaining_energy -= accumulated_energy;
+ if (remaining_energy < kRemainingThreshold) {
+ break;
+ }
+
+ // Start a new bin and update the remaining energy.
+ target_accumulated_energy = remaining_energy * kBinEnergyRatio;
+ bin_begin = bin_end;
+ accumulated_energy = 0.0f;
+ }
+ }
+
+ // Require at least some data points to perform linear fitting.
+ const size_t kMinNumDataPointsForFitting = 3;
+ if (bin_midpoints.size() < kMinNumDataPointsForFitting) {
+ LOG(WARNING) << "Insufficient number of data points for fitting";
+ return 0.0f;
+ }
+
+ // Perform linear fitting.
+ float slope = 0.0f;
+ float intercept = 0.0f;
+ float r_square = 0.0f;
+ if (!LinearLeastSquareFitting(bin_midpoints, bin_heights, &slope, &intercept,
+ &r_square)) {
+ LOG(WARNING) << "Linear least square fitting failed";
+ return 0.0f;
+ }
+ LOG(INFO) << "Fitted slope= " << slope << "; intercept= " << intercept
+ << "; R^2= " << r_square;
+
+ // RT60 is defined as how long it takes for the energy to decay 60 dB.
+ // Note that |slope| should be negative.
+ if (slope < 0.0f) {
+ return -60.0f / slope;
+ } else {
+ LOG(WARNING) << "Invalid RT60 from non-negative slope";
+ return 0.0f;
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/estimating_rt60.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/estimating_rt60.h
new file mode 100644
index 000000000..6e7d5a12d
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/estimating_rt60.h
@@ -0,0 +1,38 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Function to estimate RT60s.
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_ESTIMATING_RT60_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_ESTIMATING_RT60_H_
+
+#include <vector>
+
+namespace vraudio {
+
+// Estimates the RT60 value from collected energy impulse responses.
+//
+// @param energy_impulse_responses Energy impulse responses.
+// @param sampling_rate Sampling rate in Hz. Used to convert indices of the
+// impulse response vector to time in seconds.
+// @return Estimated RT60 value in seconds. Returns 0.0f (meaning no reverb
+// effect at all) when the estimation fails.
+float EstimateRT60(const std::vector<float>& energy_impulse_responses,
+ float sampling_rate);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_ESTIMATING_RT60_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/estimating_rt60_test.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/estimating_rt60_test.cc
new file mode 100644
index 000000000..e48386136
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/estimating_rt60_test.cc
@@ -0,0 +1,185 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/estimating_rt60.h"
+
+#include <cmath>
+#include <memory>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "geometrical_acoustics/impulse_response_computer.h"
+#include "geometrical_acoustics/test_util.h"
+#include "platforms/common/room_effects_utils.h"
+
+namespace vraudio {
+
+namespace {
+
+class EstimateRT60Test : public testing::Test {
+ public:
+ EstimateRT60Test() {
+ Vertex min_corner = {cube_center_[0] - 0.5f * cube_dimensions_[0],
+ cube_center_[1] - 0.5f * cube_dimensions_[1],
+ cube_center_[2] - 0.5f * cube_dimensions_[2]};
+ Vertex max_corner = {cube_center_[0] + 0.5f * cube_dimensions_[0],
+ cube_center_[1] + 0.5f * cube_dimensions_[1],
+ cube_center_[2] + 0.5f * cube_dimensions_[2]};
+
+ BuildTestBoxScene(min_corner, max_corner, &cube_vertices_, &cube_triangles_,
+ &cube_wall_triangles_);
+ }
+
+ protected:
+ std::vector<float> CollectImpulseResponsesAndEstimateRT60(
+ const std::vector<Path>& paths, SceneManager* scene_manager) {
+ // Listener and impulse response computer.
+ std::unique_ptr<std::vector<AcousticListener>> listeners(
+ new std::vector<AcousticListener>);
+ listeners->emplace_back(Eigen::Vector3f(cube_center_),
+ impulse_response_num_samples_);
+ ImpulseResponseComputer impulse_response_computer(
+ listener_sphere_radius_, sampling_rate_, std::move(listeners),
+ scene_manager);
+
+ // Collect impulse responses.
+ impulse_response_computer.CollectContributions(paths);
+ const std::array<std::vector<float>, kNumReverbOctaveBands>&
+ energy_impulse_responses =
+ impulse_response_computer.GetFinalizedListeners()
+ .at(0)
+ .energy_impulse_responses;
+
+ // Estimate RT60 values.
+ std::vector<float> output_rt60_values(kNumReverbOctaveBands, 0.0f);
+ for (size_t band = 0; band < kNumReverbOctaveBands; ++band) {
+ output_rt60_values[band] =
+ EstimateRT60(energy_impulse_responses[band], sampling_rate_);
+ }
+ return output_rt60_values;
+ }
+
+ // Ray-tracing related fields.
+ const float sampling_rate_ = 48000.0f;
+ const float listener_sphere_radius_ = 0.1f;
+ const size_t impulse_response_num_samples_ = 96000;
+ Eigen::Vector3f listener_position_;
+
+ // Data describing a cube scene.
+ std::vector<Vertex> cube_vertices_;
+ std::vector<Triangle> cube_triangles_;
+ const float cube_center_[3] = {0.5f, 0.5f, 0.5f};
+ const float cube_dimensions_[3] = {1.0f, 1.0f, 1.0f};
+
+ std::vector<MaterialName> wall_materials_;
+
+ // Triangles for six walls of a cube. Useful for assigning surface materials.
+ std::vector<std::unordered_set<unsigned int>> cube_wall_triangles_;
+};
+
+// Tests that estimating from an empty impulse responses vector fails.
+TEST_F(EstimateRT60Test, EstimateFromEmptyImpulseResponsesFails) {
+ std::vector<float> empty_energy_impulse_responses;
+
+ // Expect that the estimation function returns 0.
+ EXPECT_EQ(0.0f, EstimateRT60(empty_energy_impulse_responses, sampling_rate_));
+}
+
+// Tests that if the impulse responses are increasing in energy and therefore
+// RT60 is not defined, the estimation returns 0.
+TEST_F(EstimateRT60Test, EstimateFromIncreasingEnergyFails) {
+ std::vector<float> increasing_energy_impulse_responses(1000, 0.0f);
+ for (size_t i = 0; i < 1000; ++i) {
+ increasing_energy_impulse_responses[i] = static_cast<float>(i) * 1e-3f;
+ }
+
+ // Expect that the estimation function returns 0.
+ EXPECT_EQ(0.0f,
+ EstimateRT60(increasing_energy_impulse_responses, sampling_rate_));
+}
+
+// Tests that if estimating from perfectly-constructed, exponentially-decaying
+// impulse responses, then the known RT60 is returned.
+TEST_F(EstimateRT60Test, EstimateFromExponentiallyDecayingResponses) {
+ std::vector<float> energy_impulse_responses(1000, 0.0f);
+ const std::vector<float> expected_RT60s = {0.05f, 0.1f, 0.2f,
+ 0.5f, 1.0f, 2.0f};
+
+ for (const float expected_RT60 : expected_RT60s) {
+ // Construct an exponentially decaying reverb tail with a known RT60, whose
+ // energy at index i is 10^(6 * i / sampling_rate / RT60).
+ for (size_t i = 1; i < energy_impulse_responses.size(); ++i) {
+ energy_impulse_responses[i] = std::pow(
+ 10.0f, -(static_cast<float>(6 * i) / sampling_rate_ / expected_RT60));
+ }
+
+ // Expect that the estimated RT60 is close to the expected one.
+ EXPECT_NEAR(expected_RT60,
+ EstimateRT60(energy_impulse_responses, sampling_rate_), 0.01f);
+ }
+}
+
+// Tests that RT60s estimated from a cube scene agrees with those computed
+// using RoomEffectsUtils::ComputeReverbProperties() (which uses Eyring's
+// equation under the hood).
+TEST_F(EstimateRT60Test, EstimateFromCubeSceneAgreesWithHeuristics) {
+ wall_materials_ = std::vector<MaterialName>{
+ MaterialName::kPlasterSmooth,
+ MaterialName::kLinoleumOnConcrete,
+ MaterialName::kConcreteBlockPainted,
+ MaterialName::kGlassThin,
+ MaterialName::kBrickBare,
+ MaterialName::kPlywoodPanel,
+ };
+
+ // Trace rays in a cube scene and estimate RT60s.
+ SceneManager scene_manager;
+ std::vector<Path> paths = TracePathsInTestcene(
+ 100 /* min_num_rays */, 100 /* max_depth */,
+ 1e-12f /* energy_threshold */, cube_center_, cube_vertices_,
+ cube_triangles_, cube_wall_triangles_, wall_materials_, &scene_manager);
+ std::vector<float> estimated_rt60_values =
+ CollectImpulseResponsesAndEstimateRT60(paths, &scene_manager);
+
+ // A default room properties with some fields set to non-default values.
+ RoomProperties room_properties;
+ room_properties.position[0] = cube_center_[0];
+ room_properties.position[1] = cube_center_[1];
+ room_properties.position[2] = cube_center_[2];
+ room_properties.dimensions[0] = cube_dimensions_[0];
+ room_properties.dimensions[1] = cube_dimensions_[1];
+ room_properties.dimensions[2] = cube_dimensions_[2];
+ for (size_t wall = 0; wall < kNumRoomSurfaces; ++wall) {
+ room_properties.material_names[wall] = wall_materials_[wall];
+ }
+
+ // Reverb properties computed using heuristics.
+ const ReverbProperties reverb_properties =
+ ComputeReverbProperties(room_properties);
+
+ // Compare the two sets of RT60s.
+ const float rt60_error_tolerance = 0.05f;
+ for (size_t band = 0; band < kNumReverbOctaveBands; ++band) {
+ const float estimated_rt60 = estimated_rt60_values[band];
+ const float expected_rt60 = reverb_properties.rt60_values[band];
+ EXPECT_NEAR(estimated_rt60, expected_rt60, rt60_error_tolerance);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/impulse_response_computer.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/impulse_response_computer.cc
new file mode 100644
index 000000000..a4771ed58
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/impulse_response_computer.cc
@@ -0,0 +1,188 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/impulse_response_computer.h"
+
+#include <utility>
+
+#include "Eigen/Core"
+#include "base/logging.h"
+#include "geometrical_acoustics/parallel_for.h"
+#include "geometrical_acoustics/reflection_kernel.h"
+
+namespace vraudio {
+
+using Eigen::Vector3f;
+
+ImpulseResponseComputer::ImpulseResponseComputer(
+ float listener_sphere_radius, float sampling_rate,
+ std::unique_ptr<std::vector<AcousticListener>> listeners,
+ SceneManager* scene_manager)
+ : listeners_(std::move(listeners)),
+ collection_kernel_(listener_sphere_radius, sampling_rate),
+ scene_manager_(scene_manager),
+ finalized_(false),
+ num_total_paths_(0) {
+ CHECK_NOTNULL(listeners_.get());
+ scene_manager->BuildListenerScene(*listeners_, listener_sphere_radius);
+}
+
+ImpulseResponseComputer::~ImpulseResponseComputer() {}
+
+void ImpulseResponseComputer::CollectContributions(
+ const std::vector<Path>& paths_batch) {
+ // Do not collect anymore if finalized.
+ if (finalized_) {
+ return;
+ }
+
+ CHECK(scene_manager_->is_scene_committed());
+ CHECK(scene_manager_->is_listener_scene_committed());
+
+ const unsigned int num_threads = GetNumberOfHardwareThreads();
+ ParallelFor(
+ num_threads, paths_batch.size(), [&paths_batch, this](size_t path_index) {
+ const Path& path = paths_batch.at(path_index);
+ const AcousticRay* previous_ray = nullptr;
+ AcousticRay diffuse_rain_ray;
+ for (const AcousticRay& ray : path.rays) {
+ // Handle diffuse reflections separately using the diffuse rain
+ // algorithm. For details, see "A Fast Reverberation Estimator for
+ // Virtual Environments" by Schroder et al., 2007.
+ if (ray.type() == AcousticRay::RayType::kDiffuse &&
+ previous_ray != nullptr) {
+ // Connect each listener from the reflection point.
+ for (AcousticListener& listener : *listeners_) {
+ const ReflectionKernel& reflection =
+ scene_manager_->GetAssociatedReflectionKernel(
+ previous_ray->intersected_primitive_id());
+
+ float reflection_pdf = 0.0f;
+ reflection.ReflectDiffuseRain(*previous_ray, ray,
+ listener.position, &reflection_pdf,
+ &diffuse_rain_ray);
+
+ if (!diffuse_rain_ray.Intersect(scene_manager_->scene())) {
+ // Get PDF from the reflection kernel that the previous ray
+ // intersects.
+ collection_kernel_.CollectDiffuseRain(
+ diffuse_rain_ray, 1.0f /* weight */, reflection_pdf,
+ &listener);
+ }
+ }
+
+ previous_ray = &ray;
+ continue;
+ }
+
+ // |ray| may intersect multiple listener spheres. We handle the
+ // intersections one-by-one as following:
+ // 1. Find the first intersected sphere S. The ray's data will be
+ // modified so that ray.t_far() corresponds to the intersection
+ // point. Terminate if no intersection is found.
+ // 2. Collect contribution to the listener associated to S.
+ // 3. Spawn a sub-ray that starts at the intersection point (moved
+ // slightly inside S) and repeat 1.
+ //
+ // Since the origin of the new sub-ray is inside sphere S, it does not
+ // intersect with S again (according to our definition of
+ // intersections; see Sphere::SphereIntersection()).
+ //
+ // Each of the sub-rays has the same origin and direction as the
+ // original ray, but with t_near and t_far partitioning the interval
+ // between the original ray's t_near and t_far. For example:
+ //
+ // ray: t_near = 0.0 ------------------------------> t_far = 5.0
+ // sub_ray_1: t_near = 0.0 -------> t_far = 2.0
+ // sub_ray_2: t_near = 2.0 ---------> t_far = 5.0
+ AcousticRay sub_ray(ray.origin(), ray.direction(), ray.t_near(),
+ ray.t_far(), ray.energies(), ray.type(),
+ ray.prior_distance());
+
+ // Norm of |ray.direction|. Useful in computing
+ // |sub_ray.prior_distance| later.
+ const float ray_direction_norm = Vector3f(ray.direction()).norm();
+
+ // In theory with sufficient AcousticRay::kRayEpsilon, the same sphere
+ // should not be intersected twice by the same ray segment. In
+ // practice, due to floating-point inaccuracy, this might still
+ // happen. We explicitly prevent double-counting by checking whether
+ // the intersected sphere id has changed.
+ unsigned int previous_intersected_sphere_id = RTC_INVALID_GEOMETRY_ID;
+
+ // To prevent an infinite loop, terminate if one ray segment has more
+ // intersections than the number of listeners, which is an upper bound
+ // of the number of actually contributing sub-rays.
+ size_t num_intersections = 0;
+ while (sub_ray.Intersect(scene_manager_->listener_scene()) &&
+ num_intersections < listeners_->size()) {
+ const unsigned int sphere_id = sub_ray.intersected_geometry_id();
+ if (sphere_id != previous_intersected_sphere_id) {
+ AcousticListener& listener = listeners_->at(
+ scene_manager_->GetListenerIndexFromSphereId(sphere_id));
+ collection_kernel_.Collect(sub_ray, 1.0f /* weight */, &listener);
+ } else {
+ LOG(WARNING) << "Double intersection with sphere[" << sphere_id
+ << "]; contribution skipped. Consider increasing "
+ << "AcousticRay::kRayEpsilon";
+ }
+ previous_intersected_sphere_id = sphere_id;
+
+ // Spawn a new sub-ray whose t_near corresponds to the intersection
+ // point. The new sub-ray's |prior_distance| field is extended by
+ // the distance traveled by the old sub-ray up to the intersection
+ // point.
+ const float new_prior_distance =
+ sub_ray.prior_distance() +
+ (sub_ray.t_far() - sub_ray.t_near()) * ray_direction_norm;
+ sub_ray = AcousticRay(ray.origin(), ray.direction(),
+ sub_ray.t_far() + AcousticRay::kRayEpsilon,
+ ray.t_far(), ray.energies(), ray.type(),
+ new_prior_distance);
+ ++num_intersections;
+ }
+
+ previous_ray = &ray;
+ }
+ });
+ num_total_paths_ += paths_batch.size();
+}
+
+const std::vector<AcousticListener>&
+ImpulseResponseComputer::GetFinalizedListeners() {
+ DCHECK_GT(num_total_paths_, 0U);
+
+ if (!finalized_) {
+ // For a Monte Carlo method that estimates a value with N samples,
+ // <estimated value> = 1/N * sum(<value of a sample>). We apply the
+ // 1/N weight after all impulse responses for all listeners are collected.
+ const float monte_carlo_weight =
+ 1.0f / static_cast<float>(num_total_paths_);
+ for (AcousticListener& listener : *listeners_) {
+ for (std::vector<float>& responses_in_band :
+ listener.energy_impulse_responses) {
+ for (float& response : responses_in_band) {
+ response *= monte_carlo_weight;
+ }
+ }
+ }
+ finalized_ = true;
+ }
+
+ return *listeners_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/impulse_response_computer.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/impulse_response_computer.h
new file mode 100644
index 000000000..59f3d91a0
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/impulse_response_computer.h
@@ -0,0 +1,112 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_IMPULSE_RESPONSE_COMPUTER_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_IMPULSE_RESPONSE_COMPUTER_H_
+
+#include <memory>
+#include <vector>
+
+#include "geometrical_acoustics/acoustic_listener.h"
+#include "geometrical_acoustics/collection_kernel.h"
+#include "geometrical_acoustics/path.h"
+#include "geometrical_acoustics/scene_manager.h"
+
+namespace vraudio {
+
+// A class that computes impulse responses from traced sound propagation paths
+// and modifies the listener's data (namely listener.energy_impulse_response)
+// in place.
+// Each listener is associated to a sphere with finite volume, and rays that
+// intersect with the sphere contributes energy to the listener.
+//
+// This class is to be used in conjunction with a PathTracer, which produces
+// batches of sound propagation paths that this class consumes.
+// Example use (which follows a create-collect-finalize pattern):
+//
+// // Create.
+// ImpulseResponseComputer impulse_response_computer(0.1f, 44100.0f,
+// std::move(listeners));
+// // Collect.
+// for (const auto& source : sources) {
+// // Divide |total_num_paths| into |num_batch| batches, each of size
+// // |num_rays_per_batch|.
+// for (size_t i = 0; i < num_batch; ++i) {
+// paths_batch =
+// path_tracer.TracePaths(source, num_rays_per_batch, 10, 1e-6);
+// impulse_response_computer.CollectContributions(paths_batch,
+// total_num_paths);
+// }
+// }
+//
+// // Finalize and use the impulse responses in listeners e.g. write them to
+// // a file or pass them to an audio render.
+// for (const auto& listener:
+// impulse_response_computer. GetFinalizedListeners()) {
+// const auto& responses = listener.energy_impulse_responses;
+// // Do something with |responses|.
+// }
+class ImpulseResponseComputer {
+ public:
+ // Constructor.
+ //
+ // @param listener_sphere_radius Radius of listener spheres (m).
+ // @param sampling_rate Sampling rate (Hz).
+ // @param listeners Vector of AcousticListener's whose impulse responses are
+ // to be computed.
+ // @param scene_manager SceneManager.
+ ImpulseResponseComputer(
+ float listener_sphere_radius, float sampling_rate,
+ std::unique_ptr<std::vector<AcousticListener>> listeners,
+ SceneManager* scene_manager);
+ virtual ~ImpulseResponseComputer();
+
+ // Collects contributions from a batch of paths to all listeners if
+ // the collection is not finalized yet.
+ //
+ // @param paths_batch All sound propagation paths in a batch.
+ void CollectContributions(const std::vector<Path>& paths_batch);
+
+ // Finalizes the listeners and returns them. After calling this, further
+ // calls to CollectContributions() have no effect.
+ //
+ // @return Vector of finalized listeners.
+ const std::vector<AcousticListener>& GetFinalizedListeners();
+
+ private:
+ // Vector of listeners.
+ const std::unique_ptr<std::vector<AcousticListener>> listeners_;
+
+ // Collection kernel used to collect contributions from rays to listeners.
+ CollectionKernel collection_kernel_;
+
+ // Scene manager. Used to keep records of listener spheres for ray-sphere
+ // intersection tests. Also used in the diffuse rain algorithm to to test
+ // whether there is an un-obstructed path from a reflection point to a
+ // listener.
+ SceneManager* scene_manager_;
+
+ // Is the collection finalized.
+ bool finalized_;
+
+ // Total number of paths (contributing or not) that energies are collected
+ // from before the collection is finalized. This will be used to average
+ // energy contributions.
+ size_t num_total_paths_;
+};
+
+} // namespace vraudio
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_IMPULSE_RESPONSE_COMPUTER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/impulse_response_computer_test.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/impulse_response_computer_test.cc
new file mode 100644
index 000000000..f3e042586
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/impulse_response_computer_test.cc
@@ -0,0 +1,377 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/impulse_response_computer.h"
+
+#include <array>
+#include <cmath>
+#include <random>
+#include <utility>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "geometrical_acoustics/acoustic_listener.h"
+#include "geometrical_acoustics/acoustic_ray.h"
+#include "geometrical_acoustics/acoustic_source.h"
+#include "geometrical_acoustics/path.h"
+#include "geometrical_acoustics/scene_manager.h"
+#include "geometrical_acoustics/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+using Eigen::Vector3f;
+
+const float kSamplingRateHz = 1000.0f;
+const float kListenerSphereRadiusMeter = 0.1f;
+const size_t kImpulseResponseNumSamples = 1000;
+
+// The energy collected from a ray.
+// kCollectedEnergyPerRay
+// = 1.0 * CollectionKernel::sphere_size_energy_factor_
+// = 1.0 * 4.0 / kListenerSphereRadius^2 = 1.0 * 4.0 / 0.01
+// = 400.0.
+// See CollectionKernel::sphere_size_energy_factor_ for details.
+const float kCollectedEnergyPerRay = 400.0f;
+
+class ImpulseResponseComputerTest : public testing::Test {
+ public:
+ void SetUp() override { paths_.clear(); }
+
+ protected:
+ void AddListenersAtPositions(const std::vector<Vector3f>& positions,
+ std::vector<AcousticListener>* listeners) {
+ for (const Vector3f& position : positions) {
+ listeners->emplace_back(position, kImpulseResponseNumSamples);
+ }
+ }
+
+ // Validate indices and values of the impulse responses for all listeners.
+ //
+ // param@ listeners Listeners whose impulse responses are to be validated.
+ // param@ expected_indices_for_listeners Expected indices of the non-zero
+ // elements for all listeners' impulse responses. So that
+ // expected_indices_for_listeners[i][j] is the index of the j-th non-zero
+ // element in listener i's impulse response.
+ // param@ expected_energies_for_listeners Expected energy values of the
+ // non-zero elements for all listeners' impulse responses. So that
+ // the value stored in expected_energies_for_listeners[i][j] is the j-th
+ // non-zero element in listener i's impulse response.
+ // param@ relative_error_tolerance_for_listeners Tolerances of relative errors
+ // when comparing energy impulse responses for all listeners.
+ void ValidateImpulseResponses(
+ const std::vector<AcousticListener>& listeners,
+ const std::vector<std::vector<size_t>>& expected_indices_for_listeners,
+ const std::vector<std::vector<float>>& expected_energies_for_listeners,
+ const std::vector<float> relative_error_tolerances_for_listeners) {
+ ASSERT_EQ(expected_indices_for_listeners.size(), listeners.size());
+ ASSERT_EQ(expected_energies_for_listeners.size(), listeners.size());
+ ASSERT_EQ(relative_error_tolerances_for_listeners.size(), listeners.size());
+ for (size_t i = 0; i < listeners.size(); ++i) {
+ for (size_t j = 0; j < kNumReverbOctaveBands; ++j) {
+ ValidateSparseFloatArray(listeners[i].energy_impulse_responses.at(j),
+ expected_indices_for_listeners[i],
+ expected_energies_for_listeners[i],
+ relative_error_tolerances_for_listeners[i]);
+ }
+ }
+ }
+
+ const std::array<float, kNumReverbOctaveBands> kUnitEnergies{
+ {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}};
+
+ std::vector<Path> paths_;
+ SceneManager scene_manager_;
+};
+
+TEST_F(ImpulseResponseComputerTest, OnePathMultipleRaysOneListenerTest) {
+ // A listener at (1, 0, 0).
+ std::unique_ptr<std::vector<AcousticListener>> listeners(
+ new std::vector<AcousticListener>);
+ const Vector3f listener_position(1.0f, 0.0f, 0.0f);
+ AddListenersAtPositions({listener_position}, listeners.get());
+
+ scene_manager_.BuildScene({}, {});
+ ImpulseResponseComputer impulse_response_computer(
+ kListenerSphereRadiusMeter, kSamplingRateHz, std::move(listeners),
+ &scene_manager_);
+
+ // One path with multiple ray segments.
+ Path path;
+
+ // Imagine a scene with two specular walls parallel to the y-z plane.
+ // One (called the right wall) is at (2, 0, 0) and the other (called the
+ // left wall) at (-2, 0, 0). A ray shooting at the +x direction will reflect
+ // back-and-forth between these parallel walls.
+ //
+ // First ray segment: from source at (0, 0, 0), shooting at (1, 0, 0).
+ const Vector3f source_position(0.0f, 0.0f, 0.0f);
+ const Vector3f left_to_right_direction(1.0f, 0.0f, 0.0f);
+ const float wall_position_x = 2.0f;
+ path.rays.emplace_back(source_position.data(), left_to_right_direction.data(),
+ 0.0f, wall_position_x, kUnitEnergies,
+ AcousticRay::RayType::kSpecular, 0.0f);
+
+ // Also record the distances traveled each time the ray passes through the
+ // listener sphere. This will be used later to verify the indices of non-zero
+ // elements in the impulse response.
+ std::vector<float> source_listener_distances = {
+ (source_position - listener_position).norm()}; // First segment.
+
+ // The rest of the reflections. Half of these rays start from the left wall
+ // and stop at the right wall, and the other half start from the right wall
+ // and stop at the left wall. We compute up to order |max_order| = 10.
+ const Vector3f right_wall_origin(wall_position_x, 0.0f, 0.0f);
+ const Vector3f left_wall_origin(-wall_position_x, 0.0f, 0.0f);
+ const float distance_between_walls =
+ (right_wall_origin - left_wall_origin).norm();
+ const float listener_left_wall_distance =
+ (listener_position - left_wall_origin).norm();
+ const float listener_right_wall_distance =
+ (listener_position - right_wall_origin).norm();
+ const Vector3f right_to_left_direction = -left_to_right_direction;
+ const size_t max_order = 10;
+
+ // Prior distance corresponds to the first ray segment.
+ float prior_distance = (right_wall_origin - source_position).norm();
+ for (size_t order = 1; order < max_order; ++order) {
+ float source_listener_distance = source_listener_distances.back();
+ if (order % 2 == 0) {
+ path.rays.emplace_back(left_wall_origin.data(),
+ left_to_right_direction.data(), 0.0f,
+ distance_between_walls, kUnitEnergies,
+ AcousticRay::RayType::kSpecular, prior_distance);
+
+ // Add the distance of {listener -> left wall -> listener} to the
+ // accumulated source-listener distance.
+ source_listener_distance += 2.0f * listener_left_wall_distance;
+ } else {
+ path.rays.emplace_back(right_wall_origin.data(),
+ right_to_left_direction.data(), 0.0f,
+ distance_between_walls, kUnitEnergies,
+ AcousticRay::RayType::kSpecular, prior_distance);
+
+ // Add the distance of {listener -> right wall -> listener} to the
+ // accumulated source-listener distance.
+ source_listener_distance += 2.0f * listener_right_wall_distance;
+ }
+ source_listener_distances.push_back(source_listener_distance);
+
+ // Each reflection adds |distance_between_walls| to |prior_distance|.
+ prior_distance += distance_between_walls;
+ }
+ paths_.push_back(path);
+
+ // Compute impulse response.
+ impulse_response_computer.CollectContributions(paths_);
+
+ // Validate the impulse responses.
+ // The expected indices are
+ // floor(distance / kSpeedOfSound (m/s) * kSamplingRateHz (1/s)).
+ // The theoretical energy values are all kCollectedEnergyPerRay.
+ std::vector<std::vector<size_t>> expected_indices_for_listeners(1);
+ std::vector<std::vector<float>> expected_energies_for_listeners(1);
+ for (size_t order = 0; order < max_order; ++order) {
+ expected_indices_for_listeners.back().push_back(
+ static_cast<size_t>(std::floor(source_listener_distances[order] /
+ kSpeedOfSound * kSamplingRateHz)));
+ expected_energies_for_listeners.back().push_back(kCollectedEnergyPerRay);
+ }
+ ValidateImpulseResponses(impulse_response_computer.GetFinalizedListeners(),
+ expected_indices_for_listeners,
+ expected_energies_for_listeners, {kEpsilonFloat});
+}
+
+TEST_F(ImpulseResponseComputerTest, OnePathMultipleListenersTest) {
+ // A series of listeners along the z-axis. Some of them overlapping.
+ std::unique_ptr<std::vector<AcousticListener>> listeners(
+ new std::vector<AcousticListener>);
+ AddListenersAtPositions({{0.0f, 0.0f, 1.0f},
+ {0.0f, 0.0f, 2.0f},
+ {0.0f, 0.0f, 3.0f},
+ {0.0f, 0.0f, 3.05f},
+ {0.0f, 0.0f, 3.10f}},
+ listeners.get());
+
+ scene_manager_.BuildScene({}, {});
+ ImpulseResponseComputer impulse_response_computer(
+ kListenerSphereRadiusMeter, kSamplingRateHz, std::move(listeners),
+ &scene_manager_);
+
+ // One path with only one ray. This ray should pass through all listener
+ // spheres.
+ const Vector3f origin(0.0f, 0.0f, 0.0f);
+ const Vector3f direction(0.0f, 0.0f, 1.0f);
+ Path path;
+ path.rays.emplace_back(origin.data(), direction.data(), 0.0f, 100.0f,
+ kUnitEnergies, AcousticRay::RayType::kSpecular, 0.0f);
+ paths_.push_back(path);
+
+ // Compute impulse response.
+ impulse_response_computer.CollectContributions(paths_);
+
+ // Validate the impulse responses.
+ // The theoretical indices are
+ // floor(distance / kSpeedOfSound (m/s) * kSamplingRateHz (1/s)).
+ // The theoretical energy values are all kCollectedEnergyPerRay.
+ std::vector<std::vector<size_t>> expected_indices_for_listeners;
+ std::vector<std::vector<float>> expected_energies_for_listeners;
+ std::vector<float> relative_error_tolerances_for_listeners;
+ for (const AcousticListener& listener :
+ impulse_response_computer.GetFinalizedListeners()) {
+ const float distance = (listener.position - origin).norm();
+ expected_indices_for_listeners.push_back({static_cast<size_t>(
+ std::floor(distance / kSpeedOfSound * kSamplingRateHz))});
+ expected_energies_for_listeners.push_back({kCollectedEnergyPerRay});
+ relative_error_tolerances_for_listeners.push_back(kEpsilonFloat);
+ }
+ ValidateImpulseResponses(impulse_response_computer.GetFinalizedListeners(),
+ expected_indices_for_listeners,
+ expected_energies_for_listeners,
+ relative_error_tolerances_for_listeners);
+}
+
+// Test that the energies collected at different positions is proportional to
+// 1/D^2, where D is the source-listener distance.
+TEST_F(ImpulseResponseComputerTest,
+ EnergyInverselyProportionalDistanceSquaredTest) {
+ // A series of listeners along the y-axis.
+ std::unique_ptr<std::vector<AcousticListener>> listeners(
+ new std::vector<AcousticListener>);
+ AddListenersAtPositions(
+ {
+ {0.0f, 1.0f, 0.0f},
+ {0.0f, 1.2f, 0.0f},
+ {0.0f, 1.4f, 0.0f},
+ {0.0f, 1.6f, 0.0f},
+ {0.0f, 1.8f, 0.0f},
+ {0.0f, 2.0f, 0.0f},
+ {0.0f, 2.5f, 0.0f},
+ {0.0f, 3.0f, 0.0f},
+ },
+ listeners.get());
+
+ scene_manager_.BuildScene({}, {});
+ ImpulseResponseComputer impulse_response_computer(
+ kListenerSphereRadiusMeter, kSamplingRateHz, std::move(listeners),
+ &scene_manager_);
+
+ // Add 10,000 paths from rays with uniformly distributed directions.
+ const size_t min_num_paths = 10000;
+ const Vector3f source_position(0.0f, 0.0f, 0.0f);
+ paths_ = GenerateUniformlyDistributedRayPaths(source_position.data(),
+ min_num_paths);
+
+ // Compute impulse response.
+ impulse_response_computer.CollectContributions(paths_);
+
+ // Check the index and value of the single non-zero element for each listener.
+ std::vector<std::vector<size_t>> expected_indices_for_listeners;
+ std::vector<std::vector<float>> expected_energies_for_listeners;
+ std::vector<float> relative_error_tolerances_for_listeners;
+ for (const AcousticListener& listener :
+ impulse_response_computer.GetFinalizedListeners()) {
+ // The theoretical index is
+ // floor(distance / kSpeedOfSound (m/s) * kSamplingRateHz (1/s)).
+ const float distance = (listener.position - source_position).norm();
+ expected_indices_for_listeners.push_back({static_cast<size_t>(
+ std::floor(distance / kSpeedOfSound * kSamplingRateHz))});
+
+ // The theoretical energy value is 1.0 / distance^2.
+ expected_energies_for_listeners.push_back({1.0f / (distance * distance)});
+
+ // The expected relative error of a Monte Carlo integration is O(1/sqrt(M)),
+ // where M is the expected number of samples. A listener sphere of radius R
+ // at a distance D away from the source is expected to intersect
+ // M = N * (PI * R^2) / (4 * PI * D^2) = 0.25 * N * R^2 /D^2 rays.
+ // We use 2 / sqrt(M) as the tolerance for relative errors.
+ const float expected_num_intersecting_rays =
+ 0.25f * static_cast<float>(paths_.size()) * kListenerSphereRadiusMeter *
+ kListenerSphereRadiusMeter / (distance * distance);
+ relative_error_tolerances_for_listeners.push_back(
+ 2.0f / std::sqrt(expected_num_intersecting_rays));
+ }
+ ValidateImpulseResponses(impulse_response_computer.GetFinalizedListeners(),
+ expected_indices_for_listeners,
+ expected_energies_for_listeners,
+ relative_error_tolerances_for_listeners);
+}
+
+// Tests that collecting after GetFinalizedListeners() is called has no effect.
+TEST_F(ImpulseResponseComputerTest, CollectingAfterFinalizeHasNoEffect) {
+ // A listener at (1, 0, 0).
+ std::unique_ptr<std::vector<AcousticListener>> listeners(
+ new std::vector<AcousticListener>);
+ const Vector3f listener_position(1.0f, 0.0f, 0.0f);
+ AddListenersAtPositions({listener_position}, listeners.get());
+
+ scene_manager_.BuildScene({}, {});
+ ImpulseResponseComputer impulse_response_computer(
+ kListenerSphereRadiusMeter, kSamplingRateHz, std::move(listeners),
+ &scene_manager_);
+
+ // One path with only one ray. This ray should pass through the listener
+ // sphere.
+ const Vector3f origin(0.0f, 0.0f, 0.0f);
+ const Vector3f direction(1.0f, 0.0f, 0.0f);
+ Path path;
+ path.rays.emplace_back(origin.data(), direction.data(), 0.0f, 100.0f,
+ kUnitEnergies, AcousticRay::RayType::kSpecular, 0.0f);
+ paths_.push_back(path);
+ impulse_response_computer.CollectContributions(paths_);
+
+ // Finalize the listeners and make a copy of the energy impulse responses.
+ const std::array<std::vector<float>, kNumReverbOctaveBands>
+ old_energy_impulse_responses =
+ impulse_response_computer.GetFinalizedListeners()
+ .at(0)
+ .energy_impulse_responses;
+
+ // Try to collect another set of paths. This would change the energies if
+ // they were collected, but they are not because the collection is finalized.
+ paths_.clear();
+ const Vector3f another_origin(1.0f, -1.0f, 0.0f);
+ const Vector3f another_direction(0.0f, 1.0f, 0.0f);
+ Path another_path;
+ another_path.rays.emplace_back(
+ another_origin.data(), another_direction.data(), 0.0f, 100.0f,
+ kUnitEnergies, AcousticRay::RayType::kSpecular, 0.0f);
+ paths_.push_back(another_path);
+ impulse_response_computer.CollectContributions(paths_);
+
+ // Verify that the energy impulse responses are the same as the copy.
+ const std::array<std::vector<float>, kNumReverbOctaveBands>&
+ new_energy_impulse_responses =
+ impulse_response_computer.GetFinalizedListeners()
+ .at(0)
+ .energy_impulse_responses;
+ for (size_t band = 0; band < kNumReverbOctaveBands; ++band) {
+ const std::vector<float>& old_responses_in_band =
+ old_energy_impulse_responses[band];
+ const std::vector<float>& new_responses_in_band =
+ new_energy_impulse_responses[band];
+ for (size_t index = 0; index < old_responses_in_band.size(); ++index) {
+ EXPECT_FLOAT_EQ(old_responses_in_band[index],
+ new_responses_in_band[index]);
+ }
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/mesh.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/mesh.h
new file mode 100644
index 000000000..5582bcdfc
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/mesh.h
@@ -0,0 +1,40 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Collection of data structures useful to define a mesh.
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_MESH_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_MESH_H_
+
+namespace vraudio {
+
+// A simple vertex data structure.
+struct Vertex {
+ float x;
+ float y;
+ float z;
+};
+
+// A simple triangle data structure defined as the 3 indices of vertices.
+struct Triangle {
+ int v0;
+ int v1;
+ int v2;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_MESH_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/parallel_for.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/parallel_for.cc
new file mode 100644
index 000000000..bc5eef2db
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/parallel_for.cc
@@ -0,0 +1,44 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/parallel_for.h"
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include "base/logging.h"
+#include "utils/task_thread_pool.h"
+
+namespace vraudio {
+
+void ParallelFor(unsigned int num_threads, size_t num_iterations,
+ const std::function<void(const size_t)>& function) {
+ TaskThreadPool worker_thread_pool;
+ CHECK(worker_thread_pool.StartThreadPool(num_threads));
+
+ for (size_t i = 0; i < num_iterations; ++i) {
+ while (!worker_thread_pool.WaitUntilWorkerBecomesAvailable()) {
+ }
+
+ const TaskThreadPool::TaskClosure task = [i, &function]() { function(i); };
+ CHECK(worker_thread_pool.RunOnWorkerThread(task));
+ }
+ worker_thread_pool.StopThreadPool();
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/parallel_for.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/parallel_for.h
new file mode 100644
index 000000000..d2f6951c1
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/parallel_for.h
@@ -0,0 +1,53 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_PARALLEL_FOR_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_PARALLEL_FOR_H_
+
+#include <algorithm>
+#include <functional>
+#include <thread>
+
+namespace vraudio {
+
+// Gets the number of hardware threads; always returns more than zero.
+//
+// @return Number of hardware threads.
+inline unsigned int GetNumberOfHardwareThreads() {
+ // According to the standard, hardware_concurrency() may return zero if "this
+ // value is not computable or well defined". In that case, we want to have a
+ // thread count of one (instead of zero).
+ return std::max(1U, std::thread::hardware_concurrency());
+}
+
+// Repeatedly executes |function| multiple times, specified by |num_iterations|.
+// Different executions of |function| may occur on one of the |num_threads|
+// threads.
+//
+// |function| has a function signature of void(const size_t i), with |i| taking
+// values in the range [0, |num_iterations|).
+//
+// @param num_threads Number of threads to execute |function|.
+// @param num_iterations Number of iterations for this for loop.
+// @param function Function to be run. The function should take a single
+// parameter that is of type const size_t, representing the iteration
+// index, and no return value.
+void ParallelFor(unsigned int num_threads, size_t num_iterations,
+ const std::function<void(const size_t)>& function);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_PARALLEL_FOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/parallel_for_test.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/parallel_for_test.cc
new file mode 100644
index 000000000..2e1ec563a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/parallel_for_test.cc
@@ -0,0 +1,65 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/parallel_for.h"
+
+#include <array>
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+
+namespace vraudio {
+
+namespace {
+
+// Tests multi-threaded increment of a single variable, using increasing numbers
+// of threads.
+TEST(ParallelForTest, IncreasingThreadCounts) {
+ const size_t kNumIterations = 1000;
+ for (unsigned int num_threads = 1; num_threads < 16; ++num_threads) {
+ std::mutex mutex;
+ std::condition_variable condition_variable;
+ size_t index = 0;
+ ParallelFor(num_threads, kNumIterations, [&](const size_t i) {
+ std::unique_lock<std::mutex> lock(mutex);
+ while (i != index) {
+ condition_variable.wait(lock);
+ }
+ ++index;
+ condition_variable.notify_all();
+ });
+ EXPECT_EQ(kNumIterations, index);
+ }
+}
+
+// Tests recursive use of ParallelFor().
+TEST(ParallelForTest, RecursiveParallelFor) {
+ std::atomic<int> counter(0);
+ ParallelFor(16U, 16, [&](const size_t i) {
+ ParallelFor(16U, 16, [&](const size_t j) {
+ for (int k = 0; k < 16; ++k) {
+ ++counter;
+ }
+ });
+ });
+ EXPECT_EQ(16 * 16 * 16, counter.load());
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path.h
new file mode 100644
index 000000000..f6e3d1681
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path.h
@@ -0,0 +1,36 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_PATH_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_PATH_H_
+
+#include <vector>
+
+#include "geometrical_acoustics/acoustic_ray.h"
+
+namespace vraudio {
+
+// A simple data structure of modeling a sound propagation path as a vector of
+// ray segments, which contain all necessary information. For example, the
+// history of triangles on this path can be found by |ray.primID|, where |ray|
+// is an element |rays|.
+struct Path {
+ std::vector<AcousticRay> rays;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_PATH_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path_tracer.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path_tracer.cc
new file mode 100644
index 000000000..6f4c6652a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path_tracer.cc
@@ -0,0 +1,96 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/path_tracer.h"
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "geometrical_acoustics/parallel_for.h"
+#include "geometrical_acoustics/reflection_kernel.h"
+
+namespace vraudio {
+
+std::vector<Path> PathTracer::TracePaths(const AcousticSource& source,
+ size_t min_num_rays, size_t max_depth,
+ float energy_threshold) {
+ // The tracing would not work if the scene is not committed.
+ CHECK(scene_manager_.is_scene_committed());
+
+ // Find the actual number of rays to trace as the next square number greater
+ // or equal to |min_num_rays|.
+ const size_t sqrt_num_rays = static_cast<size_t>(
+ std::ceil(std::sqrt(static_cast<float>(min_num_rays))));
+ const size_t num_rays = sqrt_num_rays * sqrt_num_rays;
+
+ // In the current implementation, one ray does not spawn more than one child
+ // ray, hence the number of paths is the same as the number of rays from the
+ // source.
+ std::vector<Path> paths(num_rays);
+ std::vector<AcousticRay> rays_from_source =
+ source.GenerateStratifiedRays(num_rays, sqrt_num_rays);
+ const unsigned int num_threads = GetNumberOfHardwareThreads();
+ ParallelFor(
+ num_threads, num_rays,
+ [&rays_from_source, &paths, this, max_depth,
+ energy_threshold](size_t ray_index) {
+ if (max_depth == 0) return;
+ Path& path = paths.at(ray_index);
+
+ // Pre-allocate memory space for better performance.
+ path.rays.reserve(max_depth);
+
+ path.rays.push_back(rays_from_source[ray_index]);
+ size_t depth = 0;
+ while (true) {
+ AcousticRay& current_ray = path.rays.back();
+
+ // Stop generating new rays if the current ray escapes.
+ if (!current_ray.Intersect(scene_manager_.scene())) {
+ break;
+ }
+
+ // Stop generating new rays if |depth| reaches |max_depth|.
+ ++depth;
+ if (depth >= max_depth) {
+ break;
+ }
+
+ // Handle interactions with scene geometries.
+ const ReflectionKernel& reflection =
+ scene_manager_.GetAssociatedReflectionKernel(
+ current_ray.intersected_primitive_id());
+ AcousticRay new_ray = reflection.Reflect(current_ray);
+
+ // Stop tracing if all energies in all frequency bands of the new ray
+ // are too low in energy.
+ bool is_energy_high_enough = false;
+ for (const float energy : new_ray.energies()) {
+ if (energy >= energy_threshold) {
+ is_energy_high_enough = true;
+ break;
+ }
+ }
+ if (!is_energy_high_enough) {
+ break;
+ }
+ path.rays.push_back(new_ray);
+ }
+ });
+ return paths;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path_tracer.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path_tracer.h
new file mode 100644
index 000000000..4a8a35a7e
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path_tracer.h
@@ -0,0 +1,59 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_PATH_TRACER_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_PATH_TRACER_H_
+
+#include <functional>
+#include <vector>
+
+#include "geometrical_acoustics/acoustic_source.h"
+#include "geometrical_acoustics/path.h"
+#include "geometrical_acoustics/scene_manager.h"
+
+namespace vraudio {
+
+// A wrapper around Embree that finds sound propagation paths from a source.
+class PathTracer {
+ public:
+ // Constructor.
+ //
+ // @param scene_manager Scene manager.
+ explicit PathTracer(const SceneManager& scene_manager)
+ : scene_manager_(scene_manager) {}
+ ~PathTracer() {}
+
+ // Traces sound propagation paths from a source.
+ //
+ // @param source Source from which paths are traced.
+ // @param min_num_rays Minimum number of rays to be traced.
+ // @param max_depth Maximum depth of tracing performed along a path. The
+ // tracing stops when reading |max_depth| interactions with the scene
+ // geometries.
+ // @param energy_threshold Energy threshold below which the tracing stops.
+ // @return Vector of at least |min_num_rays| traced paths.
+ std::vector<Path> TracePaths(const AcousticSource& source,
+ size_t min_num_rays, size_t max_depth,
+ float energy_threshold);
+
+ private:
+ // Scene manager.
+ const SceneManager& scene_manager_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_PATH_TRACER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path_tracer_test.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path_tracer_test.cc
new file mode 100644
index 000000000..a1cb67395
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/path_tracer_test.cc
@@ -0,0 +1,193 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/path_tracer.h"
+
+#include <memory>
+#include <random>
+#include <unordered_set>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "geometrical_acoustics/mesh.h"
+#include "geometrical_acoustics/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+using Eigen::Vector3f;
+
+class PathTracerTest : public testing::Test {
+ public:
+ PathTracerTest() : random_engine_(), distribution_(0.0f, 1.0f) {
+ random_number_generator_ = [this] { return distribution_(random_engine_); };
+ }
+
+ void BuildEmptyScene() {
+ scene_manager_.reset(new SceneManager);
+ scene_manager_->BuildScene(std::vector<Vertex>(), std::vector<Triangle>());
+ all_triangle_indices_.clear();
+ }
+
+ void BuildGroundScene() {
+ scene_manager_.reset(new SceneManager);
+ std::vector<Vertex> ground_vertices{{0.0f, 0.0f, 0.0f},
+ {0.0f, 1.0f, 0.0f},
+ {1.0f, 0.0f, 0.0f},
+ {1.0f, 1.0f, 0.0f}};
+ std::vector<Triangle> ground_triangles{{0, 3, 1}, {0, 2, 3}};
+ scene_manager_->BuildScene(ground_vertices, ground_triangles);
+ all_triangle_indices_ = {0, 1};
+ }
+
+ void BuildBoxScene() {
+ scene_manager_.reset(new SceneManager);
+ std::vector<Vertex> box_vertices;
+ std::vector<Triangle> box_triangles;
+ BuildTestBoxScene({0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}, &box_vertices,
+ &box_triangles, nullptr);
+ scene_manager_->BuildScene(box_vertices, box_triangles);
+ all_triangle_indices_.clear();
+ for (unsigned int i = 0; i < 12; ++i) {
+ all_triangle_indices_.insert(i);
+ }
+ }
+
+ protected:
+ const std::array<float, kNumReverbOctaveBands> kZeroAbsorptionCoefficients = {
+ {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}};
+ std::unique_ptr<SceneManager> scene_manager_;
+ std::unordered_set<unsigned int> all_triangle_indices_;
+ std::default_random_engine random_engine_;
+ std::uniform_real_distribution<float> distribution_;
+ std::function<float()> random_number_generator_;
+};
+
+TEST_F(PathTracerTest, AllPathsHaveLengthOneInEmptySceneTest) {
+ BuildEmptyScene();
+ PathTracer path_tracer(*scene_manager_);
+
+ AcousticSource source({0.0f, 0.0f, 0.0f}, kUnitEnergies,
+ random_number_generator_);
+ const size_t min_num_rays = 100;
+ const size_t max_depth = 10;
+ const float energy_thresold = 1e-6f;
+ std::vector<Path> paths =
+ path_tracer.TracePaths(source, min_num_rays, max_depth, energy_thresold);
+
+ EXPECT_LE(paths.size(), min_num_rays);
+ for (size_t i = 0; i < paths.size(); ++i) {
+ EXPECT_EQ(static_cast<int>(paths[i].rays.size()), 1);
+ }
+}
+
+TEST_F(PathTracerTest, GroundSceneTest) {
+ BuildGroundScene();
+
+ // Associate a perfect reflection to all triangles.
+ ReflectionKernel reflection_kernel(kZeroAbsorptionCoefficients, 0.0f,
+ random_number_generator_);
+ scene_manager_->AssociateReflectionKernelToTriangles(reflection_kernel,
+ all_triangle_indices_);
+
+ PathTracer path_tracer(*scene_manager_);
+
+ AcousticSource source({0.5f, 0.5f, 0.1f}, kUnitEnergies,
+ random_number_generator_);
+ const size_t min_num_rays = 100;
+ const size_t max_depth = 10;
+ const float energy_thresold = 1e-6f;
+ std::vector<Path> paths =
+ path_tracer.TracePaths(source, min_num_rays, max_depth, energy_thresold);
+
+ // All paths have at least one ray (for those that did not hit the ground)
+ // and at most two rays (for those hitting the ground).
+ for (size_t i = 0; i < paths.size(); ++i) {
+ EXPECT_GE(static_cast<int>(paths[i].rays.size()), 1);
+ EXPECT_LE(static_cast<int>(paths[i].rays.size()), 2);
+
+ // For those with length = 2, validate that the first ray ends on the
+ // ground plane.
+ if (paths[i].rays.size() == 2) {
+ const AcousticRay& first_ray = paths[i].rays.front();
+ const Vector3f& end_point =
+ Vector3f(first_ray.origin()) +
+ first_ray.t_far() * Vector3f(first_ray.direction());
+ EXPECT_NEAR(end_point.z(), 0.0f, 1e-7f);
+ }
+ }
+}
+
+TEST_F(PathTracerTest, TerminateAtMaxDepthTest) {
+ // A box scene within which rays never escape.
+ BuildBoxScene();
+
+ // Associate a perfect reflection to all triangles. Theoretically there should
+ // be infinite reflections, but the path tracer stops once each path reaches
+ // length |max_depth|.
+ ReflectionKernel reflection_kernel(kZeroAbsorptionCoefficients, 0.0f,
+ random_number_generator_);
+ scene_manager_->AssociateReflectionKernelToTriangles(reflection_kernel,
+ all_triangle_indices_);
+ PathTracer path_tracer(*scene_manager_);
+
+ AcousticSource source({0.5f, 0.5f, 0.5f}, kUnitEnergies,
+ random_number_generator_);
+ const size_t min_num_rays = 100;
+ const size_t max_depth = 10;
+ const float energy_thresold = 1e-6f;
+ std::vector<Path> paths =
+ path_tracer.TracePaths(source, min_num_rays, max_depth, energy_thresold);
+
+ for (size_t i = 0; i < paths.size(); ++i) {
+ EXPECT_EQ(paths[i].rays.size(), max_depth);
+ }
+}
+
+TEST_F(PathTracerTest, TerminateWhenEnergyBelowThresholdTest) {
+ // A box scene within which rays never escape.
+ BuildBoxScene();
+
+ // Associate an absorptive reflection with an absorption coefficient slightly
+ // over 90% to all triangles.
+ std::array<float, kNumReverbOctaveBands> absorption_coefficients;
+ absorption_coefficients.fill(0.9001f);
+ ReflectionKernel reflection_kernel(absorption_coefficients, 0.0f,
+ random_number_generator_);
+ scene_manager_->AssociateReflectionKernelToTriangles(reflection_kernel,
+ all_triangle_indices_);
+ PathTracer path_tracer(*scene_manager_);
+
+ // Since the reflected energy is slightly below 1e-1 after each reflection,
+ // we expect the path tracer to stop after X reflections for an
+ // energy threshold of 1e-X, and that each path is of length X.
+ AcousticSource source({0.5f, 0.5f, 0.5f}, kUnitEnergies,
+ random_number_generator_);
+ const size_t min_num_rays = 100;
+ const size_t max_depth = 10;
+ const float energy_thresold = 1e-6f;
+ std::vector<Path> paths =
+ path_tracer.TracePaths(source, min_num_rays, max_depth, energy_thresold);
+
+ for (size_t i = 0; i < paths.size(); ++i) {
+ EXPECT_EQ(static_cast<int>(paths[i].rays.size()), 6);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/proxy_room_estimator.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/proxy_room_estimator.cc
new file mode 100644
index 000000000..d0df0369a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/proxy_room_estimator.cc
@@ -0,0 +1,438 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/proxy_room_estimator.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "Eigen/Core"
+#include "base/logging.h"
+#include "platforms/common/room_effects_utils.h"
+
+namespace vraudio {
+
+namespace {
+
+// Normals of the walls of an axis-aligned cube. Walls are indexed like this:
+// 0: Left (-x)
+// 1: Right (+x)
+// 2: Bottom (-y)
+// 3: Top (+y)
+// 4: Back (-z)
+// 5: Front (+z)
+static const float kCubeWallNormals[kNumRoomSurfaces][3] = {
+ {-1.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f},
+ {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 1.0f}};
+
+// A helper function to determine if a ray (shooting from |ray_origin| in the
+// |ray_direction| direction) intersects with a plane (whose normal is
+// |plane_normal| and whose distance to the origin is
+// |plane_distance_to_origin|). When returning true, the intersection point is
+// stored in |intersection|.
+bool PlaneIntersection(const WorldPosition& plane_normal,
+ float plane_distance_to_origin,
+ const WorldPosition& ray_origin,
+ const WorldPosition& ray_direction,
+ WorldPosition* intersection) {
+ // Do not count if the direction is too close to parallel to the plane (i.e.,
+ // the dot product of |direction| and |plane_normal|, |d_dot_n| is too close
+ // to zero).
+ const float d_dot_n = ray_direction.dot(plane_normal);
+ if (std::abs(d_dot_n) < vraudio::kEpsilonFloat) {
+ return false;
+ }
+
+ // Find the intersection point.
+ const float t =
+ (plane_distance_to_origin - ray_origin.dot(plane_normal)) / d_dot_n;
+
+ // Do not count back-intersection, i.e., the intersection is in the negative
+ // |ray_direction|.
+ if (t < 0.0f) {
+ return false;
+ }
+
+ *intersection = ray_origin + t * ray_direction;
+ return true;
+}
+
+// A helper function to find the index of the wall with which the ray
+// intersects. Returns true if such wall is found.
+bool FindIntersectingWallIndex(const WorldPosition& dimensions,
+ const WorldPosition& origin,
+ const WorldPosition& direction,
+ size_t* wall_index) {
+ // The ray intersects a wall if:
+ // 1. It intersects the plane of the wall.
+ // 2. The intersection point lies in the bounding box of the wall.
+ const float dx = dimensions[0] * 0.5f;
+ const float dy = dimensions[1] * 0.5f;
+ const float dz = dimensions[2] * 0.5f;
+
+ // Data used to test plane intersections (1. above): normals and distances
+ // of all the walls.
+ const float wall_distances[kNumRoomSurfaces] = {dx, dx, dy, dy, dz, dz};
+
+ // Data used to test whether the intersecting point is in the bounding box
+ // of the wall (2. above). The bounding box has a small thickness
+ // |wall_thickness| along the normal direction of the wall.
+ const float wall_thickness = AcousticRay::kRayEpsilon;
+
+ // The centers and corresponding dimensions defining the bounding boxes of
+ // all the walls.
+ const WorldPosition wall_centers[kNumRoomSurfaces] = {
+ {-dx, 0.0f, 0.0f}, {+dx, 0.0f, 0.0f}, {0.0f, -dy, 0.0f},
+ {0.0f, +dy, 0.0f}, {0.0f, 0.0f, -dz}, {0.0f, 0.0f, +dz},
+ };
+ const WorldPosition wall_dimensions[kNumRoomSurfaces] = {
+ {wall_thickness, dimensions[1], dimensions[2]},
+ {wall_thickness, dimensions[1], dimensions[2]},
+ {dimensions[0], wall_thickness, dimensions[2]},
+ {dimensions[0], wall_thickness, dimensions[2]},
+ {dimensions[0], dimensions[1], wall_thickness},
+ {dimensions[0], dimensions[1], wall_thickness},
+ };
+
+ // Iterate through all the walls to find the one that the ray intersects with.
+ for (size_t wall = 0; wall < kNumRoomSurfaces; ++wall) {
+ WorldPosition intersection;
+ if (PlaneIntersection(WorldPosition(kCubeWallNormals[wall]),
+ wall_distances[wall], origin, direction,
+ &intersection) &&
+ IsPositionInAabb(intersection, wall_centers[wall],
+ wall_dimensions[wall])) {
+ *wall_index = wall;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// A helper function to sort a vector of {position, distance} pairs according
+// to the distances and exclude outliers.
+std::vector<std::pair<WorldPosition, float>>
+SortPositionsAndDistancesAndFindInliers(
+ float outlier_portion, const std::vector<std::pair<WorldPosition, float>>&
+ positions_and_distances) {
+ std::vector<std::pair<WorldPosition, float>>
+ sorted_inlier_positions_and_distances(positions_and_distances);
+
+ std::sort(sorted_inlier_positions_and_distances.begin(),
+ sorted_inlier_positions_and_distances.end(),
+ [](const std::pair<WorldPosition, float>& position_and_distance_1,
+ const std::pair<WorldPosition, float>& position_and_distance_2) {
+ return position_and_distance_1.second <
+ position_and_distance_2.second;
+ });
+
+ const float total_size =
+ static_cast<float>(sorted_inlier_positions_and_distances.size());
+ const size_t inliner_begin =
+ static_cast<size_t>(std::floor(total_size * outlier_portion));
+ const size_t inliner_end =
+ static_cast<size_t>(std::ceil(total_size * (1.0f - outlier_portion)));
+
+ std::copy(sorted_inlier_positions_and_distances.begin() + inliner_begin,
+ sorted_inlier_positions_and_distances.begin() + inliner_end,
+ sorted_inlier_positions_and_distances.begin());
+ sorted_inlier_positions_and_distances.resize(inliner_end - inliner_begin);
+
+ return sorted_inlier_positions_and_distances;
+}
+
+// A helper function to compute the average position and distance from a
+// vector of positions and distances.
+void ComputeAveragePositionAndDistance(
+ const std::vector<std::pair<WorldPosition, float>>& positions_and_distances,
+ WorldPosition* average_position, float* average_distance) {
+ DCHECK(!positions_and_distances.empty());
+ WorldPosition sum_positions(0.0f, 0.0f, 0.0f);
+ float sum_distances = 0.0f;
+ for (const std::pair<WorldPosition, float>& position_and_distance :
+ positions_and_distances) {
+ sum_positions += position_and_distance.first;
+ sum_distances += position_and_distance.second;
+ }
+
+ const float inverse_size =
+ 1.0f / static_cast<float>(positions_and_distances.size());
+ *average_position = sum_positions * inverse_size;
+ *average_distance = sum_distances * inverse_size;
+}
+
+} // namespace
+
+void ProxyRoomEstimator::CollectHitPointData(
+ const std::vector<Path>& paths_batch) {
+ // The size of already collected hit points before this batch.
+ const size_t previous_size = hit_points_.size();
+ hit_points_.resize(previous_size + paths_batch.size());
+
+ // Collect one hit point per path.
+ for (size_t path_index = 0; path_index < paths_batch.size(); ++path_index) {
+ const size_t hit_point_index = path_index + previous_size;
+ const Path& path = paths_batch.at(path_index);
+ hit_points_[hit_point_index] = CollectHitPointDataFromPath(path);
+ }
+}
+
+bool ProxyRoomEstimator::EstimateCubicProxyRoom(
+ float outlier_portion, RoomProperties* room_properties) {
+ // Check that |outlier_portion| is in the range of [0, 0.5].
+ CHECK_GE(outlier_portion, 0.0f);
+ CHECK_LE(outlier_portion, 0.5f);
+
+ // Estimation fails if there is no hit point.
+ if (hit_points_.empty()) {
+ return false;
+ }
+
+ // The geometry part (position, dimensions, and rotation) of the proxy room.
+ if (!EstimateCubeGeometry(outlier_portion, room_properties->position,
+ room_properties->dimensions,
+ room_properties->rotation)) {
+ LOG(WARNING) << "Unable to estimate a cube without a hit point with finite "
+ << "|t_far|; returning a default cube.";
+ return false;
+ }
+
+ // The surface material part of the proxy room.
+ EstimateSurfaceMaterials(WorldPosition(room_properties->position),
+ WorldPosition(room_properties->dimensions),
+ room_properties->material_names);
+ return true;
+}
+
+ProxyRoomEstimator::HitPointData
+ProxyRoomEstimator::CollectHitPointDataFromPath(const Path& path) {
+ CHECK(!path.rays.empty());
+ HitPointData hit_point;
+
+ // Common data.
+ const AcousticRay& first_order_ray = path.rays[0];
+ hit_point.origin = WorldPosition(first_order_ray.origin());
+ hit_point.direction = WorldPosition(first_order_ray.direction());
+
+ // Treating escaped and non-escaped rays differently for their |t_far| and
+ // |absorption_coefficients|.
+ if (path.rays.size() > 1) {
+ hit_point.t_far = first_order_ray.t_far();
+
+ // Figure out the absorption coefficients by examining the incoming and the
+ // reflected energy.
+ const AcousticRay& second_order_ray = path.rays[1];
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ const float incoming_energy = first_order_ray.energies().at(i);
+ const float reflected_energy = second_order_ray.energies().at(i);
+ CHECK_GT(incoming_energy, 0.0f);
+ CHECK_GE(incoming_energy, reflected_energy);
+
+ hit_point.absorption_coefficients[i] =
+ 1.0f - (reflected_energy / incoming_energy);
+ }
+ } else {
+ hit_point.t_far = AcousticRay::kInfinity;
+
+ // Escaped rays are considered as completely absorbed.
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ hit_point.absorption_coefficients[i] = 1.0f;
+ }
+ }
+
+ return hit_point;
+}
+
+bool ProxyRoomEstimator::EstimateCubeGeometry(float outlier_portion,
+ float* position,
+ float* dimensions,
+ float* rotation) {
+ // First group the hit points according to which cube wall it would hit,
+ // assuming an initial guess of a cube that
+ // 1. centers at the origin of the first ray,
+ // 2. has unit dimensions, and
+ // 3. has no rotation.
+ const WorldPosition cube_dimensions(1.0f, 1.0f, 1.0f);
+ const std::array<std::vector<HitPointData>, kNumRoomSurfaces>
+ hit_points_on_walls =
+ GroupHitPointsByWalls(hit_points_[0].origin, cube_dimensions);
+
+ // Compute the positions and distances of from the hit points on walls.
+ const std::vector<std::pair<WorldPosition, float>> positions_and_distances =
+ ComputeDistancesAndPositionsFromHitPoints(hit_points_on_walls);
+
+ // Cannot estimate if there are no valid positions and distances.
+ if (positions_and_distances.empty()) {
+ return false;
+ }
+
+ // Sort the |positions_and_distances| according to the distances and also
+ // filter out outliers (whose distances are too large or too small).
+ const std::vector<std::pair<WorldPosition, float>>
+ sorted_inlier_positions_and_distances =
+ SortPositionsAndDistancesAndFindInliers(outlier_portion,
+ positions_and_distances);
+
+ // Cannot estimate if there are no inlying positions and distances.
+ if (sorted_inlier_positions_and_distances.empty()) {
+ return false;
+ }
+
+ // Take the average distance and origin of the inlier hit points.
+ WorldPosition average_hit_point_poisition;
+ float average_distance;
+ ComputeAveragePositionAndDistance(sorted_inlier_positions_and_distances,
+ &average_hit_point_poisition,
+ &average_distance);
+
+ // Use twice the average distance as the cube's dimensions.
+ const float estimated_cube_dimension = 2.0f * average_distance;
+ dimensions[0] = estimated_cube_dimension;
+ dimensions[1] = estimated_cube_dimension;
+ dimensions[2] = estimated_cube_dimension;
+
+ // Use the average hit point position as the cube's position.
+ position[0] = average_hit_point_poisition[0];
+ position[1] = average_hit_point_poisition[1];
+ position[2] = average_hit_point_poisition[2];
+
+ // No rotation.
+ rotation[0] = 0.0f;
+ rotation[1] = 0.0f;
+ rotation[2] = 0.0f;
+ rotation[3] = 1.0f;
+
+ return true;
+}
+
+std::array<std::vector<ProxyRoomEstimator::HitPointData>, kNumRoomSurfaces>
+ProxyRoomEstimator::GroupHitPointsByWalls(
+ const WorldPosition& room_position, const WorldPosition& room_dimensions) {
+ const WorldPosition kOrigin(0.0f, 0.0f, 0.0f);
+
+ // No rotation for an axis-aligned room.
+ const WorldRotation kNoRotation(1.0f, 0.0f, 0.0f, 0.0f);
+
+ // Reserve memory space for hit points on walls.
+ std::array<std::vector<HitPointData>, kNumRoomSurfaces> hit_points_on_walls;
+ for (std::vector<HitPointData>& hit_points_on_wall : hit_points_on_walls) {
+ hit_points_on_wall.reserve(hit_points_.size());
+ }
+
+ for (const HitPointData& hit_point : hit_points_) {
+ // First transform the ray's origin and direction to the local space
+ // of the cube centered at |position|.
+ WorldPosition local_direction;
+ GetRelativeDirection(kOrigin, kNoRotation, hit_point.direction,
+ &local_direction);
+ WorldPosition local_origin;
+ GetRelativeDirection(room_position, kNoRotation, hit_point.origin,
+ &local_origin);
+
+ // Find the wall that the ray intersects.
+ size_t wall_index;
+ if (FindIntersectingWallIndex(room_dimensions, local_origin,
+ local_direction, &wall_index)) {
+ hit_points_on_walls[wall_index].push_back(hit_point);
+ }
+ }
+
+ return hit_points_on_walls;
+}
+
+std::vector<std::pair<WorldPosition, float>>
+ProxyRoomEstimator::ComputeDistancesAndPositionsFromHitPoints(
+ const std::array<std::vector<HitPointData>, kNumRoomSurfaces>&
+ hit_points_on_walls) {
+ std::vector<std::pair<WorldPosition, float>> positions_and_distances;
+ for (size_t wall = 0; wall < kNumRoomSurfaces; ++wall) {
+ const WorldPosition wall_normal(kCubeWallNormals[wall]);
+ for (const HitPointData& hit_point : hit_points_on_walls[wall]) {
+ if (hit_point.t_far == AcousticRay::kInfinity) {
+ continue;
+ }
+
+ std::pair<WorldPosition, float> position_and_distance;
+ position_and_distance.first =
+ hit_point.origin + (hit_point.t_far * hit_point.direction);
+ position_and_distance.second =
+ (position_and_distance.first - hit_point.origin).dot(wall_normal);
+ positions_and_distances.push_back(position_and_distance);
+ }
+ }
+
+ return positions_and_distances;
+}
+
+void ProxyRoomEstimator::EstimateSurfaceMaterials(
+ const WorldPosition& room_position, const WorldPosition& room_dimensions,
+ MaterialName* material_names) {
+ // Compute the average absorption coefficients on all the walls.
+ CoefficientsVector average_absorption_coefficients[kNumRoomSurfaces];
+
+ // Now that we have better estimates of the proxy room's position, dimensions,
+ // and rotation, re-group the hit points using these estimates.
+ const std::array<std::vector<HitPointData>, kNumRoomSurfaces>&
+ hit_points_on_walls =
+ GroupHitPointsByWalls(room_position, room_dimensions);
+ for (size_t wall = 0; wall < kNumRoomSurfaces; ++wall) {
+ const std::vector<HitPointData>& hit_points = hit_points_on_walls[wall];
+ for (const HitPointData& hit_point : hit_points) {
+ average_absorption_coefficients[wall] +=
+ CoefficientsVector(hit_point.absorption_coefficients.data());
+ }
+ }
+
+ for (size_t wall = 0; wall < kNumRoomSurfaces; ++wall) {
+ if (!hit_points_on_walls[wall].empty()) {
+ average_absorption_coefficients[wall] /=
+ static_cast<float>(hit_points_on_walls[wall].size());
+ } else {
+ // If no hit point is found on this wall, we consider all energy absorbed
+ // in this direction and the absorption coefficient being 1.0.
+ average_absorption_coefficients[wall].fill(1.0f);
+ }
+ }
+
+ // Find the closest surface material. The distance between two materials is
+ // defined as the simple Euclidean distance.
+ for (size_t wall = 0; wall < kNumRoomSurfaces; ++wall) {
+ MaterialName min_distance_material_name = MaterialName::kTransparent;
+ float min_material_distance = std::numeric_limits<float>::infinity();
+
+ // We want to exclude kUniform from fitting.
+ const size_t num_materials_to_fit =
+ static_cast<size_t>(MaterialName::kNumMaterialNames) - 1;
+ for (size_t material_index = 0; material_index < num_materials_to_fit;
+ ++material_index) {
+ const RoomMaterial& material = GetRoomMaterial(material_index);
+ const float material_distance =
+ (average_absorption_coefficients[wall] -
+ CoefficientsVector(material.absorption_coefficients))
+ .norm();
+ if (material_distance < min_material_distance) {
+ min_material_distance = material_distance;
+ min_distance_material_name = material.name;
+ }
+ }
+ material_names[wall] = min_distance_material_name;
+ LOG(INFO) << "Wall[" << wall << "] material= " << min_distance_material_name
+ << " distance= " << min_material_distance;
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/proxy_room_estimator.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/proxy_room_estimator.h
new file mode 100644
index 000000000..63aa58998
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/proxy_room_estimator.h
@@ -0,0 +1,156 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_PROXY_ROOM_ESTIMATOR_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_PROXY_ROOM_ESTIMATOR_H_
+
+#include <array>
+#include <utility>
+#include <vector>
+
+#include "Eigen/Core"
+#include "api/resonance_audio_api.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+#include "geometrical_acoustics/path.h"
+#include "platforms/common/room_properties.h"
+
+namespace vraudio {
+
+// A class that estimates a "proxy room" from traced sound propagation paths.
+// A proxy room is used to model dynamic early reflections as if they are
+// reflected from a box-shaped room, even though the real scene geometry is
+// arbitrarily complex. This is to complement the pre-computed late reverb
+// effects, and it takes the same input (i.e., the ray tracing results) as the
+// reverb pre-computation. The proxy room is estimated from the first order
+// ray paths (i.e., those from the source to the first hit points), and the
+// estimation has two stages:
+// 1. Fitting the geometry (currently only as an axis-aligned cube).
+// 2. Fitting the surface materials on the six walls.
+class ProxyRoomEstimator {
+ public:
+ ProxyRoomEstimator() = default;
+
+ // ProxyRoomEstimator is neither copyable nor movable.
+ ProxyRoomEstimator(const ProxyRoomEstimator&) = delete;
+ ProxyRoomEstimator& operator=(const ProxyRoomEstimator&) = delete;
+
+ // Collects hit point data from traced ray paths, batch-by-batch.
+ //
+ // @param paths_batch A batch of ray paths.
+ void CollectHitPointData(const std::vector<Path>& paths_batch);
+
+ // Estimates a cube-shaped proxy room from collected hit points. Hit points
+ // are sorted according to their traveled distance. In order to make the
+ // estimation more robust, we discard "outlier" hit points, i.e., those whose
+ // traveled distances are too large or too small.
+ //
+ // @param outlier_portion What portion of the hit points are considered as
+ // outliers. For example, a value of 0.1 means that the hit points whose
+ // distances are in the top 10% and bottom 10% are considered as outliers
+ // and discarded. The value must be in the range of [0, 0.5].
+ // @param room_properties Room properties of the estimated axis-aligned cube-
+ // shaped room, each wall having an estimated surface material.
+ // @return True if the estimation is successful.
+ bool EstimateCubicProxyRoom(float outlier_portion,
+ RoomProperties* room_properties);
+
+ private:
+ class CoefficientsVector : public Eigen::Matrix<float, kNumReverbOctaveBands,
+ 1, Eigen::DontAlign> {
+ public:
+ // Inherits all constructors with 1-or-more arguments. Necessary because
+ // MSVC12 doesn't support inheriting constructors.
+ template <typename Arg1, typename... Args>
+ CoefficientsVector(const Arg1& arg1, Args&&... args)
+ : Matrix(arg1, std::forward<Args>(args)...) {}
+
+ // Constructs a zero vector.
+ CoefficientsVector() { setZero(); }
+ };
+
+ // A struct to contain data necessary for estimating a proxy room.
+ struct HitPointData {
+ // Origin of the ray that creates this hit point.
+ WorldPosition origin;
+
+ // Direction of the ray that creates this hit point.
+ WorldPosition direction;
+
+ // Ray parameter t corresponding to the hit point. If the ray escaped the
+ // scene and did not hit anything, then |t_far| takes the value of
+ // |AcousticRay::kInfinity|. Escaped rays are still useful in estimating
+ // surface materials, because they can be considered completely absorbed
+ // and should increase the effective absorption coefficients.
+ float t_far;
+
+ // Absorption coefficients of the surface of the hit point across the
+ // frequency bands.
+ std::array<float, kNumReverbOctaveBands> absorption_coefficients;
+ };
+
+ // Collects one hit point from one traced ray path.
+ //
+ // @param path Traced ray path.
+ // @return Collected hit point.
+ HitPointData CollectHitPointDataFromPath(const Path& path);
+
+ // Estimates the geometry of the cube.
+ //
+ // @param outlier_portion Portion of all hit points to be discarded. See
+ // EstimateCube() above.
+ // @param position Output center position of the estimated cube.
+ // @param dimensions Output dimensions of the estimated cube.
+ // @return True if the estimation is successful.
+ bool EstimateCubeGeometry(float outlier_portion, float* position,
+ float* dimensions, float* rotation);
+
+ // Groups hit points by which walls they lie on in an assumed axis-aligned
+ // room.
+ //
+ // @param room_position Center position of the assumed axis-aligned room.
+ // @param room_dimensions Dimensions of the assumed axis-aligned room.
+ // @return An array of six elements, each being a vector of hit points
+ // on one of the six walls.
+ std::array<std::vector<HitPointData>, kNumRoomSurfaces> GroupHitPointsByWalls(
+ const WorldPosition& room_position, const WorldPosition& room_dimensions);
+
+ // Compute the hit point positions and distances (measured along the normal
+ // direction of the walls that they hit) from hit points on walls.
+ //
+ // @param hit_points_on_walls Hit points on walls.
+ // @return A vector of {hit point position, distance} pairs.
+ std::vector<std::pair<WorldPosition, float>>
+ ComputeDistancesAndPositionsFromHitPoints(
+ const std::array<std::vector<HitPointData>, kNumRoomSurfaces>&
+ hit_points_on_walls);
+
+ // Estimates the surface materials on the six walls of the proxy room.
+ //
+ // @param room_position Center position of the estimated proxy room.
+ // @param room_dimensions Dimensions of the estimated proxy room.
+ // @param material_names Names of the estimated surface materials.
+ void EstimateSurfaceMaterials(const WorldPosition& room_position,
+ const WorldPosition& room_dimensions,
+ MaterialName* material_names);
+
+ // Collected hit points.
+ std::vector<HitPointData> hit_points_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_PROXY_ROOM_ESTIMATOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/proxy_room_estimator_test.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/proxy_room_estimator_test.cc
new file mode 100644
index 000000000..68f0dc030
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/proxy_room_estimator_test.cc
@@ -0,0 +1,317 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/proxy_room_estimator.h"
+
+#include <random>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "geometrical_acoustics/acoustic_source.h"
+#include "geometrical_acoustics/test_util.h"
+#include "platforms/common/room_effects_utils.h"
+#include "platforms/common/room_properties.h"
+
+namespace vraudio {
+
+namespace {
+
+using Eigen::Vector3f;
+
+class ProxyRoomEstimatorTest : public testing::Test {
+ public:
+ ProxyRoomEstimatorTest() {
+ BuildTestBoxScene({0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}, &cube_vertices_,
+ &cube_triangles_, &cube_wall_triangles_);
+ }
+
+ protected:
+ void ReflectRaysInPaths(float t_far, std::vector<Path>* paths) {
+ for (Path& path : *paths) {
+ // Add a reflected ray to the end of each path, whose energy is half
+ // the first ray.
+ AcousticRay& ray = path.rays.back();
+ ray.set_t_far(t_far);
+ const float reflected_origin[3] = {
+ ray.origin()[0] + ray.t_far() * ray.direction()[0],
+ ray.origin()[1] + ray.t_far() * ray.direction()[1],
+ ray.origin()[2] + ray.t_far() * ray.direction()[2],
+ };
+ AcousticRay reflected_ray(reflected_origin, kZDirection, t_far,
+ AcousticRay::kInfinity, kHalfUnitEnergies,
+ AcousticRay::RayType::kSpecular, t_far);
+ path.rays.push_back(reflected_ray);
+ }
+ }
+
+ ProxyRoomEstimator estimator_;
+ SceneManager scene_manager_;
+
+ // Ray-tracing related fields.
+ const int kNumRays = 2000;
+ const int kMaxDepth = 3;
+ const float kEnergyThresold = 1e-6f;
+ const std::array<float, kNumReverbOctaveBands> kHalfUnitEnergies{
+ {0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f}};
+ const float kSourcePosition[3] = {1.0f, 2.0f, 3.0f};
+ const float kZDirection[3] = {0.0f, 0.0f, 1.0f};
+
+ // Data describing a cube scene.
+ std::vector<Vertex> cube_vertices_;
+ std::vector<Triangle> cube_triangles_;
+ const float cube_center_[3] = {0.5f, 0.5f, 0.5f};
+ std::vector<MaterialName> wall_materials_;
+
+ // Triangles for six walls of a cube. Useful for assigning surface materials.
+ std::vector<std::unordered_set<unsigned int>> cube_wall_triangles_;
+};
+
+// Tests that estimating from an empty paths batch fails.
+TEST_F(ProxyRoomEstimatorTest, EstimateFromEmptyPathsFails) {
+ // An empty paths batch.
+ std::vector<Path> empty_paths_batch;
+ estimator_.CollectHitPointData(empty_paths_batch);
+
+ // Expect that the estimation function returns false.
+ const float outlier_portion = 0.0f;
+ RoomProperties room_properties;
+ EXPECT_FALSE(
+ estimator_.EstimateCubicProxyRoom(outlier_portion, &room_properties));
+}
+
+// Tests that if all paths in a batch escape the scene before hitting anything,
+// the estimation fails.
+TEST_F(ProxyRoomEstimatorTest, EstimateFromEscapedPathsFails) {
+ // A paths batch in which all rays escape, i.e., have infinite |t_far|.
+ std::vector<Path> escaped_paths_batch;
+ for (size_t i = 0; i < 10; ++i) {
+ Path path;
+ path.rays.emplace_back(kSourcePosition, // origin
+ kZDirection, // direction
+ 0.0f, // t_near
+ AcousticRay::kInfinity, // t_far
+ kUnitEnergies, // energies
+ AcousticRay::RayType::kSpecular, // ray_type
+ 0.0f // prior_distance
+ );
+ escaped_paths_batch.push_back(path);
+ }
+ estimator_.CollectHitPointData(escaped_paths_batch);
+
+ // Expect that the estimation function returns false.
+ const float outlier_portion = 0.0f;
+ RoomProperties room_properties;
+ EXPECT_FALSE(
+ estimator_.EstimateCubicProxyRoom(outlier_portion, &room_properties));
+}
+
+// Tests that if the scene itself is a cube, then the estimated proxy room
+// can recover the same cube with a certain error below tolerance.
+TEST_F(ProxyRoomEstimatorTest, EstimateCubeFromCubeScene) {
+ wall_materials_ = std::vector<MaterialName>{
+ MaterialName::kAcousticCeilingTiles,
+ MaterialName::kWoodPanel,
+ MaterialName::kConcreteBlockPainted,
+ MaterialName::kGlassThin,
+ MaterialName::kGrass,
+ MaterialName::kMarble,
+ };
+
+ // Trace rays in a cube scene.
+ std::vector<Path> paths = TracePathsInTestcene(
+ kNumRays, kMaxDepth, kEnergyThresold, cube_center_, cube_vertices_,
+ cube_triangles_, cube_wall_triangles_, wall_materials_, &scene_manager_);
+
+ // Estimate a proxy room.
+ estimator_.CollectHitPointData(paths);
+ const float outlier_portion = 0.0f;
+ RoomProperties room_properties;
+ EXPECT_TRUE(
+ estimator_.EstimateCubicProxyRoom(outlier_portion, &room_properties));
+
+ // Expect that the estimated proxy room is the same cube, with a certain
+ // error in position and dimensions.
+ const float position_tolerance = 0.05f;
+ ExpectFloat3Close(room_properties.position, cube_center_, position_tolerance);
+ const float expected_dimensions[3] = {1.0f, 1.0f, 1.0f};
+ const float dimensions_tolerance = 0.01f;
+ ExpectFloat3Close(room_properties.dimensions, expected_dimensions,
+ dimensions_tolerance);
+
+ // Expect that the estimated surface materials are the same as the original
+ // cube.
+ for (size_t wall = 0; wall < 6; ++wall) {
+ EXPECT_EQ(room_properties.material_names[wall], wall_materials_[wall]);
+ }
+}
+
+// Tests that if the scene is a unit sphere, then the estimated proxy room
+// is a cube whose center is the same as the sphere. The dimensions cannot
+// be exactly the same as the sphere, but can be expected to be within
+// a range.
+TEST_F(ProxyRoomEstimatorTest, EstimateCubeFromUnitSphereScene) {
+ const size_t min_num_paths = 1000;
+ std::vector<Path> paths =
+ GenerateUniformlyDistributedRayPaths(kSourcePosition, min_num_paths);
+ ReflectRaysInPaths(1.0f, &paths);
+
+ estimator_.CollectHitPointData(paths);
+
+ const float outlier_portion = 0.0f;
+ RoomProperties room_properties;
+ EXPECT_TRUE(
+ estimator_.EstimateCubicProxyRoom(outlier_portion, &room_properties));
+
+ // Expect the estimated proxy room is centered at the source position.
+ const float position_tolerance = 0.005f;
+ ExpectFloat3Close(room_properties.position, kSourcePosition,
+ position_tolerance);
+
+ // Because the unit sphere is not a cube, the estimated proxy room will have
+ // dimensions between
+ // 1. the smallest cube enclosing the sphere, and
+ // 2. the largest cube inside the sphere.
+ // Specifically, the proxy room will have dimensions between 1.0 and 2.0, or
+ // 1.5 +- 0.5.
+ const float expected_dimensions[3] = {1.5f, 1.5f, 1.5f};
+ const float dimensions_tolerance = 0.5f;
+ ExpectFloat3Close(room_properties.dimensions, expected_dimensions,
+ dimensions_tolerance);
+
+ // Expect that the estimated surface materials are all kConcreteBlockCoarse,
+ // which is closest to absorbing half of the energy across all frequency
+ // bands.
+ for (size_t wall = 0; wall < 6; ++wall) {
+ EXPECT_EQ(room_properties.material_names[wall],
+ MaterialName::kConcreteBlockCoarse);
+ }
+}
+
+// Tests that if the scene is a cube with some openings (no walls in some
+// directions), then the estimated proxy room will have transparent (fully
+// absorbent) walls corresponding to those directions.
+TEST_F(ProxyRoomEstimatorTest, EstimateOpenWallsAsTransparent) {
+ // Remove the last four triangles (corresponding to the two walls facing
+ // the -z and +z directions) from the cube scene.
+ cube_triangles_.pop_back();
+ cube_triangles_.pop_back();
+ cube_triangles_.pop_back();
+ cube_triangles_.pop_back();
+
+ wall_materials_ = std::vector<MaterialName>{
+ MaterialName::kAcousticCeilingTiles,
+ MaterialName::kWoodPanel,
+ MaterialName::kConcreteBlockPainted,
+ MaterialName::kGlassThin,
+ };
+
+ cube_wall_triangles_.pop_back();
+ cube_wall_triangles_.pop_back();
+
+ // Trace rays in the semi-open cube scene.
+ std::vector<Path> paths = TracePathsInTestcene(
+ kNumRays, kMaxDepth, kEnergyThresold, cube_center_, cube_vertices_,
+ cube_triangles_, cube_wall_triangles_, wall_materials_, &scene_manager_);
+
+ // Estimate a proxy room.
+ estimator_.CollectHitPointData(paths);
+ const float outlier_portion = 0.0f;
+ RoomProperties room_properties;
+ EXPECT_TRUE(
+ estimator_.EstimateCubicProxyRoom(outlier_portion, &room_properties));
+
+ // Expect that the estimated proxy room is the same as the original cube.
+ const float position_tolerance = 0.005f;
+ ExpectFloat3Close(room_properties.position, cube_center_, position_tolerance);
+ const float expected_dimensions[3] = {1.0f, 1.0f, 1.0f};
+ const float dimensions_tolerance = 0.001f;
+ ExpectFloat3Close(room_properties.dimensions, expected_dimensions,
+ dimensions_tolerance);
+
+ // Expect that the estimated surface materials are the same as the original
+ // cube for the four remaining walls.
+ for (size_t wall = 0; wall < 4; ++wall) {
+ EXPECT_EQ(room_properties.material_names[wall], wall_materials_[wall]);
+ }
+
+ // Expect that the estimated surface material of the walls corresponding
+ // to the open directions as transparent, because rays in these directions
+ // all escape and are considered completely absorbed.
+ for (size_t wall = 4; wall < 6; ++wall) {
+ EXPECT_EQ(room_properties.material_names[wall], MaterialName::kTransparent);
+ }
+}
+
+// Tests if the scene is a cube, but some hit points are too far or too close
+// to the origin, those points can be considered as outliers (if a proper
+// |outlier_portion| is set) and will not affect the estimation result.
+// Therefore the estimated proxy room is still the same cube.
+TEST_F(ProxyRoomEstimatorTest, EstimateCubeIgnoringOutliers) {
+ wall_materials_ = std::vector<MaterialName>{
+ MaterialName::kAcousticCeilingTiles,
+ MaterialName::kWoodPanel,
+ MaterialName::kConcreteBlockPainted,
+ MaterialName::kGlassThin,
+ MaterialName::kGrass,
+ MaterialName::kMarble,
+ };
+
+ // Trace rays in a cube scene.
+ std::vector<Path> paths = TracePathsInTestcene(
+ kNumRays, kMaxDepth, kEnergyThresold, cube_center_, cube_vertices_,
+ cube_triangles_, cube_wall_triangles_, wall_materials_, &scene_manager_);
+
+ // Modify the traced paths so that some hit points are too close or too far.
+ const size_t num_points_too_close = 100;
+ const float distance_too_close = 0.1f;
+ for (size_t i = 0; i < num_points_too_close; ++i) {
+ paths[0].rays[0].tfar = distance_too_close;
+ }
+ const size_t num_points_too_far = 100;
+ const float distance_too_far = 10.0f;
+ for (size_t i = num_points_too_close;
+ i < num_points_too_far + num_points_too_close; ++i) {
+ paths[0].rays[0].tfar = distance_too_far;
+ }
+
+ // Estimate a proxy room with a |outlier_portion| carefully chosen such that
+ // the hit points too close or too far do not affect the estimation result.
+ estimator_.CollectHitPointData(paths);
+ const float outlier_portion =
+ static_cast<float>(num_points_too_close + num_points_too_far) /
+ static_cast<float>(paths.size()) / 2.0f;
+ RoomProperties room_properties;
+ EXPECT_TRUE(
+ estimator_.EstimateCubicProxyRoom(outlier_portion, &room_properties));
+
+ // Expect that the estimated proxy room is still the same as the original
+ // cube, even in the presence of the hit points too close or too far.
+ const float position_tolerance = 0.05f;
+ ExpectFloat3Close(room_properties.position, cube_center_, position_tolerance);
+ const float expected_dimensions[3] = {1.0f, 1.0f, 1.0f};
+ const float dimensions_tolerance = 0.01f;
+ ExpectFloat3Close(room_properties.dimensions, expected_dimensions,
+ dimensions_tolerance);
+
+ // Expect that the estimated surface materials are the same as the original
+ // cube.
+ for (size_t wall = 0; wall < 6; ++wall) {
+ EXPECT_EQ(room_properties.material_names[wall], wall_materials_[wall]);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/reflection_kernel.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/reflection_kernel.cc
new file mode 100644
index 000000000..f55f2eb72
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/reflection_kernel.cc
@@ -0,0 +1,99 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/reflection_kernel.h"
+
+#include "base/logging.h"
+#include "geometrical_acoustics/sampling.h"
+
+namespace vraudio {
+
+using Eigen::Vector3f;
+
+AcousticRay ReflectionKernel::Reflect(const AcousticRay& incident_ray) const {
+ // This function currently uses |random_number_generator| (which returns
+ // uniformly distributed numbers) to naively sample 1D and 2D points.
+
+ AcousticRay::RayType reflected_ray_type =
+ (random_number_generator_() >= scattering_coefficient_)
+ ? AcousticRay::RayType::kSpecular
+ : AcousticRay::RayType::kDiffuse;
+
+ // Compute the reflected direction.
+ Vector3f reflected_direction;
+ const Vector3f& unit_normal =
+ Vector3f(incident_ray.intersected_geometry_normal()).normalized();
+ const Vector3f& incident_direction = Vector3f(incident_ray.direction());
+ switch (reflected_ray_type) {
+ case AcousticRay::RayType::kSpecular:
+ reflected_direction =
+ incident_direction -
+ 2.0f * incident_direction.dot(unit_normal) * unit_normal;
+ break;
+ case AcousticRay::RayType::kDiffuse:
+ // A Lambertian reflection.
+ reflected_direction = CosineSampleHemisphere(
+ random_number_generator_(), random_number_generator_(), unit_normal);
+ break;
+ }
+ const Vector3f reflected_origin = Vector3f(incident_ray.origin()) +
+ incident_ray.t_far() * incident_direction;
+ const float reflected_ray_prior_distance =
+ incident_ray.prior_distance() +
+ incident_ray.t_far() * incident_direction.norm();
+
+ // New energies for each frequency band.
+ CHECK_EQ(reflection_coefficients_.size(), incident_ray.energies().size());
+ std::array<float, kNumReverbOctaveBands> new_energies =
+ incident_ray.energies();
+
+ for (size_t i = 0; i < new_energies.size(); ++i) {
+ new_energies[i] *= reflection_coefficients_[i];
+ }
+
+ return AcousticRay(reflected_origin.data(), reflected_direction.data(),
+ AcousticRay::kRayEpsilon, AcousticRay::kInfinity,
+ new_energies, reflected_ray_type,
+ reflected_ray_prior_distance);
+}
+
+void ReflectionKernel::ReflectDiffuseRain(
+ const AcousticRay& incident_ray, const AcousticRay& reference_reflected_ray,
+ const Eigen::Vector3f& listener_position, float* direction_pdf,
+ AcousticRay* diffuse_rain_ray) const {
+ const Vector3f reflection_point_to_listener =
+ listener_position - Vector3f(reference_reflected_ray.origin());
+ const Vector3f reflection_point_to_listener_direction =
+ reflection_point_to_listener.normalized();
+ const float diffuse_rain_ray_t_far =
+ reflection_point_to_listener.norm() + reference_reflected_ray.t_near();
+
+ *direction_pdf = CosineSampleHemispherePdf(
+ Vector3f(incident_ray.intersected_geometry_normal()).normalized(),
+ reflection_point_to_listener_direction);
+
+ diffuse_rain_ray->set_origin(reference_reflected_ray.origin());
+ diffuse_rain_ray->set_direction(
+ reflection_point_to_listener_direction.data());
+ diffuse_rain_ray->set_t_near(reference_reflected_ray.t_near());
+ diffuse_rain_ray->set_t_far(diffuse_rain_ray_t_far);
+ diffuse_rain_ray->set_energies(reference_reflected_ray.energies());
+ diffuse_rain_ray->set_type(reference_reflected_ray.type());
+ diffuse_rain_ray->set_prior_distance(
+ reference_reflected_ray.prior_distance());
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/reflection_kernel.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/reflection_kernel.h
new file mode 100644
index 000000000..10b34d5c7
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/reflection_kernel.h
@@ -0,0 +1,104 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_REFLECTION_KERNEL_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_REFLECTION_KERNEL_H_
+
+#include <array>
+#include <functional>
+#include <utility>
+
+#include "Eigen/Core"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "geometrical_acoustics/acoustic_ray.h"
+
+namespace vraudio {
+
+// A class modeling reflections of acoustic rays. It takes in an incident ray
+// and computes the corresponding reflected ray. NOTE: not to be confused with
+// vraudio:Reflection, which models the reflection properties of a room.
+//
+// This class handles specular and diffuse reflections, randomly chosen based
+// on the scattering coefficient. Besides, a certain portion of the energy is
+// absorbed based on the absorption coefficient.
+//
+
+class ReflectionKernel {
+ public:
+ // Constructor.
+ //
+ // @param absorption_coefficients Fraction of energy being absorbed for each
+ // frequency band. All values must be in [0.0, 1.0].
+ // @param scattering_coefficient Fraction of energy being scattered
+ // (diffusely reflected). Must be in [0.0, 1.0].
+ // @param random_number_generator Random number generator to randomly select
+ // reflection types as well as ray directions if needed. It should
+ // implement operator() that returns a random value in [0.0, 1.0).
+ ReflectionKernel(
+ const std::array<float, kNumReverbOctaveBands>& absorption_coefficients,
+ float scattering_coefficient,
+ std::function<float()> random_number_generator)
+ : scattering_coefficient_(scattering_coefficient),
+ random_number_generator_(std::move(random_number_generator)) {
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ reflection_coefficients_[i] = 1.0f - absorption_coefficients[i];
+ CHECK(reflection_coefficients_[i] >= 0.0f &&
+ reflection_coefficients_[i] <= 1.0f);
+ }
+ CHECK(scattering_coefficient_ >= 0.0f && scattering_coefficient_ <= 1.0f);
+ }
+
+ // Reflects a ray.
+ //
+ // @param incident_ray Incident (incoming) ray.
+ // @return Exitant (outgoing) ray, whose origin is on the intersection point,
+ // with direction and energy calculated based on the type of reflection
+ // and the incident ray.
+ AcousticRay Reflect(const AcousticRay& incident_ray) const;
+
+ // Reflects an incident ray to create a diffuse-rain ray, whose |direction|
+ // is from the reflection point to the listener position, and whose |t_far|
+ // is the distance between the reflection point and the listener position.
+ // All the other data are the copied from the provided
+ // |reference_reflected_ray|.
+ //
+ // @param incident_ray Incident ray.
+ // @param reflected_ray Reference reflected ray, from which the output
+ // diffuse-rain ray copies all the data except for |direction| and
+ // |t_far|.
+ // @param listener_position Listener position.
+ // @param direction_pdf Output probability density function that a reflected
+ // ray is in the direction of the diffuse-rain ray. Will be used to
+ // modulate the energy contribution from the diffuse-rain ray.
+ // @param diffuse_rain_ray Output diffuse-rain ray.
+ void ReflectDiffuseRain(const AcousticRay& incident_ray,
+ const AcousticRay& reference_reflected_ray,
+ const Eigen::Vector3f& listener_position,
+ float* direction_pdf,
+ AcousticRay* diffuse_rain_ray) const;
+
+ private:
+ std::array<float, kNumReverbOctaveBands> reflection_coefficients_;
+ const float scattering_coefficient_;
+
+ // Randon number generator for sampling reflection types and ray directions.
+ std::function<float()> random_number_generator_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_REFLECTION_KERNEL_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/reflection_kernel_test.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/reflection_kernel_test.cc
new file mode 100644
index 000000000..bf1806996
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/reflection_kernel_test.cc
@@ -0,0 +1,300 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/reflection_kernel.h"
+
+#include <cmath>
+#include <functional>
+#include <random>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "Eigen/Dense"
+#include "geometrical_acoustics/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+using Eigen::Quaternionf;
+using Eigen::Vector3f;
+
+class ReflectionKernelTest : public testing::Test {
+ public:
+ ReflectionKernelTest()
+ : incident_ray_(Vector3f(0.0f, 0.0f, 0.0f).data(),
+ Vector3f(1.0f, 1.0f, 1.0f).normalized().data(), 0.0f,
+ AcousticRay::kInfinity, kEnergies,
+ AcousticRay::RayType::kSpecular, 0.0f),
+ random_engine_(0),
+ distribution_(0.0f, 1.0f) {}
+
+ void SetUp() override {
+ // Reseed the random number generator for every test to be deterministic
+ // and remove flakiness in tests.
+ random_engine_.seed(0);
+ random_number_generator_ = [this] { return distribution_(random_engine_); };
+ }
+
+ protected:
+ const std::array<float, kNumReverbOctaveBands> kEnergies = {
+ {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f}};
+ const std::array<float, kNumReverbOctaveBands> kUnitAbsorptionCoefficients = {
+ {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}};
+ const std::array<float, kNumReverbOctaveBands> kZeroAbsorptionCoefficients = {
+ {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}};
+
+ AcousticRay incident_ray_;
+ std::default_random_engine random_engine_;
+ std::uniform_real_distribution<float> distribution_;
+ std::function<float()> random_number_generator_;
+};
+
+TEST_F(ReflectionKernelTest, EnergyAbsorptionTest) {
+ const std::array<float, kNumReverbOctaveBands>& original_energies =
+ incident_ray_.energies();
+ {
+ // An absorption coefficient of 1 means that no energy is reflected.
+ const ReflectionKernel reflection(kUnitAbsorptionCoefficients, 0.0f,
+ random_number_generator_);
+ const AcousticRay reflected_ray = reflection.Reflect(incident_ray_);
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ EXPECT_FLOAT_EQ(reflected_ray.energies().at(i), 0.0f);
+ }
+ }
+ {
+ // An absorption coefficient of 0 means that all energy is reflected.
+ const ReflectionKernel reflection(kZeroAbsorptionCoefficients, 0.0f,
+ random_number_generator_);
+ const AcousticRay reflected_ray = reflection.Reflect(incident_ray_);
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ EXPECT_FLOAT_EQ(reflected_ray.energies().at(i), original_energies.at(i));
+ }
+ }
+ {
+ // An absorption coefficient of x means that only (1.0 - x) of the energy
+ // would be reflected.
+ std::array<float, kNumReverbOctaveBands> absorption_coefficients = {
+ {0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f}};
+ const ReflectionKernel reflection(absorption_coefficients, 0.0f,
+ random_number_generator_);
+ const AcousticRay reflected_ray = reflection.Reflect(incident_ray_);
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ EXPECT_FLOAT_EQ(
+ reflected_ray.energies().at(i),
+ (1.0f - absorption_coefficients.at(i)) * original_energies.at(i));
+ }
+ }
+}
+
+TEST_F(ReflectionKernelTest, ScatteringCoefficientTest) {
+ // Setting the scattering coefficient to 0.3 means 30% of the rays are
+ // reflected diffusely while 70% are reflected specularly.
+ const ReflectionKernel reflection(kUnitAbsorptionCoefficients, 0.3f,
+ random_number_generator_);
+
+ float specular_fraction = 0.0f;
+ float diffuse_fraction = 0.0f;
+ const int num_samples = 10000;
+ const float fraction_per_sample = 1.0f / static_cast<float>(num_samples);
+ for (int i = 0; i < num_samples; ++i) {
+ const AcousticRay reflected_ray = reflection.Reflect(incident_ray_);
+ (reflected_ray.type() == AcousticRay::RayType::kDiffuse
+ ? diffuse_fraction
+ : specular_fraction) += fraction_per_sample;
+ }
+ EXPECT_NEAR(diffuse_fraction, 0.3f, 0.01f);
+ EXPECT_NEAR(specular_fraction, 0.7f, 0.01f);
+}
+
+TEST_F(ReflectionKernelTest, MultipleReflectionsTest) {
+ const ReflectionKernel reflection(kUnitAbsorptionCoefficients, 0.0f,
+ random_number_generator_);
+
+ // Reflect |num_reflections| times. Validate that each reflected ray starts
+ // where its incident ray ends. This is to simulate the effect of a typical
+ // path tracer.
+ const size_t num_reflections = 5;
+ const float t_between_intersections = 2.0f;
+ const float distance_between_intersections = t_between_intersections *
+ Vector3f(incident_ray_.direction()).norm();
+
+ AcousticRay ray = incident_ray_;
+ float expected_prior_distance = 0.0f;
+ for (size_t i = 0; i < num_reflections; ++i) {
+ // Emulate an intersection at t = 2, with a plane whose (unnormalized)
+ // normal being (0, 0, -1) for even iterations and (0, 0, 1) for odd
+ // iterations.
+ ray.set_t_far(t_between_intersections);
+ const float even_normal[3] = {0.0f, 0.0f, -1.0f};
+ const float odd_normal[3] = {0.0f, 0.0f, 1.0f};
+ ray.set_intersected_geometry_normal((i % 2 == 0) ? even_normal
+ : odd_normal);
+ const AcousticRay reflected_ray = reflection.Reflect(ray);
+
+ // Check that the reflected ray starts where the original ray ends.
+ const Vector3f& expected_reflected_ray_origin =
+ Vector3f(ray.origin()) + ray.t_far() * Vector3f(ray.direction());
+ ExpectFloat3Close(reflected_ray.origin(),
+ expected_reflected_ray_origin.data());
+ EXPECT_FLOAT_EQ(reflected_ray.t_near(), AcousticRay::kRayEpsilon);
+
+ // Check that the |prior_distance| field accumulates the distance traveled.
+ expected_prior_distance += distance_between_intersections;
+ EXPECT_FLOAT_EQ(reflected_ray.prior_distance(), expected_prior_distance);
+
+ // Continue tracing the reflected ray.
+ ray = reflected_ray;
+ }
+}
+
+TEST_F(ReflectionKernelTest, SpecularReflectionTest) {
+ // Emulate an intersection at t = 2, with a plane whose (unnormalized) normal
+ // is (-1, -1, 0).
+ incident_ray_.set_t_far(2.0f);
+ const float normal[3] = {-1.0f, -1.0f, 0.0f};
+ incident_ray_.set_intersected_geometry_normal(normal);
+
+ // Setting the scattering coefficient to zero means to always reflect
+ // specularly.
+ const ReflectionKernel reflection(kUnitAbsorptionCoefficients, 0.0f,
+ random_number_generator_);
+ const AcousticRay reflected_ray = reflection.Reflect(incident_ray_);
+
+ // Validate non-directional data.
+ EXPECT_FLOAT_EQ(reflected_ray.t_near(), AcousticRay::kRayEpsilon);
+ EXPECT_EQ(reflected_ray.type(), AcousticRay::RayType::kSpecular);
+ const float expected_reflected_origin[3] = {1.15470053838f, 1.15470053838f,
+ 1.15470053838f};
+ ExpectFloat3Close(reflected_ray.origin(), expected_reflected_origin);
+
+ // Validate the reflected direction.
+ const Vector3f& expected_reflected_direction =
+ Vector3f(-1.0f, -1.0f, 1.0f).normalized();
+ ExpectFloat3Close(reflected_ray.direction(),
+ expected_reflected_direction.data());
+}
+
+TEST_F(ReflectionKernelTest, DiffuseReflectionTest) {
+ // Emulate an intersection at t = 2, with a plane whose (unnormalized) normal
+ // is (-1, -1, 0).
+ incident_ray_.set_t_far(2.0f);
+ const float normal[3] = {-1.0f, -1.0f, 0.0f};
+ incident_ray_.set_intersected_geometry_normal(normal);
+
+ // Setting the scattering coefficient to 1.0 means to always reflect
+ // diffusely.
+ const ReflectionKernel reflection(kUnitAbsorptionCoefficients, 1.0f,
+ random_number_generator_);
+ const AcousticRay reflected_ray = reflection.Reflect(incident_ray_);
+
+ // Validate non-directional data.
+ EXPECT_FLOAT_EQ(reflected_ray.t_near(), AcousticRay::kRayEpsilon);
+ EXPECT_EQ(reflected_ray.type(), AcousticRay::RayType::kDiffuse);
+
+ // Validate the reflected directions.
+ // For a reflected ray whose direction distribution is cosine-weighted over a
+ // hemisphere:
+ // - The PDF of theta is 2 sin(theta) cos(theta), and the CDF is sin^2(theta).
+ // - The PDF of phi is 1 / 2 pi, and the CDF is 0.5 + phi / 2 pi.
+ const Vector3f& plane_unit_normal =
+ Vector3f(incident_ray_.intersected_geometry_normal()).normalized();
+ ValidateDistribution(100000, 100, [&reflection, &plane_unit_normal, this]() {
+ const AcousticRay reflected_ray = reflection.Reflect(incident_ray_);
+ const Vector3f& local_direction =
+ Quaternionf::FromTwoVectors(plane_unit_normal, Vector3f::UnitZ()) *
+ Vector3f(reflected_ray.direction());
+ const float cos_theta = local_direction.z();
+ const float sin_theta_square = 1.0f - cos_theta * cos_theta;
+ return sin_theta_square;
+ });
+
+ ValidateDistribution(100000, 100, [&reflection, &plane_unit_normal, this]() {
+ const AcousticRay reflected_ray = reflection.Reflect(incident_ray_);
+ const Vector3f& local_direction =
+ Quaternionf::FromTwoVectors(plane_unit_normal, Vector3f::UnitZ()) *
+ Vector3f(reflected_ray.direction());
+ const float phi = std::atan2(local_direction.y(), local_direction.x());
+ return 0.5f + phi / 2.0f / static_cast<float>(M_PI);
+ });
+}
+
+TEST_F(ReflectionKernelTest, DiffuseRainReflectionTest) {
+ // Emulate an intersection at t = 2, with a plane whose (unnormalized) normal
+ // is (-1, -1, 0).
+ incident_ray_.set_t_far(2.0f);
+ const float normal[3] = {-1.0f, -1.0f, 0.0f};
+ incident_ray_.set_intersected_geometry_normal(normal);
+
+ // Setting the scattering coefficient to 1.0 means to always reflect
+ // diffusely.
+ const ReflectionKernel reflection(kUnitAbsorptionCoefficients, 1.0f,
+ random_number_generator_);
+ const AcousticRay reference_reflected_ray = reflection.Reflect(incident_ray_);
+
+ // Generate a diffuse-rain ray for a listener on the same side of the plane.
+ float direction_pdf = -1.0f;
+ const Vector3f same_side_listener_position(0.0f, 0.0f, 2.0f);
+ AcousticRay diffuse_rain_ray;
+ reflection.ReflectDiffuseRain(incident_ray_, reference_reflected_ray,
+ same_side_listener_position, &direction_pdf,
+ &diffuse_rain_ray);
+
+ // The direction of the diffuse-rain ray should be from the reflection point
+ // (the origin of the |reference_reflected_ray|) to the listener position.
+ const Vector3f reflection_point_to_listener_position =
+ same_side_listener_position - Vector3f(reference_reflected_ray.origin());
+ const Vector3f expected_direction =
+ reflection_point_to_listener_position.normalized();
+ ExpectFloat3Close(diffuse_rain_ray.direction(), expected_direction.data());
+
+ // The output |direction_pdf| should be cos(theta) / pi, where theta is the
+ // angle between the normal of the reflecting surface and the expected
+ // direction.
+ const float cos_theta = expected_direction.dot(Vector3f(normal).normalized());
+ EXPECT_FLOAT_EQ(direction_pdf, cos_theta / kPi);
+
+ // Verify that the |t_far| - |t_near| is the distance between the reflection
+ // point and the listener position.
+ EXPECT_FLOAT_EQ(diffuse_rain_ray.t_far() - diffuse_rain_ray.t_near(),
+ reflection_point_to_listener_position.norm());
+
+ // Verify that other data are the same as |reference_reflected_ray|.
+ ExpectFloat3Close(diffuse_rain_ray.origin(),
+ reference_reflected_ray.origin());
+ EXPECT_FLOAT_EQ(diffuse_rain_ray.t_near(), reference_reflected_ray.t_near());
+ ExpectFloat3Close(diffuse_rain_ray.intersected_geometry_normal(),
+ reference_reflected_ray.intersected_geometry_normal());
+ for (size_t band = 0; band < kNumReverbOctaveBands; ++band) {
+ EXPECT_FLOAT_EQ(diffuse_rain_ray.energies()[band],
+ reference_reflected_ray.energies()[band]);
+ }
+ EXPECT_EQ(diffuse_rain_ray.type(), reference_reflected_ray.type());
+ EXPECT_FLOAT_EQ(diffuse_rain_ray.prior_distance(),
+ reference_reflected_ray.prior_distance());
+
+ // Generate a diffuse-rain ray for a listener on the different side of the
+ // plane and expect that the output |direction_pdf| is zero, meaning there is
+ // no chance that the diffuse-rain ray is in that direction.
+ const Vector3f different_side_listener_position(10.0f, 10.0f, 10.0f);
+ reflection.ReflectDiffuseRain(incident_ray_, reference_reflected_ray,
+ different_side_listener_position,
+ &direction_pdf, &diffuse_rain_ray);
+ EXPECT_FLOAT_EQ(direction_pdf, 0.0f);
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sampling.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sampling.h
new file mode 100644
index 000000000..99ace88d5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sampling.h
@@ -0,0 +1,126 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Collection of sampling functions useful for in stochastic ray tracing.
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_SAMPLING_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_SAMPLING_H_
+
+#include <cmath>
+
+#include "Eigen/Dense"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+// Samples a vector uniformly from a unit sphere given two random variables
+// whose values are in [0, 1).
+//
+// @param u The first random variable.
+// @param v The second random variable.
+// @return The sampled vector.
+inline Eigen::Vector3f UniformSampleSphere(float u, float v) {
+ DCHECK(u >= 0.0f && u <= 1.0f);
+ DCHECK(v >= 0.0f && v <= 1.0f);
+ const float cos_theta = 1.0f - 2.0f * v;
+ const float sin_theta = 2.0f * std::sqrt(v * (1.0f - v));
+ const float phi = kTwoPi * u;
+ return Eigen::Vector3f(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta,
+ cos_theta);
+}
+
+// Samples a vector from a unit sphere in a stratified fashion given two
+// random variables whose values are in [0, 1). The collective results from
+// |sample_index| iterating from 0 to |sqrt_num_samples|^2 are uniformly
+// distributed on a sphere.
+//
+// @param u The first random variable.
+// @param v The second random variable.
+// @param sqrt_num_samples The square root of the total number of samples.
+// @param sample_index The index of the currently sampled vector.
+// @return The sampled vector.
+inline Eigen::Vector3f StratifiedSampleSphere(float u, float v,
+ size_t sqrt_num_samples,
+ size_t sample_index) {
+ DCHECK(u >= 0.0f && u <= 1.0f);
+ DCHECK(v >= 0.0f && v <= 1.0f);
+
+ // Make a domain that have |sqrt_num_samples| by |sqrt_num_samples| cells.
+ // First decide which cell that this sample is in, denoted by the coordinates
+ // of the cell's top-left of corner.
+ const float cell_x = static_cast<float>(sample_index / sqrt_num_samples);
+ const float cell_y = static_cast<float>(sample_index % sqrt_num_samples);
+
+ // Then pick a point inside the cell using the two random variables u and v.
+ // Normalize the point to the [0, 1) x [0, 1) domain and send it to
+ // UniformSampleSphere().
+ const float cell_width = 1.0f / static_cast<float>(sqrt_num_samples);
+ return UniformSampleSphere((cell_x + u) * cell_width,
+ (cell_y + v) * cell_width);
+}
+
+// Samples a vector from a unit hemisphere according to the cosine-weighted
+// distribution given two random variables whose values are in [0, 1). The
+// hemisphere lies on a plane whose normal is assumed to be on the +z direction.
+//
+
+static Eigen::Vector3f CosineSampleHemisphere(float u, float v) {
+ DCHECK(u >= 0.0f && u <= 1.0f);
+ DCHECK(v >= 0.0f && v <= 1.0f);
+ const float cos_theta = std::sqrt(1.0f - v);
+ const float sin_theta = std::sqrt(v);
+ const float phi = kTwoPi * u;
+ return Eigen::Vector3f(std::cos(phi) * sin_theta, std::sin(phi) * sin_theta,
+ cos_theta);
+}
+
+// Same as above but the hemisphere lies on a plane whose normal is specified
+// by the user (instead of the +z direction).
+//
+// @param u The first random variable.
+// @param v The second random variable.
+// @param unit_normal The normal of the plane on which the hemisphere resides.
+// @return The sampled vector.
+static Eigen::Vector3f CosineSampleHemisphere(
+ float u, float v, const Eigen::Vector3f& unit_normal) {
+ Eigen::Vector3f local_vector = CosineSampleHemisphere(u, v);
+
+ return Eigen::Quaternionf::FromTwoVectors(Eigen::Vector3f::UnitZ(),
+ unit_normal) *
+ local_vector;
+}
+
+// The probability density function (PDF) that a vector is in a particular
+// direction if the vector is sampled from a cosine-weigted distribution over
+// a unit hemisphere (i.e. it is sampled from the CosineSampleHemisphere()
+// above).
+//
+// @param unit_normal The normal of the plane on which the hemisphere resides.
+// @param unit_direction The direction of the sampled vector.
+// @return Probability density that a sampled vector is in the |unit_direction|.
+static float CosineSampleHemispherePdf(const Eigen::Vector3f& unit_normal,
+ const Eigen::Vector3f& unit_direction) {
+ const float cos_theta = unit_normal.dot(unit_direction);
+
+ // Returns zero probability if the |unit_normal| and |unit_direction| lie on
+ // different sides of the plane, i.e., their inner-product is negative.
+ return cos_theta >= 0.0f ? cos_theta / kPi : 0.0f;
+}
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_SAMPLING_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/scene_manager.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/scene_manager.cc
new file mode 100644
index 000000000..38baeb7ab
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/scene_manager.cc
@@ -0,0 +1,153 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/scene_manager.h"
+
+#include "base/aligned_allocator.h"
+#include "geometrical_acoustics/sphere.h"
+
+namespace vraudio {
+
+namespace {
+
+// A function adapter from SphereBounds() to RTCBoundsFunc in order to be
+// passed to rtcSetBoundsFunction().
+// The signature of RTCBoundsFunc does not comply with Google's C++ style,
+
+static void EmbreeSphereBoundsFunction(void* user_data, size_t index,
+ RTCBounds& output_bounds
+) {
+ Sphere* spheres = static_cast<Sphere*>(user_data);
+ const Sphere& sphere = spheres[index];
+ SphereBounds(sphere, &output_bounds);
+}
+
+// A function adapter from SphereIntersections() to RTCIntersectFunc in order
+// to be passed to rtcSetIntersectFunction().
+// The signature of RTCIntersectFunc does not comply with Google's C++ style,
+
+static void EmbreeSphereIntersectFunction(void* user_data,
+ RTCRay& ray,
+ size_t index) {
+ Sphere* const spheres = static_cast<Sphere*>(user_data);
+ const Sphere& sphere = spheres[index];
+ SphereIntersection(sphere, &ray);
+}
+
+} // namespace
+
+SceneManager::SceneManager() {
+ // Use a single RTCDevice for all scenes.
+ device_ = rtcNewDevice(nullptr);
+ CHECK_NOTNULL(device_);
+ scene_ = rtcDeviceNewScene(device_, RTC_SCENE_STATIC | RTC_SCENE_HIGH_QUALITY,
+ RTC_INTERSECT1);
+ listener_scene_ = rtcDeviceNewScene(
+ device_, RTC_SCENE_STATIC | RTC_SCENE_HIGH_QUALITY, RTC_INTERSECT1);
+}
+
+SceneManager::~SceneManager() {
+ rtcDeleteScene(scene_);
+ rtcDeleteScene(listener_scene_);
+ rtcDeleteDevice(device_);
+}
+
+void SceneManager::BuildScene(const std::vector<Vertex>& vertex_buffer,
+ const std::vector<Triangle>& triangle_buffer) {
+ num_vertices_ = vertex_buffer.size();
+ num_triangles_ = triangle_buffer.size();
+ unsigned int mesh_id = rtcNewTriangleMesh(scene_, RTC_GEOMETRY_STATIC,
+ num_triangles_, num_vertices_);
+ struct EmbreeVertex {
+ // Embree uses 4 floats for each vertex for alignment. The last value
+ // is for padding only.
+ float x, y, z, a;
+ };
+ EmbreeVertex* const embree_vertex_array = static_cast<EmbreeVertex*>(
+ rtcMapBuffer(scene_, mesh_id, RTC_VERTEX_BUFFER));
+ for (size_t i = 0; i < num_vertices_; ++i) {
+ embree_vertex_array[i].x = vertex_buffer[i].x;
+ embree_vertex_array[i].y = vertex_buffer[i].y;
+ embree_vertex_array[i].z = vertex_buffer[i].z;
+ }
+ rtcUnmapBuffer(scene_, mesh_id, RTC_VERTEX_BUFFER);
+
+ // Triangles. Somehow Embree is a left-handed system, so we re-order all
+ // triangle indices here, i.e. {v0, v1, v2} -> {v0, v2, v1}.
+ int* const embree_index_array =
+ static_cast<int*>(rtcMapBuffer(scene_, mesh_id, RTC_INDEX_BUFFER));
+ for (size_t i = 0; i < num_triangles_; ++i) {
+ embree_index_array[3 * i + 0] = triangle_buffer[i].v0;
+ embree_index_array[3 * i + 1] = triangle_buffer[i].v2;
+ embree_index_array[3 * i + 2] = triangle_buffer[i].v1;
+ }
+ rtcUnmapBuffer(scene_, mesh_id, RTC_INDEX_BUFFER);
+ rtcCommit(scene_);
+ is_scene_committed_ = true;
+}
+
+bool SceneManager::AssociateReflectionKernelToTriangles(
+ const ReflectionKernel& reflection,
+ const std::unordered_set<unsigned int>& triangle_indices) {
+ const size_t reflection_index = reflections_.size();
+ reflections_.push_back(reflection);
+ for (const unsigned int triangle_index : triangle_indices) {
+ if (triangle_index >= num_triangles_) {
+ return false;
+ }
+ triangle_to_reflection_map_[triangle_index] = reflection_index;
+ }
+ return true;
+}
+
+void SceneManager::BuildListenerScene(
+ const std::vector<AcousticListener>& listeners,
+ float listener_sphere_radius) {
+ rtcDeleteScene(listener_scene_);
+ listener_scene_ = rtcDeviceNewScene(
+ device_, RTC_SCENE_STATIC | RTC_SCENE_HIGH_QUALITY, RTC_INTERSECT1);
+
+ for (size_t listener_index = 0; listener_index < listeners.size();
+ ++listener_index) {
+ // Create a sphere per listener and add to |listener_scene_|.
+ const AcousticListener& listener = listeners.at(listener_index);
+ const unsigned int sphere_id = rtcNewUserGeometry(listener_scene_, 1);
+ Sphere* const sphere =
+ AllignedMalloc<Sphere, size_t, Sphere*>(sizeof(Sphere),
+ /*alignment=*/64);
+ sphere->center[0] = listener.position[0];
+ sphere->center[1] = listener.position[1];
+ sphere->center[2] = listener.position[2];
+ sphere->radius = listener_sphere_radius;
+ sphere->geometry_id = sphere_id;
+
+ // rtcSetUserData() takes ownership of |sphere|.
+ rtcSetUserData(listener_scene_, sphere_id, sphere);
+ rtcSetBoundsFunction(listener_scene_, sphere_id,
+ &EmbreeSphereBoundsFunction);
+ rtcSetIntersectFunction(listener_scene_, sphere_id,
+ &EmbreeSphereIntersectFunction);
+
+ // Associate the listener to |sphere_id| through its index in the vector
+ // of listeners.
+ sphere_to_listener_map_[sphere_id] = listener_index;
+ }
+
+ rtcCommit(listener_scene_);
+ is_listener_scene_committed_ = true;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/scene_manager.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/scene_manager.h
new file mode 100644
index 000000000..85d6eb5dd
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/scene_manager.h
@@ -0,0 +1,130 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_SCENE_MANAGER_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_SCENE_MANAGER_H_
+
+#include <functional>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "embree2/rtcore.h"
+#include "embree2/rtcore_ray.h"
+#include "base/logging.h"
+#include "geometrical_acoustics/acoustic_listener.h"
+#include "geometrical_acoustics/mesh.h"
+#include "geometrical_acoustics/reflection_kernel.h"
+
+namespace vraudio {
+
+// A class to manage a scene modeling an acoustic environment and a scene
+// modeling listeners.
+class SceneManager {
+ public:
+ SceneManager();
+ virtual ~SceneManager();
+
+ // Builds a scene with triangles describing the acoustic environment.
+ //
+ // @param vertex_buffer A vector of vertices.
+ // @param triangle_buffer A vector of triangles. The triangles should be
+ // right-handed, i.e. if a triangle is defined by {v0, v1, v2},
+ // then its normal is along the right-handed cross product of (v1 - v0)
+ // and (v2 - v0).
+ void BuildScene(const std::vector<Vertex>& vertex_buffer,
+ const std::vector<Triangle>& triangle_buffer);
+
+ // Associates a reflection kernel to a set of triangles.
+ //
+ // @param reflection Reflection kernel.
+ // @param triangle_indices Indices of triangles to be associated with
+ // the reflection .
+ // @return True on success.
+ bool AssociateReflectionKernelToTriangles(
+ const ReflectionKernel& reflection_kernel,
+ const std::unordered_set<unsigned int>& triangle_indices);
+
+ // Gets the reflection kernel associated to a triangle.
+ //
+ // @param triangle_index The index of the triangle to get the reflection
+ // kernel from.
+ // @return The reflection kernel associated to the triangle; a default
+ // reflection if no reflection kernel is associated to the triangle.
+ const ReflectionKernel& GetAssociatedReflectionKernel(
+ unsigned int triangle_index) const {
+ if (triangle_to_reflection_map_.count(triangle_index) == 0) {
+ return kDefaultReflection;
+ }
+ return reflections_.at(triangle_to_reflection_map_.at(triangle_index));
+ }
+
+ RTCScene scene() const { return scene_; }
+ bool is_scene_committed() const { return is_scene_committed_; }
+ size_t num_vertices() const { return num_vertices_; }
+ size_t num_triangles() const { return num_triangles_; }
+
+ // Builds a scene with only listener spheres, one sphere per listener.
+ //
+ // @param listeners Vector of AcousticListeners.
+ // @param listener_sphere_radius Radius of listener spheres (m).
+ void BuildListenerScene(const std::vector<AcousticListener>& listeners,
+ float listener_sphere_radius);
+
+ // Gets the listener index associated to a sphere id.
+ //
+ // @param sphere_id Sphere id.
+ // @return listener_index Listener index associated to the sphere id.
+ size_t GetListenerIndexFromSphereId(unsigned int sphere_id) {
+ DCHECK_GT(sphere_to_listener_map_.count(sphere_id), 0);
+ return sphere_to_listener_map_.at(sphere_id);
+ }
+
+ RTCScene listener_scene() const { return listener_scene_; }
+ bool is_listener_scene_committed() const {
+ return is_listener_scene_committed_;
+ }
+
+ private:
+ // Perfect absorption. No randomness needed.
+ const std::array<float, kNumReverbOctaveBands>
+ kPerfectReflectionCoefficients = {
+ {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}};
+ const ReflectionKernel kDefaultReflection = ReflectionKernel(
+ kPerfectReflectionCoefficients, 0.0f, []() { return 0.5f; });
+
+ // Embree device.
+ RTCDevice device_;
+
+ // Scene modeling an acoustic environment.
+ RTCScene scene_;
+ bool is_scene_committed_ = false;
+ size_t num_vertices_ = 0;
+ size_t num_triangles_ = 0;
+ std::vector<ReflectionKernel> reflections_;
+ std::unordered_map<unsigned int, size_t> triangle_to_reflection_map_;
+
+ // Scene with only listener spheres.
+ RTCScene listener_scene_;
+ bool is_listener_scene_committed_ = false;
+
+ // Map from a sphere's id to the index of a listener.
+ std::unordered_map<unsigned int, size_t> sphere_to_listener_map_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_SCENE_MANAGER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/scene_manager_test.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/scene_manager_test.cc
new file mode 100644
index 000000000..7810e100a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/scene_manager_test.cc
@@ -0,0 +1,202 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/scene_manager.h"
+
+#include <functional>
+#include <random>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "Eigen/Core"
+#include "geometrical_acoustics/acoustic_listener.h"
+#include "geometrical_acoustics/acoustic_ray.h"
+#include "geometrical_acoustics/reflection_kernel.h"
+#include "geometrical_acoustics/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+using Eigen::Vector3f;
+
+class SceneManagerTest : public testing::Test {
+ public:
+ SceneManagerTest() : random_engine_(0), distribution_(0.0f, 1.0f) {}
+
+ void SetUp() override {
+ ground_vertices_ = {{0.0f, 0.0f, 0.0f},
+ {0.0f, 1.0f, 0.0f},
+ {1.0f, 0.0f, 0.0f},
+ {1.0f, 1.0f, 0.0f}};
+ ground_triangles_ = {{0, 2, 1}, {1, 2, 3}};
+
+ // Reseed the random number generator for every test to be deterministic
+ // and remove flakiness in tests.
+ random_engine_.seed(0);
+ random_number_generator_ = [this] { return distribution_(random_engine_); };
+ }
+
+ protected:
+ const std::array<float, kNumReverbOctaveBands> kUnitEnergies{
+ {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}};
+ const std::array<float, kNumReverbOctaveBands> kZeroAbsorptionCoefficients{
+ {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}};
+ std::vector<Vertex> ground_vertices_;
+ std::vector<Triangle> ground_triangles_;
+ SceneManager scene_manager_;
+ std::default_random_engine random_engine_;
+ std::uniform_real_distribution<float> distribution_;
+ std::function<float()> random_number_generator_;
+};
+
+TEST_F(SceneManagerTest, UncommittedSceneTest) {
+ EXPECT_FALSE(scene_manager_.is_scene_committed());
+ EXPECT_FALSE(scene_manager_.is_listener_scene_committed());
+}
+
+TEST_F(SceneManagerTest, BuildEmptySceneTest) {
+ scene_manager_.BuildScene({}, {});
+ EXPECT_TRUE(scene_manager_.is_scene_committed());
+ EXPECT_EQ(scene_manager_.num_vertices(), 0U);
+ EXPECT_EQ(scene_manager_.num_triangles(), 0U);
+}
+
+TEST_F(SceneManagerTest, BuildSceneWithTwoTrianglesTest) {
+ scene_manager_.BuildScene(ground_vertices_, ground_triangles_);
+ EXPECT_TRUE(scene_manager_.is_scene_committed());
+ EXPECT_EQ(scene_manager_.num_vertices(), 4U);
+ EXPECT_EQ(scene_manager_.num_triangles(), 2U);
+}
+
+TEST_F(SceneManagerTest, ReturnDefaultReflectionIfNotAssociatedTest) {
+ scene_manager_.BuildScene(ground_vertices_, ground_triangles_);
+
+ // Get a reflection of triangle 0; since nothing has been associated to it,
+ // a default reflection is returned.
+ const ReflectionKernel& reflection =
+ scene_manager_.GetAssociatedReflectionKernel(0);
+
+ // Test that this default reflection absorbs all energy.
+ AcousticRay ray(Vector3f(0.0f, 0.0f, 0.0f).data(),
+ Vector3f(1.0f, 0.0f, 0.0f).data(), 0.0f, 1.0f, kUnitEnergies,
+ AcousticRay::RayType::kSpecular, 0.0f);
+ AcousticRay reflected_ray = reflection.Reflect(ray);
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ EXPECT_FLOAT_EQ(reflected_ray.energies().at(i), 0.0f);
+ }
+}
+
+TEST_F(SceneManagerTest, ReturnFalseAssociatingToNonExistentTriangleTest) {
+ scene_manager_.BuildScene(ground_vertices_, ground_triangles_);
+
+ // A non-absorptive, purely specular reflection.
+ ReflectionKernel reflection(kZeroAbsorptionCoefficients, 0.0f,
+ random_number_generator_);
+
+ // Associate the reflection to triangle 2, which does not exist.
+ EXPECT_FALSE(
+ scene_manager_.AssociateReflectionKernelToTriangles(reflection, {2}));
+}
+
+TEST_F(SceneManagerTest, ReturnAssociatedReflectionsTest) {
+ scene_manager_.BuildScene(ground_vertices_, ground_triangles_);
+
+ // A half-absorptive, purely diffuse reflection.
+ std::array<float, kNumReverbOctaveBands> absorption_coefficients;
+ absorption_coefficients.fill(0.5f);
+ ReflectionKernel reflection_1(absorption_coefficients, 1.0f,
+ random_number_generator_);
+
+ // A non-absorptive, purely specular reflection.
+ ReflectionKernel reflection_2(kZeroAbsorptionCoefficients, 0.0f,
+ random_number_generator_);
+
+ // Associate them to triangle 1 and 0 respectively.
+ EXPECT_TRUE(
+ scene_manager_.AssociateReflectionKernelToTriangles(reflection_1, {1}));
+ EXPECT_TRUE(
+ scene_manager_.AssociateReflectionKernelToTriangles(reflection_2, {0}));
+
+ AcousticRay ray(Vector3f(0.0f, 0.0f, 0.0f).data(),
+ Vector3f(1.0f, 0.0f, 0.0f).data(), 0.0f, 1.0f, kUnitEnergies,
+ AcousticRay::RayType::kSpecular, 0.0f);
+ {
+ // Test that the reflection from triangle 0, which should be |reflection_2|,
+ // absorbs none of the energy and reflects specularly.
+
+ const ReflectionKernel& reflection =
+ scene_manager_.GetAssociatedReflectionKernel(0);
+ AcousticRay reflected_ray = reflection.Reflect(ray);
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ EXPECT_FLOAT_EQ(reflected_ray.energies().at(i), 1.0f);
+ }
+ EXPECT_EQ(reflected_ray.type(), AcousticRay::RayType::kSpecular);
+ }
+ {
+ // Test that the reflection from triangle 1, which should be |reflection_1|,
+ // absorbs half of the energy and reflects diffusely.
+ const ReflectionKernel& reflection =
+ scene_manager_.GetAssociatedReflectionKernel(1);
+ AcousticRay reflected_ray = reflection.Reflect(ray);
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ EXPECT_FLOAT_EQ(reflected_ray.energies().at(i), 0.5f);
+ }
+ EXPECT_EQ(reflected_ray.type(), AcousticRay::RayType::kDiffuse);
+ }
+}
+
+TEST_F(SceneManagerTest, BuildEmptyListenerSceneTest) {
+ const float listener_sphere_radius = 0.1f;
+ scene_manager_.BuildListenerScene({}, listener_sphere_radius);
+ EXPECT_TRUE(scene_manager_.is_listener_scene_committed());
+}
+
+TEST_F(SceneManagerTest, RayIntersectListenerSceneTest) {
+ // Five AcousticListeners, lining up on the x-axis, with positions (i, 0, 0).
+ std::vector<AcousticListener> listeners;
+ const size_t impulse_response_length = 1000;
+ for (size_t i = 0; i < 5; ++i) {
+ const Vector3f listener_position(static_cast<float>(i), 0.0f, 0.0f);
+ listeners.emplace_back(listener_position, impulse_response_length);
+ }
+
+ // Build the scene.
+ const float listener_sphere_radius = 0.1f;
+ scene_manager_.BuildListenerScene(listeners, listener_sphere_radius);
+ EXPECT_TRUE(scene_manager_.is_listener_scene_committed());
+
+ // Shoot 5 rays from (i, -1, 0), all with direction (0, 1, 0). Ray i is
+ // expected to intersect with listener i's sphere.
+ const float direction[3] = {0.0f, 1.0f, 0.0f};
+ for (size_t i = 0; i < 5; ++i) {
+ const float origin[3] = {static_cast<float>(i), -1.0f, 0.0f};
+ AcousticRay ray_i(origin, direction, 0.0f /* t_near */,
+ AcousticRay::kInfinity /* t_far */, kUnitEnergies,
+ AcousticRay::RayType::kDiffuse,
+ 0.0f /* prior_distance */);
+
+ // Verify that |ray_i| intersects with listener i's sphere.
+ EXPECT_TRUE(ray_i.Intersect(scene_manager_.listener_scene()));
+ const unsigned int intersected_sphere_id = ray_i.intersected_geometry_id();
+ EXPECT_EQ(
+ scene_manager_.GetListenerIndexFromSphereId(intersected_sphere_id), i);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sphere.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sphere.cc
new file mode 100644
index 000000000..b0f0d141c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sphere.cc
@@ -0,0 +1,120 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/sphere.h"
+
+#include <cmath>
+#include <utility>
+
+#include "Eigen/Core"
+
+namespace vraudio {
+
+using Eigen::Vector3f;
+
+namespace {
+// Fills intersection data in an RTCRay.
+//
+// @param sphere Sphere that intersects with the ray.
+// @param t0 Ray parameter corresponding to the first intersection point.
+// @param t1 Ray parameter corresponding to the second intersection point.
+// @param ray RTCRay that potentially intersects with |sphere|, whose data
+// fields are to be filled.
+inline void FillRaySphereIntersectionData(const Sphere& sphere, float t0,
+ float t1, RTCRay* ray) {
+ // For convenience we enforce that t0 <= t1.
+ if (t0 > t1) {
+ std::swap(t0, t1);
+ }
+
+ // In our application we only consider "intersecting" if the ray starts and
+ // ends outside the sphere, i.e. only if ray.tnear < t0 and ray.tfar > t1.
+ if ((ray->tnear >= t0) || (ray->tfar <= t1)) {
+ return;
+ }
+
+ // Set intersection-related data.
+ ray->tfar = t0;
+ ray->geomID = sphere.geometry_id;
+
+ // ray.Ng is the normal at the intersection point. For a sphere the normal is
+ // always pointing radially outward, i.e. normal = p - sphere.center, where
+ // p is the intersection point. Of the two intersection points, We use the
+ // first.
+ ray->Ng[0] = ray->org[0] + t0 * ray->dir[0] - sphere.center[0];
+ ray->Ng[1] = ray->org[1] + t0 * ray->dir[1] - sphere.center[1];
+ ray->Ng[2] = ray->org[2] + t0 * ray->dir[2] - sphere.center[2];
+}
+
+} // namespace
+
+void SphereIntersection(const Sphere& sphere, RTCRay* ray) {
+ // The intersection is tested by finding if there exists a point p that is
+ // both on the ray and on the sphere:
+ // - Point on the ray: p = ray.origin + t * ray.direction, where t is a
+ // parameter.
+ // - Point on the sphere: || p - sphere.center || = sphere.radius.
+ //
+ // Solving the above two equations for t leads to solving a quadratic
+ // equation:
+ // at^2 + bt + c = 0,
+ // where
+ // a = || ray.direction, ray.direction ||^2
+ // b = 2 * Dot(ray.direction, ray.origin - sphere.center)
+ // c = || ray.origin - sphere.center ||^2.
+ // The two possible solutions to this quadratic equation are:
+ // t0 = - b - sqrt(b^2 - 4ac) / 2a
+ // t1 = - b + sqrt(b^2 - 4ac) / 2a.
+ // The existence of real solutions can be tested if a discriminant value,
+ // b^2 - 4ac, is greater than 0 (we treat discriminant == 0, which corresponds
+ // to the ray touching the sphere at a single point, as not intersecting).
+
+ // Vector pointing from the sphere's center to the ray's origin, i.e.
+ // (ray.origin - sphere.center) in the above equations.
+ const Vector3f center_ray_origin_vector(ray->org[0] - sphere.center[0],
+ ray->org[1] - sphere.center[1],
+ ray->org[2] - sphere.center[2]);
+ const Vector3f ray_direction(ray->dir);
+ const float a = ray_direction.squaredNorm();
+ const float b = 2.0f * center_ray_origin_vector.dot(ray_direction);
+ const float c =
+ center_ray_origin_vector.squaredNorm() - sphere.radius * sphere.radius;
+ const float discriminant = b * b - 4.0f * a * c;
+
+ // No intersection; do nothing.
+ if (discriminant <= 0.0f) {
+ return;
+ }
+
+ // Solve for t0 and t1. As suggested in "Physically Based Rendering" by Pharr
+ // and Humphreys, directly computing - b +- sqrt(b^2 - 4ac) / 2a gives poor
+ // numeric precision when b is close to +- sqrt(b^2 - 4ac), and a more stable
+ // form is used instead:
+ // t0 = q / a
+ // t1 = c / q,
+ // where
+ // q = -(b - sqrt(b^2 - 4ac)) / 2 for b < 0
+ // -(b + sqrt(b^2 - 4ac)) / 2 otherwise.
+ const float sqrt_discriminant = std::sqrt(discriminant);
+ const float q = (b < 0.0f) ? -0.5f * (b - sqrt_discriminant)
+ : -0.5f * (b + sqrt_discriminant);
+ const float t0 = q / a;
+ const float t1 = c / q;
+
+ FillRaySphereIntersectionData(sphere, t0, t1, ray);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sphere.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sphere.h
new file mode 100644
index 000000000..bea49ccde
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sphere.h
@@ -0,0 +1,69 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_SPHERE_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_SPHERE_H_
+
+#include "embree2/rtcore.h"
+#include "embree2/rtcore_ray.h"
+
+namespace vraudio {
+
+// A user defined sphere geometry that enables Embree to test ray intersections.
+struct RTCORE_ALIGN(16) Sphere {
+ // Center of the sphere.
+ float center[3];
+
+ // Radius of the sphere.
+ float radius;
+
+ // Geometry Id. This will be reported by ray intersections.
+ unsigned int geometry_id;
+};
+
+// The following functions are to be called by Embree internally to enable fast
+// ray-sphere intersections tests.
+
+// Calculates the RTCBounds (essentially a bounding box) of a given sphere.
+//
+// @param sphere Sphere of interest.
+// @param output_bounds Output RTCBounds of the sphere.
+inline void SphereBounds(const Sphere& sphere, RTCBounds* output_bounds) {
+ output_bounds->lower_x = sphere.center[0] - sphere.radius;
+ output_bounds->lower_y = sphere.center[1] - sphere.radius;
+ output_bounds->lower_z = sphere.center[2] - sphere.radius;
+ output_bounds->upper_x = sphere.center[0] + sphere.radius;
+ output_bounds->upper_y = sphere.center[1] + sphere.radius;
+ output_bounds->upper_z = sphere.center[2] + sphere.radius;
+}
+
+// Tests whether a sphere and a ray intersects and sets the related data for
+// the ray when an intersection is found:
+// - |ray.tfar| will be set to corresponding to the first intersection point.
+// - |ray.geomID| will be set to the geometry id of the sphere.
+// - |ray.Ng| will be set to the normal at the first intersection point,
+// pointing radially outward
+//
+// Because this function will be called internally by Embree, it works on
+// Embree's native type RTCRay as opposed to AcousticRay.
+//
+// @param spheres Sphere of interest.
+// @param ray RTCRay with which intersections are tested and related data set.
+void SphereIntersection(const Sphere& sphere, RTCRay* ray);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_SPHERE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sphere_test.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sphere_test.cc
new file mode 100644
index 000000000..d0daea12e
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/sphere_test.cc
@@ -0,0 +1,267 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/sphere.h"
+
+#include <cmath>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/aligned_allocator.h"
+#include "base/logging.h"
+#include "geometrical_acoustics/acoustic_ray.h"
+#include "geometrical_acoustics/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+// A function adapter from SphereBounds() to RTCBoundsFunc in order to be
+// passed to rtcSetBoundsFunction().
+// The signature of RTCBoundsFunc does not comply with Google's C++ style,
+
+static void EmbreeSphereBoundsFunction(void* user_data, size_t index,
+ RTCBounds& output_bounds
+ ) {
+ Sphere* spheres = static_cast<Sphere*>(user_data);
+ const Sphere& sphere = spheres[index];
+ SphereBounds(sphere, &output_bounds);
+}
+
+// A function adapter from SphereIntersections() to RTCIntersectFunc in order
+// to be passed to rtcSetIntersectFunction().
+// The signature of RTCIntersectFunc does not comply with Google's C++ style,
+
+static void EmbreeSphereIntersectFunction(void* user_data,
+ RTCRay& ray,
+ size_t index) {
+ Sphere* spheres = static_cast<Sphere*>(user_data);
+ const Sphere& sphere = spheres[index];
+ SphereIntersection(sphere, &ray);
+}
+
+void SetSphereData(const float center[3], float radius,
+ unsigned int geometry_id, Sphere* sphere) {
+ sphere->center[0] = center[0];
+ sphere->center[1] = center[1];
+ sphere->center[2] = center[2];
+ sphere->radius = radius;
+ sphere->geometry_id = geometry_id;
+}
+
+TEST(Sphere, SphereBoundsTest) {
+ const float center[3] = {1.0f, 2.0f, 3.0f};
+ const float radius = 3.0f;
+ Sphere sphere;
+ SetSphereData(center, radius, 0u, &sphere);
+
+ RTCBounds bounds;
+ SphereBounds(sphere, &bounds);
+ EXPECT_FLOAT_EQ(bounds.lower_x, -2.0f);
+ EXPECT_FLOAT_EQ(bounds.lower_y, -1.0f);
+ EXPECT_FLOAT_EQ(bounds.lower_z, 0.0f);
+ EXPECT_FLOAT_EQ(bounds.upper_x, 4.0f);
+ EXPECT_FLOAT_EQ(bounds.upper_y, 5.0f);
+ EXPECT_FLOAT_EQ(bounds.upper_z, 6.0f);
+}
+
+TEST(Sphere, RayIntersectSphereTest) {
+ const float center[3] = {0.0f, 0.0f, 0.0f};
+ const float radius = 1.0f;
+ const unsigned sphere_geometry_id = 1u;
+ Sphere sphere;
+ SetSphereData(center, radius, sphere_geometry_id, &sphere);
+
+ // This ray passes through the sphere.
+ const float origin[3] = {0.5f, 0.0f, -2.0f};
+ const float direction[3] = {0.0f, 0.0f, 1.0f};
+ AcousticRay ray(origin, direction, 0.0f, AcousticRay::kInfinity, {},
+ AcousticRay::RayType::kSpecular, 0.0f);
+
+ SphereIntersection(sphere, &ray);
+
+ // Expect an intersection, so that ray.intersected_geometry_id() returns
+ // |sphere_geometry_id|.
+ EXPECT_EQ(ray.intersected_geometry_id(), sphere_geometry_id);
+
+ // Expect that the intersection point is at (0.5, 0, -sqrt(3)/2).
+ const float expected_intersection_point[3] = {0.5f, 0.0f,
+ -0.5f * kSqrtThree};
+ float intersection_point[3];
+ intersection_point[0] = ray.origin()[0] + ray.t_far() * ray.direction()[0];
+ intersection_point[1] = ray.origin()[1] + ray.t_far() * ray.direction()[1];
+ intersection_point[2] = ray.origin()[2] + ray.t_far() * ray.direction()[2];
+ ExpectFloat3Close(intersection_point, expected_intersection_point);
+
+ // Expect that the normal at the intersection point is (0.5, 0, -sqrt(3)/2).
+ const float expected_normal[3] = {0.5f, 0.0f, -0.5f * kSqrtThree};
+ ExpectFloat3Close(ray.intersected_geometry_normal(), expected_normal);
+}
+
+TEST(Sphere, RayOutsideSphereNotIntersectTest) {
+ const float center[3] = {0.0f, 0.0f, 0.0f};
+ const float radius = 1.0f;
+ Sphere sphere;
+ SetSphereData(center, radius, 1u, &sphere);
+
+ // This ray completely lies outside of the sphere.
+ const float origin[3] = {2.0f, 2.0f, 2.0f};
+ const float direction[3] = {0.0f, 0.0f, 1.0f};
+ AcousticRay ray(origin, direction, 0.0f, AcousticRay::kInfinity, {},
+ AcousticRay::RayType::kSpecular, 0.0f);
+
+ SphereIntersection(sphere, &ray);
+
+ // Expect no intersection, i.e. so that ray.intersected_geometry_id() returns
+ // RTC_INVALID_GEOMETRY_ID.
+ EXPECT_EQ(ray.intersected_geometry_id(), RTC_INVALID_GEOMETRY_ID);
+}
+
+TEST(Sphere, RayStartingInsideSphereNotIntersectTest) {
+ const float center[3] = {0.0f, 0.0f, 0.0f};
+ const float radius = 1.0f;
+ Sphere sphere;
+ SetSphereData(center, radius, 1u, &sphere);
+
+ // This ray theoretically intersects with the sphere, but as its starting
+ // point is inside the sphere, our intersection test regards it as
+ // non-intersecting.
+ const float origin[3] = {0.5f, 0.5f, 0.0f};
+ const float direction[3] = {0.0f, 0.0f, 1.0f};
+ AcousticRay ray(origin, direction, 0.0f, AcousticRay::kInfinity, {},
+ AcousticRay::RayType::kSpecular, 0.0f);
+
+ SphereIntersection(sphere, &ray);
+
+ // Expect no intersection, i.e. so that ray.intersected_geometry_id() returns
+ // RTC_INVALID_GEOMETRY_ID.
+ EXPECT_EQ(ray.intersected_geometry_id(), RTC_INVALID_GEOMETRY_ID);
+}
+
+TEST(Sphere, RayEndingInsideSphereNotIntersectTest) {
+ const float center[3] = {0.0f, 0.0f, 0.0f};
+ const float radius = 1.0f;
+ Sphere sphere;
+ SetSphereData(center, radius, 1u, &sphere);
+
+ // This ray theoretically intersects with the sphere, but as its end point
+ // (controlled by |ray.tfar| is inside the sphere, our intersection test
+ // regards it as non-intersecting.
+ const float origin[3] = {0.5f, 0.0f, -2.0f};
+ const float direction[3] = {0.0f, 0.0f, 1.0f};
+ AcousticRay ray(origin, direction, 0.0f, 2.0f, {},
+ AcousticRay::RayType::kSpecular, 0.0f);
+
+ SphereIntersection(sphere, &ray);
+
+ // Expect no intersection, i.e. so that ray.intersected_geometry_id() returns
+ // RTC_INVALID_GEOMETRY_ID.
+ EXPECT_EQ(ray.intersected_geometry_id(), RTC_INVALID_GEOMETRY_ID);
+}
+
+// The following test fixture and tests mimic real use cases of ray-sphere
+// intersections in our acoustics computation:
+//
+// 1) A sphere is constructed.
+// 2) The sphere is registered to the scene as user data. Two callback
+// functions of types RTCBoundsFunc and RTCIntersectFunc are registered
+// to the scene as well.
+// 2) An AcousticRay is constructed.
+// 3) The intersection is found by AcousticRay.Intersect().
+//
+// This is different from previous tests in that we do not directly invoke
+// SphereBounds() and SphereIntersection(); Embree does it for us.
+
+class SphereTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ // Use a single RTCDevice for all tests.
+ static RTCDevice device = rtcNewDevice(nullptr);
+ CHECK_NOTNULL(device);
+ scene_ = rtcDeviceNewScene(
+ device, RTC_SCENE_STATIC | RTC_SCENE_HIGH_QUALITY, RTC_INTERSECT1);
+ }
+
+ void TearDown() override { rtcDeleteScene(scene_); }
+
+ unsigned int AddSphereToScene() {
+ const unsigned int geometry_id = rtcNewUserGeometry(scene_, 1);
+
+ const float center[3] = {0.0f, 0.0f, 0.0f};
+ const float radius = 1.0f;
+ Sphere* const sphere =
+ AllignedMalloc<Sphere, size_t, Sphere*>(sizeof(Sphere),
+ /*alignment=*/64);
+
+ SetSphereData(center, radius, geometry_id, sphere);
+
+ // rtcSetUserData() takes ownership of |sphere|.
+ rtcSetUserData(scene_, geometry_id, sphere);
+ rtcSetBoundsFunction(scene_, geometry_id, &EmbreeSphereBoundsFunction);
+ rtcSetIntersectFunction(scene_, geometry_id,
+ &EmbreeSphereIntersectFunction);
+ return geometry_id;
+ }
+
+ const std::array<float, kNumReverbOctaveBands> kUnitEnergies{
+ {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}};
+ RTCScene scene_ = nullptr;
+};
+
+TEST_F(SphereTest, AcousticRayIntersectSphereTest) {
+ const unsigned int sphere_geometry_id = AddSphereToScene();
+ rtcCommit(scene_);
+
+ // Construct an AcousticRay that passes through the sphere.
+ const float origin[3] = {0.5f, 0.0f, -2.0f};
+ const float direction[3] = {0.0f, 0.0f, 1.0f};
+ AcousticRay ray(origin, direction, 0.0f, AcousticRay::kInfinity,
+ kUnitEnergies, AcousticRay::RayType::kSpecular, 0.0f);
+
+ // Expect intersection happens and the intersected geometry id set to
+ // |sphere_geometry_id|.
+ EXPECT_TRUE(ray.Intersect(scene_));
+ EXPECT_EQ(ray.intersected_geometry_id(), sphere_geometry_id);
+
+ // Expect that the intersection point is at (0.5, 0, -sqrt(3)/2).
+ const float expected_intersection_point[3] = {0.5f, 0.0f,
+ -0.5f * kSqrtThree};
+ float intersection_point[3];
+ intersection_point[0] = ray.origin()[0] + ray.t_far() * ray.direction()[0];
+ intersection_point[1] = ray.origin()[1] + ray.t_far() * ray.direction()[1];
+ intersection_point[2] = ray.origin()[2] + ray.t_far() * ray.direction()[2];
+ ExpectFloat3Close(intersection_point, expected_intersection_point);
+
+ // Expect that the normal at the intersection point is (0.5, 0, -sqrt(3)/2).
+ const float expected_normal[3] = {0.5f, 0.0f, -0.5f * kSqrtThree};
+ ExpectFloat3Close(ray.intersected_geometry_normal(), expected_normal);
+}
+
+TEST_F(SphereTest, AcousticRayIntersectNothingTest) {
+ rtcCommit(scene_);
+
+ // Construct an AcousticRay completely outside of the sphere.
+ const float origin[3] = {2.0f, 2.0f, 2.0f};
+ const float direction[3] = {0.0f, 0.0f, 1.0f};
+ AcousticRay ray(origin, direction, 0.0f, AcousticRay::kInfinity,
+ kUnitEnergies, AcousticRay::RayType::kSpecular, 0.0f);
+
+ // Expect no intersection.
+ EXPECT_FALSE(ray.Intersect(scene_));
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/test_util.cc b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/test_util.cc
new file mode 100644
index 000000000..b407f6f07
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/test_util.cc
@@ -0,0 +1,231 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "geometrical_acoustics/test_util.h"
+
+#include <algorithm>
+#include <random>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/logging.h"
+#include "geometrical_acoustics/path_tracer.h"
+
+namespace vraudio {
+
+namespace {
+// Given N bins each having some count, validates that the counts are almost the
+// same across all bins.
+void ValidateUniformDistribution(const std::vector<int>& count_in_bins,
+ const int total_count) {
+ const float expected_fraction =
+ 1.0f / static_cast<float>(count_in_bins.size());
+ const float tolerance = static_cast<float>(count_in_bins.size()) /
+ static_cast<float>(total_count);
+ for (const int count : count_in_bins) {
+ const float actual_fraction =
+ static_cast<float>(count) / static_cast<float>(total_count);
+ const float error = actual_fraction - expected_fraction;
+ const float chi_square = error * error / expected_fraction;
+ EXPECT_NEAR(chi_square, 0.0f, tolerance);
+ }
+}
+
+// Assigns surface materials to groups of triangles, one material to one group.
+void AssignSurfaceMaterialsToTriangleGroups(
+ const std::vector<std::unordered_set<unsigned int>>& triangle_groups,
+ const std::vector<MaterialName>& materials,
+ const std::function<float()>& random_number_generator,
+ SceneManager* scene_manager) {
+ CHECK_EQ(triangle_groups.size(), materials.size());
+
+ const float scattering_coefficient = 1.0f;
+ std::array<float, kNumReverbOctaveBands> absorption_coefficients;
+ for (size_t group = 0; group < materials.size(); ++group) {
+ const size_t material_index = static_cast<size_t>(materials[group]);
+ const RoomMaterial& room_material = GetRoomMaterial(material_index);
+ for (size_t band = 0; band < kNumReverbOctaveBands; ++band) {
+ absorption_coefficients[band] =
+ room_material.absorption_coefficients[band];
+ }
+ ReflectionKernel reflection_kernel(absorption_coefficients,
+ scattering_coefficient,
+ random_number_generator);
+ scene_manager->AssociateReflectionKernelToTriangles(reflection_kernel,
+ triangle_groups[group]);
+ }
+}
+
+} // namespace
+
+void ValidateDistribution(const int num_samples, const int num_bins,
+ std::function<float()> cdf) {
+ DCHECK_GT(num_bins, 0);
+ std::vector<int> cdf_bins(num_bins, 0);
+ for (int i = 0; i < num_samples; ++i) {
+ const float cdf_value = cdf();
+ const int cdf_bin_index = std::min(
+ num_bins - 1, std::max(0, static_cast<int>(cdf_value * num_bins)));
+ ++cdf_bins[cdf_bin_index];
+ }
+ ValidateUniformDistribution(cdf_bins, num_samples);
+}
+
+void AddTestGround(RTCScene scene) {
+ unsigned int mesh_id = rtcNewTriangleMesh(scene, RTC_GEOMETRY_STATIC, 2, 4);
+
+ // Vertices. Each vertex has 4 floats: x, y, z, and a padding float whose
+ // value we do not care.
+ float ground_vertices[] = {
+ 0.0f, 0.0f, 0.0f, 0.0f, // Vertex_0.
+ 0.0f, 1.0f, 0.0f, 0.0f, // Vertex_1.
+ 1.0f, 0.0f, 0.0f, 0.0f, // Vertex_2.
+ 1.0f, 1.0f, 0.0f, 0.0f, // Vertex_3.
+ };
+
+ // Triangles. Somehow Embree is a left-handed system.
+ int ground_indices[] = {
+ 0, 1, 2, // Triangle_0.
+ 1, 3, 2, // Triangle_1.
+ };
+ float* const embree_vertices =
+ static_cast<float*>(rtcMapBuffer(scene, mesh_id, RTC_VERTEX_BUFFER));
+ std::copy(ground_vertices, ground_vertices + 16, embree_vertices);
+ rtcUnmapBuffer(scene, mesh_id, RTC_VERTEX_BUFFER);
+ int* const embree_indices =
+ static_cast<int*>(rtcMapBuffer(scene, mesh_id, RTC_INDEX_BUFFER));
+ std::copy(ground_indices, ground_indices + 6, embree_indices);
+ rtcUnmapBuffer(scene, mesh_id, RTC_INDEX_BUFFER);
+}
+
+void BuildTestBoxScene(
+ const Vertex& min_corner, const Vertex& max_corner,
+ std::vector<Vertex>* box_vertices, std::vector<Triangle>* box_triangles,
+ std::vector<std::unordered_set<unsigned int>>* box_wall_triangles) {
+ const float min_x = min_corner.x;
+ const float min_y = min_corner.y;
+ const float min_z = min_corner.z;
+ const float max_x = max_corner.x;
+ const float max_y = max_corner.y;
+ const float max_z = max_corner.z;
+ CHECK_LT(min_x, max_x);
+ CHECK_LT(min_y, max_y);
+ CHECK_LT(min_z, max_z);
+
+ if (box_vertices != nullptr) {
+ *box_vertices = std::vector<Vertex>{
+ {min_x, min_y, min_z}, {min_x, max_y, min_z}, {max_x, min_y, min_z},
+ {max_x, max_y, min_z}, {min_x, min_y, max_z}, {min_x, max_y, max_z},
+ {max_x, min_y, max_z}, {max_x, max_y, max_z}};
+ }
+ if (box_vertices != nullptr) {
+ *box_triangles = std::vector<Triangle>{
+ {0, 1, 4}, {1, 5, 4}, // wall facing the -x direction.
+ {2, 6, 3}, {3, 6, 7}, // wall facing the +x direction.
+ {0, 6, 2}, {0, 4, 6}, // wall facing the -y direction.
+ {1, 3, 7}, {1, 7, 5}, // wall facing the +y direction.
+ {0, 3, 1}, {0, 2, 3}, // wall facing the -z direction.
+ {4, 7, 6}, {4, 5, 7}, // wall facing the +z direction.
+ };
+ }
+ if (box_wall_triangles != nullptr) {
+ *box_wall_triangles = std::vector<std::unordered_set<unsigned int>>{
+ {0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}};
+ }
+}
+
+std::vector<Path> TracePathsInTestcene(
+ size_t min_num_rays, size_t max_depth, float energy_threshold,
+ const float source_position[3], const std::vector<Vertex>& scene_vertices,
+ const std::vector<Triangle>& scene_triangles,
+ const std::vector<std::unordered_set<unsigned int>>& scene_triangle_groups,
+ const std::vector<MaterialName>& materials, SceneManager* scene_manager) {
+ // Build the scene.
+ scene_manager->BuildScene(scene_vertices, scene_triangles);
+
+ // Assign materials.
+ std::default_random_engine engine(0);
+ std::uniform_real_distribution<float> distribution(0.0f, 1.0f);
+ std::function<float()> random_number_generator = [&engine, &distribution] {
+ return distribution(engine);
+ };
+ AssignSurfaceMaterialsToTriangleGroups(
+ scene_triangle_groups, materials, random_number_generator, scene_manager);
+
+ // Trace paths.
+ AcousticSource source(Eigen::Vector3f(source_position), kUnitEnergies,
+ random_number_generator);
+ PathTracer path_tracer(*scene_manager);
+ return path_tracer.TracePaths(source, min_num_rays, max_depth,
+ energy_threshold);
+}
+
+std::vector<Path> GenerateUniformlyDistributedRayPaths(
+ const float source_position[3], size_t min_num_rays) {
+ // Use AcousticSource to generate rays in uniformly distributed directions.
+ std::default_random_engine engine(0);
+ std::uniform_real_distribution<float> distribution(0.0f, 1.0f);
+ AcousticSource source(
+ Eigen::Vector3f(source_position), kUnitEnergies,
+ [&engine, &distribution] { return distribution(engine); });
+
+ const size_t sqrt_num_rays = static_cast<size_t>(
+ std::ceil(std::sqrt(static_cast<float>(min_num_rays))));
+ const size_t num_rays = sqrt_num_rays * sqrt_num_rays;
+ std::vector<Path> paths(num_rays);
+ const std::vector<AcousticRay>& rays =
+ source.GenerateStratifiedRays(num_rays, sqrt_num_rays);
+ for (size_t i = 0; i < num_rays; ++i) {
+ Path path;
+ path.rays.push_back(rays[i]);
+ paths[i] = path;
+ }
+
+ return paths;
+}
+
+void ExpectFloat3Close(const float actual_vector[3],
+ const float expected_vector[3]) {
+ EXPECT_FLOAT_EQ(actual_vector[0], expected_vector[0]);
+ EXPECT_FLOAT_EQ(actual_vector[1], expected_vector[1]);
+ EXPECT_FLOAT_EQ(actual_vector[2], expected_vector[2]);
+}
+
+void ExpectFloat3Close(const float actual_vector[3],
+ const float expected_vector[3], float tolerance) {
+ EXPECT_NEAR(actual_vector[0], expected_vector[0], tolerance);
+ EXPECT_NEAR(actual_vector[1], expected_vector[1], tolerance);
+ EXPECT_NEAR(actual_vector[2], expected_vector[2], tolerance);
+}
+
+void ValidateSparseFloatArray(const std::vector<float>& actual_array,
+ const std::vector<size_t>& expected_indices,
+ const std::vector<float>& expected_values,
+ float relative_error_tolerance) {
+ // First construct the expected array.
+ std::vector<float> expected_array(actual_array.size(), 0.0f);
+ for (size_t i = 0; i < expected_indices.size(); ++i) {
+ expected_array[expected_indices[i]] = expected_values[i];
+ }
+
+ // Compare with the actual array element-by-element.
+ for (size_t i = 0; i < actual_array.size(); ++i) {
+ EXPECT_NEAR(actual_array[i], expected_array[i],
+ expected_array[i] * relative_error_tolerance);
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/test_util.h b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/test_util.h
new file mode 100644
index 000000000..8d9382b35
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/geometrical_acoustics/test_util.h
@@ -0,0 +1,131 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Utilities useful for testing Geometrical Acoustics related classes.
+#ifndef RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_TEST_UTIL_H_
+#define RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_TEST_UTIL_H_
+
+#include <cstddef>
+#include <functional>
+#include <unordered_set>
+#include <vector>
+
+#include "embree2/rtcore.h"
+#include "api/resonance_audio_api.h"
+#include "base/constants_and_types.h"
+#include "geometrical_acoustics/mesh.h"
+#include "geometrical_acoustics/path.h"
+#include "geometrical_acoustics/scene_manager.h"
+#include "platforms/common/room_effects_utils.h"
+
+namespace vraudio {
+
+// Array of |kNumReverbOctaveBands| of unit energies. Useful to set as initial
+// energies of sound sources in tests.
+static const std::array<float, kNumReverbOctaveBands> kUnitEnergies{
+ {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}};
+
+// Validates a distribution given the cumulative distribution function (CDF).
+// For any probability density function (PDF), its CDF should be uniformly
+// distributed. So we generate some samples, gather them into bins based on
+// their CDF values, and verify that the number of samples are almost the
+// same across all bins.
+//
+// @param num_samples Number of samples to generate.
+// @param num_bins Number of bins to gather the samples.
+// @param cdf Cumulative distribution function of the distribution to test.
+void ValidateDistribution(const int num_samples, const int num_bins,
+ std::function<float()> cdf);
+
+// Adds a ground (a rectangle consisting of two triangles) to a test scene.
+//
+// @param scene Test scene to which the ground is added.
+void AddTestGround(RTCScene scene);
+
+// Builds a box scene with 8 vertices, 12 triangles, and 6 walls.
+//
+// @param min_corner Corner of the box with minimum x-, y-, z-components.
+// @param max_corner Corner of the box with maximum x-, y-, z-components.
+// @param box_vertices Output vertices of the box. Not filled if nullptr is
+// passed in.
+// @param box_triangles Output triangles of the box. Not filled if nullptr is
+// passed in.
+// @param box_wall_triangles Output wall-to-triangles mapping, with six walls
+// each having two triangles. Not filled if nullptr is passed in.
+void BuildTestBoxScene(
+ const Vertex& min_corner, const Vertex& max_corner,
+ std::vector<Vertex>* box_vertices, std::vector<Triangle>* box_triangles,
+ std::vector<std::unordered_set<unsigned int>>* box_wall_triangles);
+
+// Traces ray paths in a created test scene.
+//
+// @param num_rays Number of rays.
+// @param max_depth Maximum depth of tracing performed along a path.
+// @param energy_threshold Energy threshold below which the tracing stops.
+// @param source_position Position from which to shoot rays.
+// @param scene_vertices Vertices of the scene.
+// @param scene_triangles Triangles of the scene.
+// @param scene_triangle_groups Groups of triangles that share the same
+// material.
+// @param materials Materials for each triangle group.
+// @param scene_manager Scene manager that holds the created test scene.
+// @return Vector of traced paths.
+std::vector<Path> TracePathsInTestcene(
+ size_t num_rays, size_t max_depth, float energy_threshold,
+ const float source_position[3], const std::vector<Vertex>& scene_vertices,
+ const std::vector<Triangle>& scene_triangles,
+ const std::vector<std::unordered_set<unsigned int>>& scene_triangle_groups,
+ const std::vector<MaterialName>& materials, SceneManager* scene_manager);
+
+// Generated ray paths in uniformly distributed directions.
+//
+// @param source_position Position from which to shoot rays.
+// @param min_num_rays Minimum number of rays generated.
+// @return Vector of traced paths.
+std::vector<Path> GenerateUniformlyDistributedRayPaths(
+ const float source_position[3], size_t min_num_rays);
+
+// Compares two float vectors[3]s and expect that their components are close.
+//
+// @param actual_vector Actual vector.
+// @param expected_vector Expected vector.
+void ExpectFloat3Close(const float actual_vector[3],
+ const float expected_vector[3]);
+
+// Same as above but with a user-specified tolerance.
+//
+// @param actual_vector Actual vector.
+// @param expected_vector Expected vector.
+// @param tolerance Tolerance for comparisons.
+void ExpectFloat3Close(const float actual_vector[3],
+ const float expected_vector[3], float tolerance);
+
+// Validates a sparse float array using the indices and values of its
+// non-zero elements.
+//
+// @param actual_array Actual array to be validated.
+// @param expected_indices Indices of the non-zero elements of the expected
+// array.
+// @param expected_values Values of the non-zero elements of the expected array.
+// @param relative_error_tolerance Tolerance of relative errors for element
+// comparisons.
+void ValidateSparseFloatArray(const std::vector<float>& actual_array,
+ const std::vector<size_t>& expected_indices,
+ const std::vector<float>& expected_values,
+ float relative_error_tolerance);
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GEOMETRICAL_ACOUSTICS_TEST_UTIL_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_binaural_decoder_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_binaural_decoder_node.cc
new file mode 100644
index 000000000..a3c8ff0cb
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_binaural_decoder_node.cc
@@ -0,0 +1,111 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/ambisonic_binaural_decoder_node.h"
+
+#include "ambisonics/stereo_from_soundfield_converter.h"
+#include "ambisonics/utils.h"
+#include "base/constants_and_types.h"
+
+#include "dsp/sh_hrir_creator.h"
+
+namespace vraudio {
+
+AmbisonicBinauralDecoderNode::AmbisonicBinauralDecoderNode(
+ const SystemSettings& system_settings, int ambisonic_order,
+ const std::string& sh_hrir_filename, FftManager* fft_manager,
+ Resampler* resampler)
+ : system_settings_(system_settings),
+ num_ambisonic_channels_(GetNumPeriphonicComponents(ambisonic_order)),
+ is_stereo_speaker_mode_(system_settings_.IsStereoSpeakerModeEnabled()),
+ num_frames_processed_on_empty_input_(
+ system_settings_.GetFramesPerBuffer()),
+ stereo_output_buffer_(kNumStereoChannels,
+ system_settings.GetFramesPerBuffer()),
+ silence_input_buffer_(num_ambisonic_channels_,
+ system_settings.GetFramesPerBuffer()),
+ crossfader_(system_settings_.GetFramesPerBuffer()),
+ crossfaded_output_buffer_(kNumStereoChannels,
+ system_settings.GetFramesPerBuffer()),
+ temp_crossfade_buffer_(kNumStereoChannels,
+ system_settings.GetFramesPerBuffer()) {
+ silence_input_buffer_.Clear();
+ EnableProcessOnEmptyInput(true);
+ std::unique_ptr<AudioBuffer> sh_hrirs = CreateShHrirsFromAssets(
+ sh_hrir_filename, system_settings_.GetSampleRateHz(), resampler);
+ CHECK_EQ(sh_hrirs->num_channels(), num_ambisonic_channels_);
+ ambisonic_binaural_decoder_.reset(new AmbisonicBinauralDecoder(
+ *sh_hrirs, system_settings_.GetFramesPerBuffer(), fft_manager));
+}
+
+AmbisonicBinauralDecoderNode::~AmbisonicBinauralDecoderNode() {}
+
+const AudioBuffer* AmbisonicBinauralDecoderNode::AudioProcess(
+ const NodeInput& input) {
+
+
+ const bool was_stereo_speaker_mode_enabled = is_stereo_speaker_mode_;
+ is_stereo_speaker_mode_ = system_settings_.IsStereoSpeakerModeEnabled();
+
+ const size_t num_frames = system_settings_.GetFramesPerBuffer();
+ const AudioBuffer* input_buffer = input.GetSingleInput();
+ if (input_buffer == nullptr) {
+ if (num_frames_processed_on_empty_input_ < num_frames &&
+ !was_stereo_speaker_mode_enabled) {
+ // If we have no input, generate a silent input buffer until the node
+ // states are cleared.
+ num_frames_processed_on_empty_input_ += num_frames;
+ ambisonic_binaural_decoder_->Process(silence_input_buffer_,
+ &stereo_output_buffer_);
+ return &stereo_output_buffer_;
+ } else {
+ // Skip processing entirely when the states are fully cleared.
+ return nullptr;
+ }
+ }
+
+ num_frames_processed_on_empty_input_ = 0;
+
+ DCHECK_EQ(input_buffer->num_channels(), num_ambisonic_channels_);
+ DCHECK_EQ(input_buffer->num_frames(), num_frames);
+
+ // If stereo speaker mode is enabled, perform M-S stereo decode. Otherwise,
+ // perform binaural decode.
+ if (is_stereo_speaker_mode_) {
+ StereoFromSoundfield(*input_buffer, &stereo_output_buffer_);
+ } else {
+ ambisonic_binaural_decoder_->Process(*input_buffer, &stereo_output_buffer_);
+ }
+
+ if (is_stereo_speaker_mode_ != was_stereo_speaker_mode_enabled) {
+ // Apply linear crossfade between binaural decode and stereo decode outputs.
+ if (was_stereo_speaker_mode_enabled) {
+ StereoFromSoundfield(*input_buffer, &temp_crossfade_buffer_);
+ } else {
+ ambisonic_binaural_decoder_->Process(*input_buffer,
+ &temp_crossfade_buffer_);
+ }
+ crossfader_.ApplyLinearCrossfade(stereo_output_buffer_,
+ temp_crossfade_buffer_,
+ &crossfaded_output_buffer_);
+ return &crossfaded_output_buffer_;
+ }
+
+ // Return the rendered output directly.
+ return &stereo_output_buffer_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_binaural_decoder_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_binaural_decoder_node.h
new file mode 100644
index 000000000..548d44e01
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_binaural_decoder_node.h
@@ -0,0 +1,88 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_AMBISONIC_BINAURAL_DECODER_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_AMBISONIC_BINAURAL_DECODER_NODE_H_
+
+#include <memory>
+#include <string>
+
+#include "ambisonics/ambisonic_binaural_decoder.h"
+#include "base/audio_buffer.h"
+#include "dsp/fft_manager.h"
+#include "dsp/resampler.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+#include "utils/buffer_crossfader.h"
+
+namespace vraudio {
+
+// Node that takes an ambisonic soundfield as input and renders a binaural
+// stereo buffer as output.
+class AmbisonicBinauralDecoderNode : public ProcessingNode {
+ public:
+ // Initializes AmbisonicBinauralDecoderNode class.
+ //
+ // @param system_settings Global system configuration.
+ // @param ambisonic_order Ambisonic order.
+ // @param sh_hrir_filename Filename to load the HRIR data from.
+ // @param fft_manager Pointer to a manager to perform FFT transformations.
+ // @resampler Pointer to a resampler used to convert HRIRs to the system rate.
+ AmbisonicBinauralDecoderNode(const SystemSettings& system_settings,
+ int ambisonic_order,
+ const std::string& sh_hrir_filename,
+ FftManager* fft_manager, Resampler* resampler);
+
+ ~AmbisonicBinauralDecoderNode() override;
+
+ protected:
+ // Implements ProcessingNode.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ const SystemSettings& system_settings_;
+
+ // Number of Ambisonic channels.
+ const size_t num_ambisonic_channels_;
+
+ // Denotes if the stereo speaker mode is enabled.
+ bool is_stereo_speaker_mode_;
+
+ // Ambisonic decoder used to render binaural output.
+ std::unique_ptr<AmbisonicBinauralDecoder> ambisonic_binaural_decoder_;
+
+ size_t num_frames_processed_on_empty_input_;
+
+ // Stereo output buffer.
+ AudioBuffer stereo_output_buffer_;
+
+ // Silence mono buffer to render reverb tails.
+ AudioBuffer silence_input_buffer_;
+
+ // Buffer crossfader to apply linear crossfade when the stereo speaker mode is
+ // changed.
+ BufferCrossfader crossfader_;
+
+ // Stereo output buffer to store the crossfaded decode output when necessary.
+ AudioBuffer crossfaded_output_buffer_;
+
+ // Temporary crossfade buffer to store the intermediate stereo output.
+ AudioBuffer temp_crossfade_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_AMBISONIC_BINAURAL_DECODER_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_mixing_encoder_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_mixing_encoder_node.cc
new file mode 100644
index 000000000..88405464c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_mixing_encoder_node.cc
@@ -0,0 +1,69 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/ambisonic_mixing_encoder_node.h"
+
+#include "ambisonics/utils.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+
+
+namespace vraudio {
+
+AmbisonicMixingEncoderNode::AmbisonicMixingEncoderNode(
+ const SystemSettings& system_settings,
+ const AmbisonicLookupTable& lookup_table, int ambisonic_order)
+ : system_settings_(system_settings),
+ lookup_table_(lookup_table),
+ ambisonic_order_(ambisonic_order),
+ gain_mixer_(GetNumPeriphonicComponents(ambisonic_order_),
+ system_settings_.GetFramesPerBuffer()),
+ coefficients_(GetNumPeriphonicComponents(ambisonic_order_)) {}
+
+const AudioBuffer* AmbisonicMixingEncoderNode::AudioProcess(
+ const NodeInput& input) {
+
+
+ const WorldPosition& listener_position = system_settings_.GetHeadPosition();
+ const WorldRotation& listener_rotation = system_settings_.GetHeadRotation();
+
+ gain_mixer_.Reset();
+ for (auto& input_buffer : input.GetInputBuffers()) {
+ const int source_id = input_buffer->source_id();
+ const auto source_parameters =
+ system_settings_.GetSourceParameters(source_id);
+ DCHECK_NE(source_id, kInvalidSourceId);
+ DCHECK_EQ(input_buffer->num_channels(), 1U);
+
+ // Compute the relative source direction in spherical angles.
+ const ObjectTransform& source_transform =
+ source_parameters->object_transform;
+ WorldPosition relative_direction;
+ GetRelativeDirection(listener_position, listener_rotation,
+ source_transform.position, &relative_direction);
+ const SphericalAngle source_direction =
+ SphericalAngle::FromWorldPosition(relative_direction);
+
+ lookup_table_.GetEncodingCoeffs(ambisonic_order_, source_direction,
+ source_parameters->spread_deg,
+ &coefficients_);
+
+ gain_mixer_.AddInputChannel((*input_buffer)[0], source_id, coefficients_);
+ }
+ return gain_mixer_.GetOutput();
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_mixing_encoder_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_mixing_encoder_node.h
new file mode 100644
index 000000000..865677571
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_mixing_encoder_node.h
@@ -0,0 +1,71 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_AMBISONIC_MIXING_ENCODER_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_AMBISONIC_MIXING_ENCODER_NODE_H_
+
+#include <vector>
+
+#include "ambisonics/ambisonic_lookup_table.h"
+#include "base/audio_buffer.h"
+#include "base/spherical_angle.h"
+#include "dsp/gain_mixer.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+// Node that accepts single mono sound object buffer as input and encodes it
+// into an Ambisonic sound field.
+class AmbisonicMixingEncoderNode : public ProcessingNode {
+ public:
+ // Initializes AmbisonicMixingEncoderNode class.
+ //
+ // @param system_settings Global system configuration.
+ // @param lookup_table Ambisonic encoding lookup table.
+ // @param ambisonic_order Order of Ambisonic sources.
+ AmbisonicMixingEncoderNode(const SystemSettings& system_settings,
+ const AmbisonicLookupTable& lookup_table,
+ int ambisonic_order);
+
+ // Node implementation.
+ bool CleanUp() final {
+ CallCleanUpOnInputNodes();
+ // Prevent node from being disconnected when all sources are removed.
+ return false;
+ }
+
+ protected:
+ // Implements ProcessingNode.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ const SystemSettings& system_settings_;
+ const AmbisonicLookupTable& lookup_table_;
+
+ // Ambisonic order of encoded sources.
+ const int ambisonic_order_;
+
+ // |GainMixer| instance.
+ GainMixer gain_mixer_;
+
+ // Encoding coefficient values to be applied to encode the input.
+ std::vector<float> coefficients_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_AMBISONIC_MIXING_ENCODER_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_mixing_encoder_node_test.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_mixing_encoder_node_test.cc
new file mode 100644
index 000000000..1977ab646
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/ambisonic_mixing_encoder_node_test.cc
@@ -0,0 +1,150 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/ambisonic_mixing_encoder_node.h"
+
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "ambisonics/ambisonic_lookup_table.h"
+#include "ambisonics/utils.h"
+#include "api/resonance_audio_api.h"
+#include "base/constants_and_types.h"
+#include "base/object_transform.h"
+#include "config/source_config.h"
+#include "graph/buffered_source_node.h"
+#include "node/sink_node.h"
+#include "node/source_node.h"
+#include "utils/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+// Number of frames per input buffer.
+const size_t kFramesPerBuffer = 16;
+
+// Simulated system sample rate.
+const int kSampleRate = 48000;
+
+} // namespace
+
+// Provides unit tests for |AmbisonicMixingEncoderNode|.
+class AmbisonicMixingEncoderNodeTest
+ : public ::testing::TestWithParam<SourceGraphConfig> {
+ protected:
+ AmbisonicMixingEncoderNodeTest()
+ : system_settings_(kNumStereoChannels, kFramesPerBuffer, kSampleRate),
+ lookup_table_(kMaxSupportedAmbisonicOrder) {}
+
+ void SetUp() override {
+ const auto source_config = GetParam();
+ ambisonic_order_ = source_config.ambisonic_order;
+ ambisonic_mixing_encoder_node_ =
+ std::make_shared<AmbisonicMixingEncoderNode>(
+ system_settings_, lookup_table_, ambisonic_order_);
+ }
+
+ const AudioBuffer* ProcessMultipleInputs(size_t num_sources,
+ const WorldPosition& position,
+ float spread_deg) {
+ // Create the node graph, adding input nodes to the Ambisonic Mixing Encoder
+ // Node.
+ buffered_source_nodes_.clear();
+ auto parameters_manager = system_settings_.GetSourceParametersManager();
+ for (size_t i = 0; i < num_sources; ++i) {
+ buffered_source_nodes_.emplace_back(std::make_shared<BufferedSourceNode>(
+ static_cast<SourceId>(i) /*source id*/, kNumMonoChannels,
+ kFramesPerBuffer));
+ parameters_manager->Register(static_cast<SourceId>(i));
+ }
+ const AudioBuffer* output_buffer = nullptr;
+
+ for (auto& input_node : buffered_source_nodes_) {
+ ambisonic_mixing_encoder_node_->Connect(input_node);
+ }
+ auto output_node = std::make_shared<SinkNode>();
+ output_node->Connect(ambisonic_mixing_encoder_node_);
+
+ // Input data containing unit pulses.
+ const std::vector<float> kInputData(kFramesPerBuffer, 1.0f);
+
+ for (size_t index = 0; index < buffered_source_nodes_.size(); ++index) {
+ AudioBuffer* input_buffer =
+ buffered_source_nodes_[index]
+ ->GetMutableAudioBufferAndSetNewBufferFlag();
+ (*input_buffer)[0] = kInputData;
+ auto source_parameters = parameters_manager->GetMutableParameters(
+ static_cast<SourceId>(index));
+ source_parameters->object_transform.position = position;
+ source_parameters->spread_deg = spread_deg;
+ }
+
+ const std::vector<const AudioBuffer*>& buffer_vector =
+ output_node->ReadInputs();
+ if (!buffer_vector.empty()) {
+ DCHECK_EQ(buffer_vector.size(), 1U);
+ output_buffer = buffer_vector.front();
+ }
+
+ return output_buffer;
+ }
+
+ SystemSettings system_settings_;
+ int ambisonic_order_;
+ AmbisonicLookupTable lookup_table_;
+ std::shared_ptr<AmbisonicMixingEncoderNode> ambisonic_mixing_encoder_node_;
+ std::vector<std::shared_ptr<BufferedSourceNode>> buffered_source_nodes_;
+};
+
+// Tests that a number of sound objects encoded in the same direction are
+// correctly combined into an output buffer.
+TEST_P(AmbisonicMixingEncoderNodeTest, TestEncodeAndMix) {
+ // Number of sources to encode and mix.
+ const size_t kNumSources = 4;
+ // Minimum angular source spread of 0 ensures that no gain correction
+ // coefficients are to be applied to the Ambisonic encoding coefficients.
+ const float kSpreadDeg = 0.0f;
+ // Arbitrary world position of sound sources corresponding to the 36 degrees
+ // azimuth and 18 degrees elevation.
+ const WorldPosition kPosition =
+ WorldPosition(-0.55901699f, 0.30901699f, -0.76942088f);
+ // Expected Ambisonic output for a single source at the above position (as
+ // generated using /matlab/ambisonics/ambix/ambencode.m Matlab function):
+ const std::vector<float> kExpectedSingleSourceOutput = {
+ 1.0f, 0.55901699f, 0.30901699f, 0.76942088f,
+ 0.74498856f, 0.29920441f, -0.35676274f, 0.41181955f,
+ 0.24206145f, 0.64679299f, 0.51477443f, -0.17888019f,
+ -0.38975424f, -0.24620746f, 0.16726035f, -0.21015578f};
+
+ const AudioBuffer* output_buffer =
+ ProcessMultipleInputs(kNumSources, kPosition, kSpreadDeg);
+
+ const size_t num_channels = GetNumPeriphonicComponents(ambisonic_order_);
+
+ for (size_t i = 0; i < num_channels; ++i) {
+ EXPECT_NEAR(
+ kExpectedSingleSourceOutput[i] * static_cast<float>(kNumSources),
+ (*output_buffer)[i][kFramesPerBuffer - 1], kEpsilonFloat);
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(TestParameters, AmbisonicMixingEncoderNodeTest,
+ testing::Values(BinauralLowQualityConfig(),
+ BinauralMediumQualityConfig(),
+ BinauralHighQualityConfig()));
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.cc
new file mode 100644
index 000000000..79889fabd
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.cc
@@ -0,0 +1,495 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/binaural_surround_renderer_impl.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "base/misc_math.h"
+#include "base/simd_utils.h"
+#include "base/spherical_angle.h"
+#include "graph/resonance_audio_api_impl.h"
+#include "platforms/common/room_effects_utils.h"
+#include "platforms/common/room_properties.h"
+#include "utils/planar_interleaved_conversion.h"
+
+namespace vraudio {
+
+namespace {
+
+// Maximum number of audio buffers in buffer queue.
+const size_t kNumMaxBuffers = 64;
+
+// Output gain, to avoid clipping of individual virtual speaker channels.
+const float kGain = 0.5f;
+
+} // namespace
+
+BinauralSurroundRendererImpl::BinauralSurroundRendererImpl(
+ size_t frames_per_buffer, int sample_rate_hz)
+ :
+ resonance_audio_api_(nullptr),
+ frames_per_buffer_(frames_per_buffer),
+ sample_rate_hz_(sample_rate_hz),
+ surround_format_(kInvalid),
+ num_input_channels_(0),
+ output_buffer_(kNumStereoChannels, frames_per_buffer),
+ total_frames_buffered_(0),
+ num_zero_padded_frames_(0),
+ output_gain_(1.0f) {
+}
+
+bool BinauralSurroundRendererImpl::Init(SurroundFormat surround_format) {
+ surround_format_ = surround_format;
+ num_input_channels_ =
+ GetExpectedNumChannelsFromSurroundFormat(surround_format);
+
+ temp_planar_buffer_ptrs_.resize(num_input_channels_);
+
+ input_audio_buffer_queue_.reset(new ThreadsafeFifo<AudioBuffer>(
+ kNumMaxBuffers, AudioBuffer(num_input_channels_, frames_per_buffer_)));
+
+ buffer_partitioner_.reset(new BufferPartitioner(
+ num_input_channels_, frames_per_buffer_,
+ std::bind(&BinauralSurroundRendererImpl::BufferPartitionerCallback, this,
+ std::placeholders::_1)));
+
+ buffer_unpartitioner_.reset(new BufferUnpartitioner(
+ kNumStereoChannels, frames_per_buffer_,
+ std::bind(&BinauralSurroundRendererImpl::ProcessBuffer, this)));
+
+ resonance_audio_api_.reset(CreateResonanceAudioApi(
+ kNumStereoChannels, frames_per_buffer_, sample_rate_hz_));
+
+ if (surround_format == kSurroundMono || surround_format == kSurroundStereo ||
+ surround_format == kSurroundFiveDotOne ||
+ surround_format == kSurroundSevenDotOne) {
+ InitializeRoomReverb();
+ }
+ // Initialize rendering mode.
+ switch (surround_format) {
+ case kSurroundMono:
+ InitializeBinauralMono();
+ break;
+ case kSurroundStereo:
+ InitializeBinauralStereo();
+ break;
+ case kSurroundFiveDotOne:
+ InitializeBinauralSurround5dot1();
+ break;
+ case kSurroundSevenDotOne:
+ InitializeBinauralSurround7dot1();
+ break;
+ case kFirstOrderAmbisonics:
+ case kSecondOrderAmbisonics:
+ case kThirdOrderAmbisonics:
+ InitializeAmbisonics();
+ break;
+ case kFirstOrderAmbisonicsWithNonDiegeticStereo:
+ case kSecondOrderAmbisonicsWithNonDiegeticStereo:
+ case kThirdOrderAmbisonicsWithNonDiegeticStereo:
+ InitializeAmbisonicsWithNonDiegeticStereo();
+ break;
+ default:
+ LOG(FATAL) << "Undefined rendering mode";
+ return false;
+ break;
+ }
+ return true;
+}
+
+BinauralSurroundRendererImpl::BinauralSurroundRendererImpl()
+ :
+ resonance_audio_api_(nullptr),
+ frames_per_buffer_(0),
+ sample_rate_hz_(0),
+ total_frames_buffered_(0),
+ num_zero_padded_frames_(0) {
+}
+
+AudioBuffer* BinauralSurroundRendererImpl::BufferPartitionerCallback(
+ AudioBuffer* processed_buffer) {
+ if (processed_buffer != nullptr) {
+ input_audio_buffer_queue_->ReleaseInputObject(processed_buffer);
+ }
+ DCHECK(!input_audio_buffer_queue_->Full());
+ return input_audio_buffer_queue_->AcquireInputObject();
+}
+
+void BinauralSurroundRendererImpl::SetStereoSpeakerMode(bool enabled) {
+ resonance_audio_api_->SetStereoSpeakerMode(enabled);
+}
+
+size_t BinauralSurroundRendererImpl::GetExpectedNumChannelsFromSurroundFormat(
+ SurroundFormat surround_format) {
+ switch (surround_format) {
+ case kSurroundMono:
+ return kNumMonoChannels;
+ case kSurroundStereo:
+ return kNumStereoChannels;
+ case kSurroundFiveDotOne:
+ return kNumSurroundFiveDotOneChannels;
+ case kSurroundSevenDotOne:
+ return kNumSurroundSevenDotOneChannels;
+ case kFirstOrderAmbisonics:
+ return kNumFirstOrderAmbisonicChannels;
+ case kSecondOrderAmbisonics:
+ return kNumSecondOrderAmbisonicChannels;
+ case kThirdOrderAmbisonics:
+ return kNumThirdOrderAmbisonicChannels;
+ case kFirstOrderAmbisonicsWithNonDiegeticStereo:
+ return kNumFirstOrderAmbisonicChannels + kNumStereoChannels;
+ case kSecondOrderAmbisonicsWithNonDiegeticStereo:
+ return kNumSecondOrderAmbisonicChannels + kNumStereoChannels;
+ case kThirdOrderAmbisonicsWithNonDiegeticStereo:
+ return kNumThirdOrderAmbisonicChannels + kNumStereoChannels;
+ default:
+ LOG(FATAL) << "Undefined surround format mode";
+ return false;
+ break;
+ }
+ return 0;
+}
+
+void BinauralSurroundRendererImpl::InitializeBinauralMono() {
+ source_ids_.resize(kNumMonoChannels);
+ // Front (0 degrees):
+ source_ids_[0] = CreateSoundObject(0.0f);
+ output_gain_ = kGain;
+}
+
+void BinauralSurroundRendererImpl::InitializeBinauralStereo() {
+
+ source_ids_.resize(kNumStereoChannels);
+ // Front left (30 degrees):
+ source_ids_[0] = CreateSoundObject(30.0f);
+ // Front right (-30 degrees):
+ source_ids_[1] = CreateSoundObject(-30.0f);
+ output_gain_ = kGain;
+}
+
+void BinauralSurroundRendererImpl::InitializeBinauralSurround5dot1() {
+ source_ids_.resize(kNumSurroundFiveDotOneChannels);
+ // Left (30 degrees):
+ source_ids_[0] = CreateSoundObject(30.0f);
+ // Right (-30 degrees):
+ source_ids_[1] = CreateSoundObject(-30.0f);
+ // Center (0 degrees):
+ source_ids_[2] = CreateSoundObject(0.0f);
+ // Low frequency effects at front center:
+ source_ids_[3] = CreateSoundObject(0.0f);
+ // Left surround (110 degrees):
+ source_ids_[4] = CreateSoundObject(110.0f);
+ // Right surround (-110 degrees):
+ source_ids_[5] = CreateSoundObject(-110.0f);
+ output_gain_ = kGain;
+}
+
+void BinauralSurroundRendererImpl::InitializeBinauralSurround7dot1() {
+ source_ids_.resize(kNumSurroundSevenDotOneChannels);
+ // Left (30 degrees):
+ source_ids_[0] = CreateSoundObject(30.0f);
+ // Right (-30 degrees):
+ source_ids_[1] = CreateSoundObject(-30.0f);
+ // Center (0 degrees):
+ source_ids_[2] = CreateSoundObject(0.0f);
+ // Low frequency effects at front center:
+ source_ids_[3] = CreateSoundObject(0.0f);
+ // Left surround 1 (90 degrees):
+ source_ids_[4] = CreateSoundObject(90.0f);
+ // Right surround 1 (-90 degrees):
+ source_ids_[5] = CreateSoundObject(-90.0f);
+ // Left surround 2 (150 degrees):
+ source_ids_[6] = CreateSoundObject(150.0f);
+ // Right surround 2 (-150 degrees):
+ source_ids_[7] = CreateSoundObject(-150.0f);
+ output_gain_ = kGain;
+}
+
+void BinauralSurroundRendererImpl::InitializeAmbisonics() {
+ source_ids_.resize(1);
+ source_ids_[0] =
+ resonance_audio_api_->CreateAmbisonicSource(num_input_channels_);
+}
+
+void BinauralSurroundRendererImpl::InitializeAmbisonicsWithNonDiegeticStereo() {
+ source_ids_.resize(2);
+ CHECK_GT(num_input_channels_, kNumStereoChannels);
+ source_ids_[0] = resonance_audio_api_->CreateAmbisonicSource(
+ num_input_channels_ - kNumStereoChannels);
+ source_ids_[1] = resonance_audio_api_->CreateStereoSource(kNumStereoChannels);
+}
+
+SourceId BinauralSurroundRendererImpl::CreateSoundObject(float azimuth_deg) {
+ static const float kZeroElevation = 0.0f;
+ auto speaker_position =
+ vraudio::SphericalAngle::FromDegrees(azimuth_deg, kZeroElevation)
+ .GetWorldPositionOnUnitSphere();
+ const SourceId source_id = resonance_audio_api_->CreateSoundObjectSource(
+ RenderingMode::kBinauralHighQuality);
+ resonance_audio_api_->SetSourcePosition(
+ source_id, speaker_position[0], speaker_position[1], speaker_position[2]);
+ return source_id;
+}
+
+void BinauralSurroundRendererImpl::InitializeRoomReverb() {
+ // The following settings has been applied based on AESTD1001.1.01-10.
+ RoomProperties room_properties;
+ room_properties.dimensions[0] = 9.54f;
+ room_properties.dimensions[1] = 6.0f;
+ room_properties.dimensions[2] = 15.12f;
+ room_properties.reverb_brightness = 0.0f;
+ room_properties.reflection_scalar = 1.0f;
+ // Reduce reverb gain to compensate for virtual speakers gain.
+ room_properties.reverb_gain = output_gain_;
+ for (size_t i = 0; i < kNumRoomSurfaces; ++i) {
+ room_properties.material_names[i] = MaterialName::kUniform;
+ }
+ resonance_audio_api_->SetReflectionProperties(
+ ComputeReflectionProperties(room_properties));
+ resonance_audio_api_->SetReverbProperties(
+ ComputeReverbProperties(room_properties));
+ resonance_audio_api_->EnableRoomEffects(true);
+}
+
+size_t BinauralSurroundRendererImpl::GetNumAvailableFramesInInputBuffer()
+ const {
+ DCHECK_NE(surround_format_, kInvalid);
+ if (num_zero_padded_frames_ > 0) {
+ // Zero padded output buffers must be consumed prior to
+ // |AddInterleavedBuffer| calls;
+ return 0;
+ }
+ if (input_audio_buffer_queue_->Full()) {
+ return 0;
+ }
+ // Subtract two buffers from the available input slots to ensure the buffer
+ // partitioner can be flushed at any time while keeping an extra buffer
+ // available in the |buffer_partitioner_| callback for the next incoming data.
+ const size_t num_frames_available_in_input_slots =
+ (kNumMaxBuffers - input_audio_buffer_queue_->Size() - 2) *
+ frames_per_buffer_;
+ DCHECK_GT(frames_per_buffer_, buffer_partitioner_->GetNumBufferedFrames());
+ const size_t num_frames_available_in_buffer_partitioner =
+ frames_per_buffer_ - buffer_partitioner_->GetNumBufferedFrames();
+ return num_frames_available_in_input_slots +
+ num_frames_available_in_buffer_partitioner;
+}
+
+size_t BinauralSurroundRendererImpl::AddInterleavedInput(
+ const int16* input_buffer_ptr, size_t num_channels, size_t num_frames) {
+ return AddInputBufferTemplated<const int16*>(input_buffer_ptr, num_channels,
+ num_frames);
+}
+
+size_t BinauralSurroundRendererImpl::AddInterleavedInput(
+ const float* input_buffer_ptr, size_t num_channels, size_t num_frames) {
+ return AddInputBufferTemplated<const float*>(input_buffer_ptr, num_channels,
+ num_frames);
+}
+
+size_t BinauralSurroundRendererImpl::AddPlanarInput(
+ const int16* const* input_buffer_ptrs, size_t num_channels,
+ size_t num_frames) {
+ return AddInputBufferTemplated<const int16* const*>(input_buffer_ptrs,
+ num_channels, num_frames);
+}
+
+size_t BinauralSurroundRendererImpl::AddPlanarInput(
+ const float* const* input_buffer_ptrs, size_t num_channels,
+ size_t num_frames) {
+ return AddInputBufferTemplated<const float* const*>(input_buffer_ptrs,
+ num_channels, num_frames);
+}
+
+template <typename BufferType>
+size_t BinauralSurroundRendererImpl::AddInputBufferTemplated(
+ const BufferType input_buffer_ptr, size_t num_channels, size_t num_frames) {
+ DCHECK_NE(surround_format_, kInvalid);
+ if (num_channels != num_input_channels_) {
+ LOG(WARNING) << "Invalid number of input channels";
+ return 0;
+ }
+
+ if (num_zero_padded_frames_ > 0) {
+ LOG(WARNING) << "Zero padded output buffers must be consumed prior to "
+ "|AddInterleavedBuffer| calls";
+ return 0;
+ }
+ const size_t num_available_input_frames =
+ std::min(num_frames, GetNumAvailableFramesInInputBuffer());
+
+ buffer_partitioner_->AddBuffer(input_buffer_ptr, num_input_channels_,
+ num_available_input_frames);
+ total_frames_buffered_ += num_available_input_frames;
+ return num_available_input_frames;
+}
+
+size_t BinauralSurroundRendererImpl::GetAvailableFramesInStereoOutputBuffer()
+ const {
+ const size_t num_available_samples_in_buffers =
+ (input_audio_buffer_queue_->Size() * frames_per_buffer_) +
+ buffer_unpartitioner_->GetNumBufferedFrames();
+ return std::min(total_frames_buffered_, num_available_samples_in_buffers);
+}
+
+size_t BinauralSurroundRendererImpl::GetInterleavedStereoOutput(
+ int16* output_buffer_ptr, size_t num_frames) {
+ return GetStereoOutputBufferTemplated<int16*>(output_buffer_ptr, num_frames);
+}
+
+size_t BinauralSurroundRendererImpl::GetInterleavedStereoOutput(
+ float* output_buffer_ptr, size_t num_frames) {
+ return GetStereoOutputBufferTemplated<float*>(output_buffer_ptr, num_frames);
+}
+
+size_t BinauralSurroundRendererImpl::GetPlanarStereoOutput(
+ int16** output_buffer_ptrs, size_t num_frames) {
+ return GetStereoOutputBufferTemplated<int16**>(output_buffer_ptrs,
+ num_frames);
+}
+
+size_t BinauralSurroundRendererImpl::GetPlanarStereoOutput(
+ float** output_buffer_ptrs, size_t num_frames) {
+ return GetStereoOutputBufferTemplated<float**>(output_buffer_ptrs,
+ num_frames);
+}
+
+template <typename BufferType>
+size_t BinauralSurroundRendererImpl::GetStereoOutputBufferTemplated(
+ BufferType output_buffer_ptr, size_t num_frames) {
+ DCHECK_NE(surround_format_, kInvalid);
+ const size_t num_frames_available = GetAvailableFramesInStereoOutputBuffer();
+ size_t num_frames_to_be_processed =
+ std::min(num_frames_available, num_frames);
+ if (num_frames_to_be_processed > total_frames_buffered_) {
+ // Avoid outputting zero padded input frames from |TriggerProcessing|
+ // calls.
+ num_frames_to_be_processed = total_frames_buffered_;
+ }
+
+ const size_t num_frames_written = buffer_unpartitioner_->GetBuffer(
+ output_buffer_ptr, kNumStereoChannels, num_frames_to_be_processed);
+
+ DCHECK_GE(total_frames_buffered_, num_frames_written);
+ total_frames_buffered_ -= num_frames_written;
+
+ if (total_frames_buffered_ == 0) {
+ // Clear zero padded frames from |TriggerProcessing| calls.
+ buffer_unpartitioner_->Clear();
+ num_zero_padded_frames_ = 0;
+ }
+
+ return num_frames_written;
+}
+
+void BinauralSurroundRendererImpl::Clear() {
+ input_audio_buffer_queue_->Clear();
+ buffer_partitioner_->Clear();
+ buffer_unpartitioner_->Clear();
+ total_frames_buffered_ = 0;
+ num_zero_padded_frames_ = 0;
+}
+
+bool BinauralSurroundRendererImpl::TriggerProcessing() {
+ if (num_zero_padded_frames_ > 0) {
+ LOG(WARNING) << "Zero padded output buffers must be consumed prior to "
+ "|TriggerProcessing| calls";
+ return false;
+ }
+ num_zero_padded_frames_ = buffer_partitioner_->Flush();
+ return num_zero_padded_frames_ > 0;
+}
+
+void BinauralSurroundRendererImpl::SetHeadRotation(float w, float x, float y,
+ float z) {
+ resonance_audio_api_->SetHeadRotation(x, y, z, w);
+}
+
+AudioBuffer* BinauralSurroundRendererImpl::ProcessBuffer() {
+ if (input_audio_buffer_queue_->Size() == 0) {
+ LOG(WARNING) << "Buffer underflow detected";
+ return nullptr;
+ }
+
+ const AudioBuffer* input = input_audio_buffer_queue_->AcquireOutputObject();
+ DCHECK_EQ(input->num_frames(), frames_per_buffer_);
+ DCHECK_EQ(num_input_channels_, input->num_channels());
+ GetRawChannelDataPointersFromAudioBuffer(*input, &temp_planar_buffer_ptrs_);
+ // Initialize surround rendering.
+ const float* planar_ptr;
+
+ switch (surround_format_) {
+ case kSurroundMono:
+ case kSurroundStereo:
+ case kSurroundFiveDotOne:
+ case kSurroundSevenDotOne:
+ DCHECK_EQ(input->num_channels(), source_ids_.size());
+ for (size_t source_itr = 0; source_itr < source_ids_.size();
+ ++source_itr) {
+ planar_ptr = (*input)[source_itr].begin();
+ resonance_audio_api_->SetPlanarBuffer(source_ids_[source_itr],
+ &planar_ptr, kNumMonoChannels,
+ input->num_frames());
+ }
+ break;
+ case kFirstOrderAmbisonics:
+ case kSecondOrderAmbisonics:
+ case kThirdOrderAmbisonics:
+ DCHECK_EQ(source_ids_.size(), 1U);
+ resonance_audio_api_->SetPlanarBuffer(
+ source_ids_[0], temp_planar_buffer_ptrs_.data(),
+ input->num_channels(), input->num_frames());
+ break;
+ case kFirstOrderAmbisonicsWithNonDiegeticStereo:
+ case kSecondOrderAmbisonicsWithNonDiegeticStereo:
+ case kThirdOrderAmbisonicsWithNonDiegeticStereo:
+ DCHECK_EQ(source_ids_.size(), 2U);
+ DCHECK_GT(input->num_channels(), kNumStereoChannels);
+ static_cast<ResonanceAudioApiImpl*>(resonance_audio_api_.get())
+ ->SetPlanarBuffer(source_ids_[0], temp_planar_buffer_ptrs_.data(),
+ input->num_channels() - kNumStereoChannels,
+ input->num_frames());
+ static_cast<ResonanceAudioApiImpl*>(resonance_audio_api_.get())
+ ->SetPlanarBuffer(source_ids_[1],
+ temp_planar_buffer_ptrs_.data() +
+ (input->num_channels() - kNumStereoChannels),
+ kNumStereoChannels, input->num_frames());
+ break;
+ default:
+ LOG(FATAL) << "Undefined surround format";
+ break;
+ }
+
+ // Create a copy of the processed |AudioBuffer| to pass it to output buffer
+ // queue.
+ auto* const vraudio_api_impl =
+ static_cast<ResonanceAudioApiImpl*>(resonance_audio_api_.get());
+ vraudio_api_impl->ProcessNextBuffer();
+ output_buffer_ = *vraudio_api_impl->GetStereoOutputBuffer();
+
+ if (output_gain_ != 1.0f) {
+ for (AudioBuffer::Channel& channel : output_buffer_) {
+ ScalarMultiply(output_buffer_.num_frames(), output_gain_, channel.begin(),
+ channel.begin());
+ }
+ }
+ input_audio_buffer_queue_->ReleaseOutputObject(input);
+ return &output_buffer_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.h b/src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.h
new file mode 100644
index 000000000..c4916d664
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl.h
@@ -0,0 +1,190 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_BINAURAL_SURROUND_RENDERER_IMPL_H_
+#define RESONANCE_AUDIO_GRAPH_BINAURAL_SURROUND_RENDERER_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#include "api/binaural_surround_renderer.h"
+#include "api/resonance_audio_api.h"
+#include "base/constants_and_types.h"
+#include "utils/buffer_partitioner.h"
+#include "utils/buffer_unpartitioner.h"
+#include "utils/threadsafe_fifo.h"
+
+namespace vraudio {
+
+// Renders virtual surround sound as well as ambisonic soundfields to binaural
+// stereo.
+class BinauralSurroundRendererImpl : public BinauralSurroundRenderer {
+ public:
+ // Constructor.
+ //
+ // @param frames_per_buffer Number of frames in output buffer.
+ // @param sample_rate_hz Sample rate of audio buffers.
+ BinauralSurroundRendererImpl(size_t frames_per_buffer, int sample_rate_hz);
+
+ ~BinauralSurroundRendererImpl() override{};
+
+ // Initializes surround sound decoding.
+ //
+ // @param surround_format Surround sound input format.
+ // @return True on success.
+ bool Init(SurroundFormat surround_format);
+
+ // Implements |AudioRenderer| interface.
+ void SetStereoSpeakerMode(bool enabled) override;
+ size_t GetNumAvailableFramesInInputBuffer() const override;
+ size_t AddInterleavedInput(const int16* input_buffer_ptr, size_t num_channels,
+ size_t num_frames) override;
+ size_t AddInterleavedInput(const float* input_buffer_ptr, size_t num_channels,
+ size_t num_frames) override;
+ size_t AddPlanarInput(const int16* const* input_buffer_ptrs,
+ size_t num_channels, size_t num_frames) override;
+ size_t AddPlanarInput(const float* const* input_buffer_ptrs,
+ size_t num_channels, size_t num_frames) override;
+ size_t GetAvailableFramesInStereoOutputBuffer() const override;
+ size_t GetInterleavedStereoOutput(int16* output_buffer_ptr,
+ size_t num_frames) override;
+ size_t GetInterleavedStereoOutput(float* output_buffer_ptr,
+ size_t num_frames) override;
+ size_t GetPlanarStereoOutput(int16** output_buffer_ptrs,
+ size_t num_frames) override;
+ size_t GetPlanarStereoOutput(float** output_buffer_ptrs,
+ size_t num_frames) override;
+ bool TriggerProcessing() override;
+ void Clear() override;
+ void SetHeadRotation(float w, float x, float y, float z) override;
+
+ protected:
+ // Protected default constructor for mock tests.
+ BinauralSurroundRendererImpl();
+
+ private:
+ // Callback triggered by |buffer_partitioner_| whenever a new |AudioBuffer|
+ // has been generated.
+ //
+ // @param processed_buffer Pointer to processed buffer.
+ // @return Pointer to next |AudioBuffer| to be filled up.
+ AudioBuffer* BufferPartitionerCallback(AudioBuffer* processed_buffer);
+
+ // Helper method to implement |AddInterleavedInput| independently from the
+ // sample type.
+ //
+ // @tparam BufferType Input buffer type.
+ // @param input_buffer_ptr Pointer to interleaved input data.
+ // @param num_channels Number of channels in input buffer.
+ // @param num_frames Number of frames in input buffer.
+ // @return The number of consumed samples.
+ template <typename BufferType>
+ size_t AddInputBufferTemplated(const BufferType input_buffer_ptr,
+ size_t num_channels, size_t num_frames);
+
+ // Helper method to implement |GetInterleavedOutput| independently from the
+ // sample type.
+ //
+ // @tparam BufferType Output buffer type.
+ // @param output_buffer_ptr Pointer to allocated interleaved output buffer.
+ // @param num_frames Size of output buffer in frames.
+ // @return The number of consumed frames.
+ template <typename BufferType>
+ size_t GetStereoOutputBufferTemplated(BufferType output_buffer_ptr,
+ size_t num_frames);
+
+ // Helper method to obtain the expected number of audio channels for a given
+ // surround format.
+ //
+ // @param surround_format Surround format query.
+ // @return Number of audio channels.
+ static size_t GetExpectedNumChannelsFromSurroundFormat(
+ SurroundFormat surround_format);
+
+ // Process method executed by |buffer_unpartitioner_|.
+ AudioBuffer* ProcessBuffer();
+
+ // Initializes binaural mono rendering.
+ void InitializeBinauralMono();
+
+ // Initializes binaural stereo rendering.
+ void InitializeBinauralStereo();
+
+ // Initializes binaural 5.1 rendering.
+ void InitializeBinauralSurround5dot1();
+
+ // Initializes binaural 7.1 rendering.
+ void InitializeBinauralSurround7dot1();
+
+ // Initializes binaural ambisonic rendering.
+ void InitializeAmbisonics();
+
+ // Initializes binaural ambisonic rendering with non-diegetic stereo.
+ void InitializeAmbisonicsWithNonDiegeticStereo();
+
+ // Creates a sound object at given angle within the horizontal listener plane.
+ SourceId CreateSoundObject(float azimuth_deg);
+
+ // Initializes room reverb for virtual surround sound rendering.
+ void InitializeRoomReverb();
+
+ // ResonanceAudioApi instance.
+ std::unique_ptr<ResonanceAudioApi> resonance_audio_api_;
+
+ // Frames per buffer.
+ const size_t frames_per_buffer_;
+
+ // System sample rate.
+ const int sample_rate_hz_;
+
+ // Selected surround sound format.
+ SurroundFormat surround_format_;
+
+ // Number of input channels.
+ size_t num_input_channels_;
+
+ // Partitions input buffers into |AudioBuffer|s.
+ std::unique_ptr<BufferPartitioner> buffer_partitioner_;
+
+ // Buffer queue containing partitioned input |AudioBuffer|s.
+ std::unique_ptr<ThreadsafeFifo<AudioBuffer>> input_audio_buffer_queue_;
+
+ // Binaural stereo output buffer.
+ AudioBuffer output_buffer_;
+
+ // Unpartitions processed |AudioBuffer|s into interleaved output buffers.
+ std::unique_ptr<BufferUnpartitioner> buffer_unpartitioner_;
+
+ // Vector containing the source ids of all rendered sound sources.
+ std::vector<SourceId> source_ids_;
+
+ // Total number of frames currently buffered.
+ size_t total_frames_buffered_;
+
+ // Total number of zero padded frames from |TriggerProcessing| calls.
+ size_t num_zero_padded_frames_;
+
+ // Temporary buffer to store pointers to planar ambisonic and stereo channels.
+ std::vector<const float*> temp_planar_buffer_ptrs_;
+
+ // Global output gain adjustment, to avoid clipping of individual channels
+ // in virtual speaker modes.
+ float output_gain_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_BINAURAL_SURROUND_RENDERER_IMPL_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl_test.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl_test.cc
new file mode 100644
index 000000000..98652ca3e
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/binaural_surround_renderer_impl_test.cc
@@ -0,0 +1,202 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/binaural_surround_renderer_impl.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+class BinauralSurroundRendererTest
+ : public ::testing::TestWithParam<
+ BinauralSurroundRenderer::SurroundFormat> {
+ protected:
+ BinauralSurroundRendererTest() {}
+
+ // Virtual methods from ::testing::Test
+ ~BinauralSurroundRendererTest() override {}
+ void SetUp() override {}
+ void TearDown() override {}
+
+ void InitBinauralSurroundRenderer(size_t frames_per_buffer,
+ int sample_rate_hz) {
+ binaural_surround_renderer_api_.reset(
+ new BinauralSurroundRendererImpl(frames_per_buffer, sample_rate_hz));
+ }
+
+ // Processes an interleaved input vector and returns interleaved binaural
+ // stereo output.
+ std::vector<float> ProcessInterleaved(
+ const std::vector<float>& interleaved_input, size_t num_channels,
+ size_t frames_per_buffer) {
+ EXPECT_EQ(interleaved_input.size() % (num_channels * frames_per_buffer),
+ 0U);
+ std::vector<float> interleaved_output;
+ const size_t num_buffers =
+ interleaved_input.size() / (num_channels * frames_per_buffer);
+ for (size_t b = 0; b < num_buffers; ++b) {
+ const float* interleaved_input_ptr =
+ interleaved_input.data() + b * num_channels * frames_per_buffer;
+ binaural_surround_renderer_api_->AddInterleavedInput(
+ interleaved_input_ptr, num_channels, frames_per_buffer);
+
+ interleaved_output.resize((b + 1) * kNumStereoChannels *
+ frames_per_buffer);
+ float* interleaved_output_ptr =
+ interleaved_output.data() +
+ b * kNumStereoChannels * frames_per_buffer;
+
+ EXPECT_EQ(binaural_surround_renderer_api_->GetInterleavedStereoOutput(
+ interleaved_output_ptr, frames_per_buffer),
+ frames_per_buffer);
+ }
+ return interleaved_output;
+ }
+
+ // Calculates the maximum absolute difference between adjacent samples in an
+ // interleaved audio buffer.
+ float GetMaximumSampleDiff(const std::vector<float>& interleaved_input,
+ size_t num_channels) {
+ if (interleaved_input.size() <= num_channels) {
+ return 0.0f;
+ }
+
+ float max_sample_diff = 0.0f;
+ std::vector<float> prev_samples(num_channels);
+ for (size_t i = 0; i < num_channels; ++i) {
+ prev_samples[i] = interleaved_input[i];
+ }
+ for (size_t i = num_channels; i < interleaved_input.size(); ++i) {
+ const size_t channel = i % num_channels;
+ max_sample_diff =
+ std::max(max_sample_diff,
+ std::abs(interleaved_input[i] - prev_samples[channel]));
+ prev_samples[channel] = interleaved_input[i];
+ }
+ return max_sample_diff;
+ }
+
+ // Helper to return the number of input channels for a given surround format.
+ size_t GetNumInputChannelsForSurroundFormat(
+ BinauralSurroundRenderer::SurroundFormat format) {
+ switch (format) {
+ case BinauralSurroundRenderer::SurroundFormat::kSurroundMono:
+ return kNumMonoChannels;
+ break;
+ case BinauralSurroundRenderer::SurroundFormat::kSurroundStereo:
+ return kNumStereoChannels;
+ break;
+ case BinauralSurroundRenderer::SurroundFormat::kSurroundFiveDotOne:
+ return kNumSurroundFiveDotOneChannels;
+ break;
+ case BinauralSurroundRenderer::SurroundFormat::kSurroundSevenDotOne:
+ return kNumSurroundSevenDotOneChannels;
+ break;
+ case BinauralSurroundRenderer::SurroundFormat::kFirstOrderAmbisonics:
+ return kNumFirstOrderAmbisonicChannels;
+ break;
+ case BinauralSurroundRenderer::SurroundFormat::
+ kFirstOrderAmbisonicsWithNonDiegeticStereo:
+ return kNumFirstOrderAmbisonicWithNonDiegeticStereoChannels;
+ break;
+ case BinauralSurroundRenderer::SurroundFormat::kSecondOrderAmbisonics:
+ return kNumSecondOrderAmbisonicChannels;
+ break;
+ case BinauralSurroundRenderer::SurroundFormat::
+ kSecondOrderAmbisonicsWithNonDiegeticStereo:
+ return kNumSecondOrderAmbisonicWithNonDiegeticStereoChannels;
+ break;
+ case BinauralSurroundRenderer::SurroundFormat::kThirdOrderAmbisonics:
+ return kNumThirdOrderAmbisonicChannels;
+ break;
+ case BinauralSurroundRenderer::SurroundFormat::
+ kThirdOrderAmbisonicsWithNonDiegeticStereo:
+ return kNumThirdOrderAmbisonicWithNonDiegeticStereoChannels;
+ break;
+ default:
+ break;
+ }
+ LOG(FATAL) << "Unexpected format";
+ return 0;
+ }
+
+ // VR Audio API instance to test.
+ std::unique_ptr<BinauralSurroundRendererImpl> binaural_surround_renderer_api_;
+};
+
+// Processes an input signal with constant DC offset and scans the output for
+// drop outs and noise.
+TEST_P(BinauralSurroundRendererTest, DropOutGlitchTesting) {
+
+ const std::vector<int> kTestSampleRates = {44100, 48000};
+ const std::vector<int> kTestBufferSizes = {256, 413, 512};
+ const size_t kNumTestBuffers = 16;
+ const size_t kNumNumChannels =
+ GetNumInputChannelsForSurroundFormat(GetParam());
+
+ for (int sample_rate : kTestSampleRates) {
+ for (int buffer_size : kTestBufferSizes) {
+ InitBinauralSurroundRenderer(buffer_size, sample_rate);
+ binaural_surround_renderer_api_->Init(GetParam());
+
+ // Create DC input signal with magnitude 0.5f.
+ const std::vector<float> interleaved_dc_signal(
+ buffer_size * kNumNumChannels * kNumTestBuffers, 0.5f);
+
+ std::vector<float> interleaved_output = ProcessInterleaved(
+ interleaved_dc_signal, kNumNumChannels, buffer_size);
+
+ // Remove first half of samples from output vector to remove initial
+ // filter ringing effects and initial gain ramps.
+ interleaved_output.erase(
+ interleaved_output.begin(),
+ interleaved_output.begin() + interleaved_output.size() / 2);
+
+ const float kMaxExpectedMagnitudeDiff = 0.07f;
+ const float maximum_adjacent_frames_magnitude_diff =
+ GetMaximumSampleDiff(interleaved_output, kNumStereoChannels);
+ EXPECT_LT(maximum_adjacent_frames_magnitude_diff,
+ kMaxExpectedMagnitudeDiff);
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(
+ SurroundFormatInstances, BinauralSurroundRendererTest,
+ ::testing::Values(
+ BinauralSurroundRenderer::SurroundFormat::kSurroundMono,
+ BinauralSurroundRenderer::SurroundFormat::kSurroundStereo,
+ BinauralSurroundRenderer::SurroundFormat::kSurroundFiveDotOne,
+ BinauralSurroundRenderer::SurroundFormat::kSurroundSevenDotOne,
+ BinauralSurroundRenderer::SurroundFormat::kFirstOrderAmbisonics,
+ BinauralSurroundRenderer::SurroundFormat::
+ kFirstOrderAmbisonicsWithNonDiegeticStereo,
+ BinauralSurroundRenderer::SurroundFormat::kSecondOrderAmbisonics,
+ BinauralSurroundRenderer::SurroundFormat::
+ kSecondOrderAmbisonicsWithNonDiegeticStereo,
+ BinauralSurroundRenderer::SurroundFormat::kThirdOrderAmbisonics,
+ BinauralSurroundRenderer::SurroundFormat::
+ kThirdOrderAmbisonicsWithNonDiegeticStereo));
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/buffered_source_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/buffered_source_node.cc
new file mode 100644
index 000000000..ab3e49d73
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/buffered_source_node.cc
@@ -0,0 +1,46 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/buffered_source_node.h"
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+BufferedSourceNode::BufferedSourceNode(SourceId source_id, size_t num_channels,
+ size_t frames_per_buffer)
+ : source_id_(source_id),
+ input_audio_buffer_(num_channels, frames_per_buffer),
+ new_buffer_flag_(false) {
+ input_audio_buffer_.Clear();
+}
+
+AudioBuffer* BufferedSourceNode::GetMutableAudioBufferAndSetNewBufferFlag() {
+ new_buffer_flag_ = true;
+ return &input_audio_buffer_;
+}
+
+const AudioBuffer* BufferedSourceNode::AudioProcess() {
+ if (!new_buffer_flag_) {
+ return nullptr;
+ }
+ new_buffer_flag_ = false;
+ input_audio_buffer_.set_source_id(source_id_);
+ return &input_audio_buffer_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/buffered_source_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/buffered_source_node.h
new file mode 100644
index 000000000..3d5ce0b4a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/buffered_source_node.h
@@ -0,0 +1,62 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_BUFFERED_SOURCE_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_BUFFERED_SOURCE_NODE_H_
+
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "node/source_node.h"
+
+namespace vraudio {
+
+// Node that sets the |AudioBuffer| of a source. This class is *not*
+// thread-safe and calls to this class must be synchronized with the graph
+// processing.
+class BufferedSourceNode : public SourceNode {
+ public:
+ // Constructor.
+ //
+ // @param source_id Source id.
+ // @param num_channel Number of channels in output buffers.
+ BufferedSourceNode(SourceId source_id, size_t num_channels,
+ size_t frames_per_buffer);
+
+ // Returns a mutable pointer to the internal |AudioBuffer| and sets a flag to
+ // process the buffer in the next graph processing iteration. Calls to this
+ // method must be synchronized with the audio graph processing.
+ //
+ // @return Mutable audio buffer pointer.
+ AudioBuffer* GetMutableAudioBufferAndSetNewBufferFlag();
+
+ protected:
+ // Implements SourceNode.
+ const AudioBuffer* AudioProcess() override;
+
+ // Source id.
+ const SourceId source_id_;
+
+ // Input audio buffer.
+ AudioBuffer input_audio_buffer_;
+
+ // Flag indicating if an new audio buffer has been set via
+ // |GetMutableAudioBufferAndSetNewBufferFlag|.
+ bool new_buffer_flag_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_BUFFERED_SOURCE_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/foa_rotator_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/foa_rotator_node.cc
new file mode 100644
index 000000000..71c81d017
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/foa_rotator_node.cc
@@ -0,0 +1,67 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/foa_rotator_node.h"
+
+#include "base/logging.h"
+
+
+namespace vraudio {
+
+FoaRotatorNode::FoaRotatorNode(SourceId source_id,
+ const SystemSettings& system_settings)
+ : system_settings_(system_settings),
+ output_buffer_(kNumFirstOrderAmbisonicChannels,
+ system_settings.GetFramesPerBuffer()) {
+ output_buffer_.Clear();
+ output_buffer_.set_source_id(source_id);
+}
+
+const AudioBuffer* FoaRotatorNode::AudioProcess(const NodeInput& input) {
+
+
+ // Get the soundfield input buffer.
+ const AudioBuffer* input_buffer = input.GetSingleInput();
+ DCHECK(input_buffer);
+ DCHECK_GT(input_buffer->num_frames(), 0U);
+ DCHECK_EQ(input_buffer->num_channels(), 4U);
+ DCHECK_EQ(input_buffer->source_id(), output_buffer_.source_id());
+
+ // Rotate soundfield buffer by the inverse head orientation.
+ const auto source_parameters =
+ system_settings_.GetSourceParameters(input_buffer->source_id());
+ if (source_parameters == nullptr) {
+ LOG(WARNING) << "Could not find source parameters";
+ return nullptr;
+ }
+
+ const WorldRotation& source_rotation =
+ source_parameters->object_transform.rotation;
+ const WorldRotation inverse_head_rotation =
+ system_settings_.GetHeadRotation().conjugate();
+ const WorldRotation rotation = inverse_head_rotation * source_rotation;
+ const bool rotation_applied =
+ foa_rotator_.Process(rotation, *input_buffer, &output_buffer_);
+
+ if (!rotation_applied) {
+ return input_buffer;
+ }
+
+ // Copy buffer parameters.
+ return &output_buffer_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/foa_rotator_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/foa_rotator_node.h
new file mode 100644
index 000000000..802478a0a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/foa_rotator_node.h
@@ -0,0 +1,54 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_FOA_ROTATOR_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_FOA_ROTATOR_NODE_H_
+
+#include <memory>
+
+#include "ambisonics/foa_rotator.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+// Node that accepts a single first order PeriphonicSoundfieldBuffer as input
+// and outputs a rotated PeriphonicSoundfieldBuffer of the corresponding
+// soundfield input using head rotation information from the system settings.
+class FoaRotatorNode : public ProcessingNode {
+ public:
+ FoaRotatorNode(SourceId source_id, const SystemSettings& system_settings);
+
+ protected:
+ // Implements ProcessingNode. Returns a null pointer if we are in stereo
+ // loudspeaker or stereo pan mode.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ const SystemSettings& system_settings_;
+
+ // Soundfield rotator used to rotate first order soundfields.
+ FoaRotator foa_rotator_;
+
+ // Output buffer.
+ AudioBuffer output_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_FOA_ROTATOR_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/gain_mixer_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/gain_mixer_node.cc
new file mode 100644
index 000000000..26ab2982f
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/gain_mixer_node.cc
@@ -0,0 +1,66 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/gain_mixer_node.h"
+
+#include <vector>
+
+#include "base/constants_and_types.h"
+
+
+namespace vraudio {
+
+GainMixerNode::GainMixerNode(const AttenuationType& attenuation_type,
+ const SystemSettings& system_settings,
+ size_t num_channels)
+ : mute_enabled_(false),
+ attenuation_type_(attenuation_type),
+ gain_mixer_(num_channels, system_settings.GetFramesPerBuffer()),
+ system_settings_(system_settings) {}
+
+void GainMixerNode::SetMute(bool mute_enabled) { mute_enabled_ = mute_enabled; }
+
+bool GainMixerNode::CleanUp() {
+ CallCleanUpOnInputNodes();
+ // Prevent node from being disconnected when all sources are removed.
+ return false;
+}
+
+const AudioBuffer* GainMixerNode::AudioProcess(const NodeInput& input) {
+
+
+ if (mute_enabled_) {
+ // Skip processing and output nullptr audio buffer.
+ return nullptr;
+ }
+
+ // Apply the gain to each input buffer channel.
+ gain_mixer_.Reset();
+ for (auto input_buffer : input.GetInputBuffers()) {
+ const auto source_parameters =
+ system_settings_.GetSourceParameters(input_buffer->source_id());
+ if (source_parameters != nullptr) {
+ const float target_gain =
+ source_parameters->attenuations[attenuation_type_];
+ const size_t num_channels = input_buffer->num_channels();
+ gain_mixer_.AddInput(*input_buffer,
+ std::vector<float>(num_channels, target_gain));
+ }
+ }
+ return gain_mixer_.GetOutput();
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/gain_mixer_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/gain_mixer_node.h
new file mode 100644
index 000000000..df569ccdd
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/gain_mixer_node.h
@@ -0,0 +1,68 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_GAIN_MIXER_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_GAIN_MIXER_NODE_H_
+
+#include "base/audio_buffer.h"
+#include "base/source_parameters.h"
+#include "dsp/gain_mixer.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+// Node that accepts multiple input buffers, calculates and applies a gain
+// value to each buffer based upon the given |AttenuationType| and then mixes
+// the results together.
+class GainMixerNode : public ProcessingNode {
+ public:
+ // Constructs |GainMixerNode| with given gain calculation method.
+ //
+ // @param attenuation_type Gain attenuation type to be used.
+ // @param system_settings Global system settings.
+ // @param num_channels Number of channels.
+ GainMixerNode(const AttenuationType& attenuation_type,
+ const SystemSettings& system_settings, size_t num_channels);
+
+ // Mute the mixer node by skipping the audio processing and outputting nullptr
+ // buffers.
+ void SetMute(bool mute_enabled);
+
+ // Node implementation.
+ bool CleanUp() final;
+
+ protected:
+ // Implements ProcessingNode.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ // Flag indicating the mute status.
+ bool mute_enabled_;
+
+ // Gain attenuation type.
+ const AttenuationType attenuation_type_;
+
+ // Gain mixer.
+ GainMixer gain_mixer_;
+
+ // Global system settings.
+ const SystemSettings& system_settings_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_GAIN_MIXER_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/gain_mixer_node_test.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/gain_mixer_node_test.cc
new file mode 100644
index 000000000..6e2a98f81
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/gain_mixer_node_test.cc
@@ -0,0 +1,246 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/gain_mixer_node.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "graph/buffered_source_node.h"
+#include "node/sink_node.h"
+#include "node/source_node.h"
+
+namespace vraudio {
+
+namespace {
+
+// Values to initialize a |SystemSettings| instance.
+const size_t kNumFrames = 4;
+const size_t kSampleRate = 44100;
+
+// Helper class to detect deletion.
+class DeletionDetector {
+ public:
+ explicit DeletionDetector(bool* node_deletion_flag)
+ : node_deletion_flag_(node_deletion_flag) {}
+ ~DeletionDetector() {
+ if (node_deletion_flag_ != nullptr) {
+ *node_deletion_flag_ = true;
+ }
+ }
+
+ private:
+ bool* node_deletion_flag_;
+};
+
+// Wraps |SourceNode| to detect its deletion.
+class MySourceNode : public SourceNode, DeletionDetector {
+ public:
+ explicit MySourceNode(bool* node_deletion_flag)
+ : DeletionDetector(node_deletion_flag) {}
+
+ protected:
+ const AudioBuffer* AudioProcess() final { return nullptr; }
+};
+
+// Wraps |GainMixerNode| to detect its deletion.
+class MyGainMixerNode : public GainMixerNode, DeletionDetector {
+ public:
+ MyGainMixerNode(bool* node_deletion_flag,
+ const AttenuationType& attenuation_type,
+ const SystemSettings& system_settings)
+ : GainMixerNode(attenuation_type, system_settings, kNumMonoChannels),
+ DeletionDetector(node_deletion_flag) {}
+};
+
+// Wraps |SinkNode| to detect its deletion.
+class MySinkNode : public SinkNode, DeletionDetector {
+ public:
+ explicit MySinkNode(bool* node_deletion_flag)
+ : DeletionDetector(node_deletion_flag) {}
+};
+
+// Tests that the |GainMixerNode| keeps connected at the moment all of its
+// sources are removed.
+TEST(AudioNodesTest, CleanUpOnEmptyInputTest) {
+ bool source_node_deleted = false;
+ bool gain_mixer_node_deleted = false;
+ bool sink_node_deleted = false;
+
+ SystemSettings system_settings(kNumMonoChannels, kNumFrames, kSampleRate);
+ auto sink_node = std::make_shared<MySinkNode>(&sink_node_deleted);
+
+ {
+ // Create a source and mixer node and connect it to sink node.
+ auto source_node = std::make_shared<MySourceNode>(&source_node_deleted);
+ auto gain_mixer_node = std::make_shared<MyGainMixerNode>(
+ &gain_mixer_node_deleted, AttenuationType::kInput, system_settings);
+
+ // Connect nodes.
+ sink_node->Connect(gain_mixer_node);
+ gain_mixer_node->Connect(source_node);
+
+ // End of stream is marked in source node. Do not expect any data anymore.
+ source_node->MarkEndOfStream();
+ }
+
+ EXPECT_FALSE(source_node_deleted);
+ EXPECT_FALSE(gain_mixer_node_deleted);
+ EXPECT_FALSE(sink_node_deleted);
+
+ sink_node->CleanUp();
+
+ EXPECT_TRUE(source_node_deleted);
+ EXPECT_FALSE(gain_mixer_node_deleted);
+ EXPECT_FALSE(sink_node_deleted);
+}
+
+// Provides unit tests for |GainMixerNode|.
+class GainMixerNodeTest : public ::testing::Test {
+ protected:
+ GainMixerNodeTest()
+ : system_settings_(kNumMonoChannels, kNumFrames, kSampleRate) {}
+
+ // Helper method to create a new input buffer.
+ //
+ // @return Mono audio buffer filled with test data.
+ std::unique_ptr<AudioBuffer> CreateInputBuffer(
+ const std::vector<float>& input_data) {
+ auto buffer = std::unique_ptr<AudioBuffer>(
+ new AudioBuffer(kNumMonoChannels, input_data.size()));
+ (*buffer)[0] = input_data;
+ return buffer;
+ }
+
+ // Helper method that generates a node graph and returns the processed
+ // output.
+ //
+ // @param num_inputs Number of input buffers to be processed.
+ void CreateGraph(size_t num_inputs) {
+ // Tests will use |AttenuationType::kInput| which directly returns the
+ // local
+ // gain value in order to avoid extra complexity.
+ gain_mixer_node_ = std::make_shared<GainMixerNode>(
+ AttenuationType::kInput, system_settings_, kNumMonoChannels);
+
+ output_node_ = std::make_shared<SinkNode>();
+ output_node_->Connect(gain_mixer_node_);
+
+ buffered_source_nodes_.resize(num_inputs);
+ auto parameters_manager = system_settings_.GetSourceParametersManager();
+ for (size_t i = 0; i < num_inputs; ++i) {
+ const auto source_id = static_cast<SourceId>(i);
+ buffered_source_nodes_[i] = std::make_shared<BufferedSourceNode>(
+ source_id, kNumMonoChannels, kNumFrames);
+ gain_mixer_node_->Connect(buffered_source_nodes_[i]);
+ parameters_manager->Register(source_id);
+ }
+ }
+
+ // Processes input buuffers with gains returning the mixed output.
+ //
+ // @param input_gains Gains to be processed.
+ // @param input_buffers Buffers to be processed.
+ // @return Processed output buffer.
+ const AudioBuffer* Process(
+ float input_gain, const std::vector<std::vector<float>>& input_buffers) {
+ DCHECK_EQ(buffered_source_nodes_.size(), input_buffers.size());
+ for (size_t i = 0; i < input_buffers.size(); ++i) {
+ const auto source_id = static_cast<SourceId>(i);
+ auto input = CreateInputBuffer(input_buffers[i]);
+ // Set the input gain.
+ auto parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ source_id);
+ parameters->attenuations[AttenuationType::kInput] = input_gain;
+ // Process the buffer.
+ AudioBuffer* const input_node_buffer =
+ buffered_source_nodes_[i]->GetMutableAudioBufferAndSetNewBufferFlag();
+ *input_node_buffer = *input;
+ }
+ const std::vector<const AudioBuffer*>& outputs = output_node_->ReadInputs();
+ if (!outputs.empty()) {
+ DCHECK_EQ(outputs.size(), 1U);
+ return outputs.front();
+ }
+ return nullptr;
+ }
+
+ // System settings.
+ SystemSettings system_settings_;
+
+ // Component nodes for the simple audio graph.
+ std::shared_ptr<GainMixerNode> gain_mixer_node_;
+ std::vector<std::shared_ptr<BufferedSourceNode>> buffered_source_nodes_;
+ std::shared_ptr<SinkNode> output_node_;
+};
+
+// Tests that the |GainMixerNode| returns the expected output buffers with
+// different gain values.
+TEST_F(GainMixerNodeTest, GainTest) {
+ const float kGain = 0.5f;
+ const std::vector<std::vector<float>> inputs({{1.0f, 1.0f, 1.0f, 1.0f},
+ {2.0f, 2.0f, 2.0f, 2.0f},
+ {3.0f, 3.0f, 3.0f, 3.0f},
+ {4.0f, 4.0f, 4.0f, 4.0f}});
+ // Zero buffer should be returned when the gain value's zero from the start.
+ CreateGraph(inputs.size());
+ auto output = Process(0.0f, inputs);
+ for (size_t i = 0; i < inputs[0].size(); ++i) {
+ EXPECT_NEAR((*output)[0][i], 0.0f, kEpsilonFloat);
+ }
+
+ // A valid output buffer should be returned when the gain value is non-zero.
+ output = Process(kGain, inputs);
+ EXPECT_NEAR((*output)[0][0], 0.0f, kEpsilonFloat);
+ for (size_t i = 1; i < inputs[0].size(); ++i) {
+ EXPECT_FALSE(std::abs((*output)[0][i]) <=
+ std::numeric_limits<float>::epsilon());
+ }
+
+ // Correct values should be returned after gain processor interpolation.
+ for (size_t i = 0; i < kUnitRampLength / 2; ++i) {
+ output = Process(kGain, inputs);
+ }
+ const float output_value =
+ kGain * (inputs[0][0] + inputs[1][0] + inputs[2][0] + inputs[3][0]);
+ for (size_t i = 0; i < inputs[0].size(); ++i) {
+ EXPECT_NEAR((*output)[0][i], output_value, kEpsilonFloat);
+ }
+
+ // A valid output buffer should be returned even when the gain value is zero
+ // while gain processor interpolation.
+ output = Process(0.0f, inputs);
+ for (size_t i = 0; i < inputs[0].size(); ++i) {
+ EXPECT_NE((*output)[0][i], 0.0f);
+ }
+
+ // Zero buffer should be returned after the interpolation is completed.
+ for (size_t i = 0; i < kUnitRampLength / 2; ++i) {
+ output = Process(0.0f, inputs);
+ }
+ for (size_t i = 0; i < inputs[0].size(); ++i) {
+ EXPECT_NEAR((*output)[0][i], 0.0f, kEpsilonFloat);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/gain_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/gain_node.cc
new file mode 100644
index 000000000..ca1c8edb2
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/gain_node.cc
@@ -0,0 +1,82 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/gain_node.h"
+
+#include <cmath>
+
+
+#include "dsp/gain.h"
+
+namespace vraudio {
+
+GainNode::GainNode(SourceId source_id, size_t num_channels,
+ const AttenuationType& attenuation_type,
+ const SystemSettings& system_settings)
+ : num_channels_(num_channels),
+ attenuation_type_(attenuation_type),
+ gain_processors_(num_channels_),
+ system_settings_(system_settings),
+ output_buffer_(num_channels, system_settings.GetFramesPerBuffer()) {
+ DCHECK_GT(num_channels, 0U);
+ output_buffer_.set_source_id(source_id);
+}
+
+const AudioBuffer* GainNode::AudioProcess(const NodeInput& input) {
+
+
+ const AudioBuffer* input_buffer = input.GetSingleInput();
+ DCHECK(input_buffer);
+ DCHECK_EQ(input_buffer->num_channels(), num_channels_);
+ DCHECK_EQ(input_buffer->source_id(), output_buffer_.source_id());
+
+ const auto source_parameters =
+ system_settings_.GetSourceParameters(input_buffer->source_id());
+ if (source_parameters == nullptr) {
+ LOG(WARNING) << "Could not find source parameters";
+ return nullptr;
+ }
+
+ const float current_gain = gain_processors_[0].GetGain();
+ const float target_gain = source_parameters->attenuations[attenuation_type_];
+ if (IsGainNearZero(target_gain) && IsGainNearZero(current_gain)) {
+ // Make sure the gain processors are initialized.
+ for (size_t i = 0; i < num_channels_; ++i) {
+ gain_processors_[i].Reset(0.0f);
+ }
+ // Skip processing in case of zero gain.
+ return nullptr;
+ }
+ if (IsGainNearUnity(target_gain) && IsGainNearUnity(current_gain)) {
+ // Make sure the gain processors are initialized.
+ for (size_t i = 0; i < num_channels_; ++i) {
+ gain_processors_[i].Reset(1.0f);
+ }
+ // Skip processing in case of unity gain.
+ return input_buffer;
+ }
+
+ // Apply the gain to each input buffer channel.
+ for (size_t i = 0; i < num_channels_; ++i) {
+ gain_processors_[i].ApplyGain(target_gain, (*input_buffer)[i],
+ &output_buffer_[i],
+ false /* accumulate_output */);
+ }
+
+ return &output_buffer_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/gain_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/gain_node.h
new file mode 100644
index 000000000..35f8c62b8
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/gain_node.h
@@ -0,0 +1,69 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_GAIN_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_GAIN_NODE_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/source_parameters.h"
+#include "dsp/gain_processor.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+// Node that calculates and applies a gain value to each channel of an input
+// buffer based upon the given |GainCalculator|.
+class GainNode : public ProcessingNode {
+ public:
+ // Constructs |GainNode| with given gain attenuation method.
+ //
+ // @param source_id Output buffer source id.
+ // @param num_channels Number of channels in the input buffer.
+ // @param attenuation_type Gain attenuation type to be used.
+ // @param system_settings Global system settings.
+ GainNode(SourceId source_id, size_t num_channels,
+ const AttenuationType& attenuation_type,
+ const SystemSettings& system_settings);
+
+ protected:
+ // Implements ProcessingNode.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ // Number of channels of the audio buffer.
+ const size_t num_channels_;
+
+ // Gain attenuation type.
+ const AttenuationType attenuation_type_;
+
+ // Gain processors per each channel.
+ std::vector<GainProcessor> gain_processors_;
+
+ // Global system settings.
+ const SystemSettings& system_settings_;
+
+ // Output buffer.
+ AudioBuffer output_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_GAIN_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/gain_node_test.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/gain_node_test.cc
new file mode 100644
index 000000000..97391f876
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/gain_node_test.cc
@@ -0,0 +1,138 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/gain_node.h"
+
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "graph/buffered_source_node.h"
+#include "node/sink_node.h"
+
+namespace vraudio {
+
+namespace {
+
+// Values to initialize a |SystemSettings| instance.
+const size_t kNumFrames = 4;
+const size_t kSampleRate = 44100;
+
+// Source id.
+const SourceId kSourceId = 1;
+
+const float kInputData[kNumFrames] = {1.0f, 2.0f, 3.0f, 4.0f};
+
+// Provides unit tests for |GainNode|.
+class GainNodeTest : public ::testing::Test {
+ protected:
+ GainNodeTest()
+ : input_data_(std::begin(kInputData), std::end(kInputData)),
+ system_settings_(kNumMonoChannels, kNumFrames, kSampleRate) {}
+
+ void SetUp() override {
+ // Tests will use |AttenuationType::kInput| which directly returns the input
+ // gain value in order to avoid extra complexity.
+ gain_node_ = std::make_shared<GainNode>(
+ kSourceId, kNumMonoChannels, AttenuationType::kInput, system_settings_);
+ input_buffer_node_ = std::make_shared<BufferedSourceNode>(
+ kSourceId, kNumMonoChannels, kNumFrames);
+ gain_node_->Connect(input_buffer_node_);
+ output_node_ = std::make_shared<SinkNode>();
+ output_node_->Connect(gain_node_);
+ // Register the source parameters.
+ system_settings_.GetSourceParametersManager()->Register(kSourceId);
+ }
+
+ // Helper method to create a new input buffer.
+ //
+ // @return Mono audio buffer filled with test data.
+ std::unique_ptr<AudioBuffer> CreateInputBuffer() {
+ std::unique_ptr<AudioBuffer> buffer(
+ new AudioBuffer(kNumMonoChannels, kNumFrames));
+ (*buffer)[0] = input_data_;
+ return buffer;
+ }
+
+ // Helper method that generates a node graph and returns the processed output.
+ //
+ // @param input_gain Input gain value to be processed.
+ // @return Processed output buffer.
+
+ const AudioBuffer* ProcessGainNode(float input_gain) {
+ // Create a new audio buffer.
+ auto input = CreateInputBuffer();
+ // Update the input gain parameter.
+ auto parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ kSourceId);
+ parameters->attenuations[AttenuationType::kInput] = input_gain;
+ // Process the buffer.
+ AudioBuffer* const input_node_buffer =
+ input_buffer_node_->GetMutableAudioBufferAndSetNewBufferFlag();
+ *input_node_buffer = *input;
+
+ const std::vector<const AudioBuffer*>& outputs = output_node_->ReadInputs();
+ if (!outputs.empty()) {
+ DCHECK_EQ(outputs.size(), 1U);
+ return outputs.front();
+ }
+ return nullptr;
+ }
+
+ private:
+ std::vector<float> input_data_;
+
+ std::shared_ptr<GainNode> gain_node_;
+ std::shared_ptr<BufferedSourceNode> input_buffer_node_;
+ std::shared_ptr<SinkNode> output_node_;
+
+ SystemSettings system_settings_;
+};
+
+// Tests that the gain node returns the expected output buffers with different
+// gain values.
+TEST_F(GainNodeTest, GainTest) {
+ // nullptr should be returned when the gain value is zero from the start.
+ auto output = ProcessGainNode(0.0f);
+
+ EXPECT_TRUE(output == nullptr);
+
+ // A valid output buffer should be returned when the gain value is non-zero.
+ output = ProcessGainNode(0.5f);
+
+ EXPECT_FALSE(output == nullptr);
+
+ // A valid output buffer should be returned even when the gain value is zero
+ // while gain processor interpolation.
+ output = ProcessGainNode(0.0f);
+
+ EXPECT_FALSE(output == nullptr);
+
+ // nullptr should be returned after the interpolation is completed.
+ for (size_t i = 0; i < kUnitRampLength; ++i) {
+ output = ProcessGainNode(0.0f);
+ }
+
+ EXPECT_TRUE(output == nullptr);
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.cc
new file mode 100644
index 000000000..cb5b85590
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.cc
@@ -0,0 +1,290 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/graph_manager.h"
+
+#include <functional>
+
+#include "ambisonics/utils.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "graph/foa_rotator_node.h"
+#include "graph/gain_node.h"
+#include "graph/hoa_rotator_node.h"
+#include "graph/mono_from_soundfield_node.h"
+#include "graph/near_field_effect_node.h"
+#include "graph/occlusion_node.h"
+
+namespace vraudio {
+
+GraphManager::GraphManager(const SystemSettings& system_settings)
+ :
+ room_effects_enabled_(true),
+ config_(GlobalConfig()),
+ system_settings_(system_settings),
+ fft_manager_(system_settings.GetFramesPerBuffer()),
+ output_node_(std::make_shared<SinkNode>()) {
+ CHECK_LE(system_settings.GetFramesPerBuffer(), kMaxSupportedNumFrames);
+
+ stereo_mixer_node_ =
+ std::make_shared<MixerNode>(system_settings_, kNumStereoChannels);
+ output_node_->Connect(stereo_mixer_node_);
+
+ /// Initialize the Ambisonic Lookup Table.
+ lookup_table_.reset(new AmbisonicLookupTable(config_.max_ambisonic_order));
+ // Initialize the Ambisonic Renderer subgraphs.
+ for (const auto& sh_hrir_filename_itr : config_.sh_hrir_filenames) {
+ const int ambisonic_order = sh_hrir_filename_itr.first;
+ const auto& sh_hrir_filename = sh_hrir_filename_itr.second;
+ InitializeAmbisonicRendererGraph(ambisonic_order, sh_hrir_filename);
+ // Initialize the Ambisonic Mixing Encoders for HRTF sound object rendering.
+ ambisonic_mixing_encoder_nodes_[ambisonic_order] =
+ std::make_shared<AmbisonicMixingEncoderNode>(
+ system_settings_, *lookup_table_, ambisonic_order);
+ ambisonic_mixer_nodes_[ambisonic_order]->Connect(
+ ambisonic_mixing_encoder_nodes_[ambisonic_order]);
+ }
+
+ // Stereo mixing panner node used in non-HRTF sound object rendering.
+ stereo_mixing_panner_node_ =
+ std::make_shared<StereoMixingPannerNode>(system_settings_);
+ stereo_mixer_node_->Connect(stereo_mixing_panner_node_);
+
+ // Initialize room effects graphs.
+ InitializeReflectionsGraph();
+ InitializeReverbGraph();
+ // Initialize ambisonic output mixer.
+ ambisonic_output_mixer_.reset(
+ new Mixer(GetNumPeriphonicComponents(config_.max_ambisonic_order),
+ system_settings.GetFramesPerBuffer()));
+}
+
+void GraphManager::CreateAmbisonicSource(SourceId ambisonic_source_id,
+ size_t num_channels) {
+ DCHECK(source_nodes_.find(ambisonic_source_id) == source_nodes_.end());
+ // Create a new |ambisonic_source_node| and register to |source_nodes_|.
+ auto ambisonic_source_node = std::make_shared<BufferedSourceNode>(
+ ambisonic_source_id, num_channels, system_settings_.GetFramesPerBuffer());
+ source_nodes_[ambisonic_source_id] = ambisonic_source_node;
+
+ // Connect |ambisonic_source_node| to the ambisonic decoding pipeline.
+ const int ambisonic_order = GetPeriphonicAmbisonicOrder(num_channels);
+ auto direct_attenuation_node =
+ std::make_shared<GainNode>(ambisonic_source_id, num_channels,
+ AttenuationType::kDirect, system_settings_);
+ direct_attenuation_node->Connect(ambisonic_source_node);
+ if (ambisonic_order == 1) {
+ // First order case.
+ auto foa_rotator_node =
+ std::make_shared<FoaRotatorNode>(ambisonic_source_id, system_settings_);
+ foa_rotator_node->Connect(direct_attenuation_node);
+ ambisonic_mixer_nodes_[ambisonic_order]->Connect(foa_rotator_node);
+ } else {
+ // Higher orders case.
+ auto hoa_rotator_node = std::make_shared<HoaRotatorNode>(
+ ambisonic_source_id, system_settings_, ambisonic_order);
+ hoa_rotator_node->Connect(direct_attenuation_node);
+ ambisonic_mixer_nodes_[ambisonic_order]->Connect(hoa_rotator_node);
+ }
+ // Connect to room effects rendering pipeline.
+ auto mono_from_soundfield_node = std::make_shared<MonoFromSoundfieldNode>(
+ ambisonic_source_id, system_settings_);
+ mono_from_soundfield_node->Connect(ambisonic_source_node);
+ reflections_gain_mixer_node_->Connect(mono_from_soundfield_node);
+ reverb_gain_mixer_node_->Connect(mono_from_soundfield_node);
+}
+
+void GraphManager::CreateSoundObjectSource(SourceId sound_object_source_id,
+ int ambisonic_order,
+ bool enable_hrtf,
+ bool enable_direct_rendering) {
+ DCHECK(source_nodes_.find(sound_object_source_id) == source_nodes_.end());
+ // Create a new |sound_object_source_node| and register to |source_nodes_|.
+ auto sound_object_source_node = std::make_shared<BufferedSourceNode>(
+ sound_object_source_id, kNumMonoChannels,
+ system_settings_.GetFramesPerBuffer());
+ source_nodes_[sound_object_source_id] = sound_object_source_node;
+
+ // Create direct rendering pipeline.
+ if (enable_direct_rendering) {
+ auto direct_attenuation_node =
+ std::make_shared<GainNode>(sound_object_source_id, kNumMonoChannels,
+ AttenuationType::kDirect, system_settings_);
+ direct_attenuation_node->Connect(sound_object_source_node);
+ auto occlusion_node = std::make_shared<OcclusionNode>(
+ sound_object_source_id, system_settings_);
+ occlusion_node->Connect(direct_attenuation_node);
+ auto near_field_effect_node = std::make_shared<NearFieldEffectNode>(
+ sound_object_source_id, system_settings_);
+
+ if (enable_hrtf) {
+ ambisonic_mixing_encoder_nodes_[ambisonic_order]->Connect(occlusion_node);
+ } else {
+ stereo_mixing_panner_node_->Connect(occlusion_node);
+ }
+
+ near_field_effect_node->Connect(occlusion_node);
+ stereo_mixer_node_->Connect(near_field_effect_node);
+ }
+
+ // Connect to room effects rendering pipeline.
+ reflections_gain_mixer_node_->Connect(sound_object_source_node);
+ reverb_gain_mixer_node_->Connect(sound_object_source_node);
+}
+
+void GraphManager::EnableRoomEffects(bool enable) {
+ room_effects_enabled_ = enable;
+ reflections_gain_mixer_node_->SetMute(!room_effects_enabled_);
+ reverb_gain_mixer_node_->SetMute(!room_effects_enabled_);
+}
+
+const AudioBuffer* GraphManager::GetAmbisonicBuffer() const {
+ ambisonic_output_mixer_->Reset();
+ for (const auto& ambisonic_mixer_node_itr : ambisonic_mixer_nodes_) {
+ const auto* ambisonic_buffer =
+ ambisonic_mixer_node_itr.second->GetOutputBuffer();
+ if (ambisonic_buffer != nullptr) {
+ ambisonic_output_mixer_->AddInput(*ambisonic_buffer);
+ }
+ }
+ return ambisonic_output_mixer_->GetOutput();
+}
+
+const AudioBuffer* GraphManager::GetStereoBuffer() const {
+ return stereo_mixer_node_->GetOutputBuffer();
+}
+
+const AudioBuffer *GraphManager::GetReverbBuffer() const
+{
+ return reverb_node_->GetOutputBuffer();
+}
+
+size_t GraphManager::GetNumMaxAmbisonicChannels() const {
+ return GetNumPeriphonicComponents(config_.max_ambisonic_order);
+}
+
+bool GraphManager::GetRoomEffectsEnabled() const {
+ return room_effects_enabled_;
+}
+
+void GraphManager::UpdateRoomReflections() { reflections_node_->Update(); }
+
+void GraphManager::UpdateRoomReverb() { reverb_node_->Update(); }
+
+void GraphManager::InitializeReverbGraph() {
+ reverb_gain_mixer_node_ = std::make_shared<GainMixerNode>(
+ AttenuationType::kReverb, system_settings_, kNumMonoChannels);
+ reverb_node_ = std::make_shared<ReverbNode>(system_settings_, &fft_manager_);
+ reverb_node_->Connect(reverb_gain_mixer_node_);
+ stereo_mixer_node_->Connect(reverb_node_);
+}
+
+void GraphManager::InitializeReflectionsGraph() {
+ reflections_gain_mixer_node_ = std::make_shared<GainMixerNode>(
+ AttenuationType::kReflections, system_settings_, kNumMonoChannels);
+ reflections_node_ = std::make_shared<ReflectionsNode>(system_settings_);
+ reflections_node_->Connect(reflections_gain_mixer_node_);
+ // Reflections are limited to First Order Ambisonics to reduce complexity.
+ const int kAmbisonicOrder1 = 1;
+ ambisonic_mixer_nodes_[kAmbisonicOrder1]->Connect(reflections_node_);
+}
+
+void GraphManager::CreateAmbisonicPannerSource(SourceId sound_object_source_id,
+ bool enable_hrtf) {
+ DCHECK(source_nodes_.find(sound_object_source_id) == source_nodes_.end());
+ // Create a new |sound_object_source_node| and register to |source_nodes_|.
+ auto sound_object_source_node = std::make_shared<BufferedSourceNode>(
+ sound_object_source_id, kNumMonoChannels,
+ system_settings_.GetFramesPerBuffer());
+ source_nodes_[sound_object_source_id] = sound_object_source_node;
+
+ if (enable_hrtf) {
+ ambisonic_mixing_encoder_nodes_[config_.max_ambisonic_order]->Connect(
+ sound_object_source_node);
+ } else {
+ stereo_mixing_panner_node_->Connect(sound_object_source_node);
+ }
+}
+
+void GraphManager::CreateStereoSource(SourceId stereo_source_id) {
+ DCHECK(source_nodes_.find(stereo_source_id) == source_nodes_.end());
+ // Create a new |stereo_source_node| and register to |source_nodes_|.
+ auto stereo_source_node = std::make_shared<BufferedSourceNode>(
+ stereo_source_id, kNumStereoChannels,
+ system_settings_.GetFramesPerBuffer());
+ source_nodes_[stereo_source_id] = stereo_source_node;
+
+ // Connect |stereo_source_node| to the stereo rendering pipeline.
+ auto gain_node =
+ std::make_shared<GainNode>(stereo_source_id, kNumStereoChannels,
+ AttenuationType::kInput, system_settings_);
+ gain_node->Connect(stereo_source_node);
+ stereo_mixer_node_->Connect(gain_node);
+}
+
+void GraphManager::DestroySource(SourceId source_id) {
+ auto source_node = LookupSourceNode(source_id);
+ if (source_node != nullptr) {
+ // Disconnect the source from the graph.
+ source_node->MarkEndOfStream();
+ output_node_->CleanUp();
+ // Unregister the source from |source_nodes_|.
+ source_nodes_.erase(source_id);
+ }
+}
+
+std::shared_ptr<SinkNode> GraphManager::GetSinkNode() { return output_node_; }
+
+void GraphManager::Process() {
+
+ output_node_->ReadInputs();
+}
+
+AudioBuffer* GraphManager::GetMutableAudioBuffer(SourceId source_id) {
+ auto source_node = LookupSourceNode(source_id);
+ if (source_node == nullptr) {
+ return nullptr;
+ }
+ return source_node->GetMutableAudioBufferAndSetNewBufferFlag();
+}
+
+void GraphManager::InitializeAmbisonicRendererGraph(
+ int ambisonic_order, const std::string& sh_hrir_filename) {
+ CHECK_LE(ambisonic_order, config_.max_ambisonic_order);
+ const size_t num_channels = GetNumPeriphonicComponents(ambisonic_order);
+ // Create binaural decoder pipeline.
+ ambisonic_mixer_nodes_[ambisonic_order] =
+ std::make_shared<MixerNode>(system_settings_, num_channels);
+ auto ambisonic_binaural_decoder_node =
+ std::make_shared<AmbisonicBinauralDecoderNode>(
+ system_settings_, ambisonic_order, sh_hrir_filename, &fft_manager_,
+ &resampler_);
+ ambisonic_binaural_decoder_node->Connect(
+ ambisonic_mixer_nodes_[ambisonic_order]);
+ stereo_mixer_node_->Connect(ambisonic_binaural_decoder_node);
+}
+
+std::shared_ptr<BufferedSourceNode> GraphManager::LookupSourceNode(
+ SourceId source_id) {
+ auto source_node_itr = source_nodes_.find(source_id);
+ if (source_node_itr == source_nodes_.end()) {
+ LOG(WARNING) << "Source node " << source_id << " not found";
+ return nullptr;
+ }
+ return source_node_itr->second;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.h b/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.h
new file mode 100644
index 000000000..818b6fa01
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.h
@@ -0,0 +1,404 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_GRAPH_MANAGER_H_
+#define RESONANCE_AUDIO_GRAPH_GRAPH_MANAGER_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "ambisonics/ambisonic_lookup_table.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "config/global_config.h"
+#include "dsp/fft_manager.h"
+#include "dsp/resampler.h"
+#include "graph/ambisonic_binaural_decoder_node.h"
+#include "graph/ambisonic_mixing_encoder_node.h"
+#include "graph/buffered_source_node.h"
+#include "graph/gain_mixer_node.h"
+#include "graph/mixer_node.h"
+#include "graph/reflections_node.h"
+#include "graph/reverb_node.h"
+#include "graph/stereo_mixing_panner_node.h"
+#include "graph/system_settings.h"
+#include "node/sink_node.h"
+
+namespace vraudio {
+
+// The GraphManager class manages the construction and lifetime of the audio
+// processing graph. It owns the output node that connects the audio processing
+// graph to the audio hardware.
+class GraphManager {
+ public:
+ // Initializes GraphManager class.
+ //
+ // @param system_settings Global system configuration.
+ explicit GraphManager(const SystemSettings& system_settings);
+
+ // Returns the sink node the audio graph is connected to.
+ //
+ // @return Shared pointer of the sink node.
+ std::shared_ptr<SinkNode> GetSinkNode();
+
+ // Triggers processing of the audio graph for all the connected nodes.
+ void Process();
+
+ // Returns a mutable pointer to the |AudioBuffer| of an audio source with
+ // given |source_id|. Calls to this method must be synchronized with the audio
+ // graph processing.
+ //
+ // @param source_id Source id.
+ // @return Mutable audio buffer pointer. Nullptr if source_id not found.
+ AudioBuffer* GetMutableAudioBuffer(SourceId source_id);
+
+ // Creates an ambisonic panner source with given |sound_object_source_id|.
+ //
+ // Processing graph:
+ //
+ // +-------------------+
+ // | |
+ // +------------+ SoundObjectSource +----------+
+ // | | | |
+ // | +---------+---------+ |
+ // | |
+ // +-----------v-----------+ +---------v----------+
+ // | | | |
+ // | AmbisonicMixingPanner | | StereoMixingPanner |
+ // | | | |
+ // +-----------+-----------+ +---------+----------+
+ // | |
+ // +-----------v-----------+ +---------v----------+
+ // | | | |
+ // | AmbisonicMixer | | StereoMixer |
+ // | | | |
+ // +-----------+-----------+ +--------------------+
+ //
+ //
+ // @param sound_object_source_id Id of sound object source.
+ // @param enable_hrtf Flag to enable HRTF-based spatialization.
+ void CreateAmbisonicPannerSource(SourceId sound_object_source_id,
+ bool enable_hrtf);
+
+ // Creates a new stereo non-spatialized source with given |stereo_source_id|.
+ //
+ // Processing graph:
+ //
+ // +--------------+
+ // | |
+ // | StereoSource |
+ // | |
+ // +-------+------+
+ // |
+ // +-------v------+
+ // | |
+ // | Gain |
+ // | |
+ // +-------+------+
+ // |
+ // +-------V------+
+ // | |
+ // | StereoMixer |
+ // | |
+ // +--------------+
+ //
+ // @param stereo_source_id Id of new stereo source.
+ void CreateStereoSource(SourceId stereo_source_id);
+
+ // Destroys source with given |source_id|. Note that this call only sets a
+ // flag to indicate that this source can be removed. The actual disconnect
+ // happens from the audio processing thread the next time the processing graph
+ // is triggered.
+ //
+ // @param source_id Id of source to be destroyed.
+ void DestroySource(SourceId source_id);
+
+ // Creates a new ambisonic source subgraph with given |ambisonic_source_id|.
+ // Note: Ambisonic source subgraph is only created if the rendering mode is
+ // HRTF.
+ //
+ // Processing graph (all the graphs created using http://asciiflow.com/):
+ //
+ // +-----------------+
+ // | |
+ // +-------+ AmbisonicSource +-------+
+ // | | | |
+ // | +-----------------+ |
+ // | |
+ // +----v---+ +----------v---------+
+ // | | | |
+ // | Gain | +--+ MonoFromSoundfield +--+
+ // | | | | | |
+ // +----+---+ | +--------------------+ |
+ // | | |
+ // | | |
+ // +--------v-------+ +--------v---------+ +------v------+
+ // | | | | | |
+ // | Foa/HoaRotator | | ReflectionsMixer | | ReverbMixer |
+ // | | | | | |
+ // +--------+-------+ +--------+---------+ +------+------+
+ // |
+ // +--------v-------+
+ // | |
+ // | AmbisonicMixer |
+ // | |
+ // +--------+-------+
+ //
+ // @param ambisonic_source_id Id of new ambisonic source.
+ // @param num_channels Number of input channels of ambisonic source node.
+ void CreateAmbisonicSource(SourceId ambisonic_source_id, size_t num_channels);
+
+ // Creates a new sound object source with given |sound_object_source_id|.
+ //
+ // Processing graph:
+ //
+ // +-------------------+
+ // | |
+ // +-------------+ SoundObjectSource +----------+
+ // | | | |
+ // | +---------+---------+ |
+ // | | |
+ // +----------v-----------+ +---------v---------+ +--------v--------+
+ // | | | | | |
+ // | ReflectionsGainMixer | | DirectAttenuation | | ReverbGainMixer |
+ // | | | | | |
+ // +----------+-----------+ +---------+---------+ +--------+--------+
+ // |
+ // +---------v---------+
+ // HRTF | | Stereo Panning
+ // +------------+ Occlusion +----------+
+ // | | | |
+ // | +---------+---------+ |
+ // | | |
+ // +-----------v-----------+ +--------v--------+ +---------v----------+
+ // | | | | | |
+ // | AmbisonicMixingPanner | | NearFieldEffect | | StereoMixingPanner |
+ // | | | | | |
+ // +-----------+-----------+ +--------+--------+ +---------+----------+
+ // | | |
+ // +-----------v-----------+ +--------v--------+ |
+ // | | | | |
+ // | AmbisonicMixer | | StereoMixer <-----------+
+ // | | | |
+ // +-----------+-----------+ +-----------------+
+ //
+ //
+ // @param sound_object_source_id Id of sound object source.
+ // @param ambisonic_order Ambisonic order to encode the sound object source.
+ // @param enable_hrtf Flag to enable HRTF-based rendering.
+ // @param enable_direct_rendering Flag to enable direct source rendering.
+ void CreateSoundObjectSource(SourceId sound_object_source_id,
+ int ambisonic_order, bool enable_hrtf,
+ bool enable_direct_rendering);
+
+ // Mutes on/off the room effects mixers.
+ //
+ // @param Whether to enable room effects.
+ void EnableRoomEffects(bool enable);
+
+ // Returns the last processed output audio buffer of the ambisonic mix with
+ // the highest possible ambisonic channel configuration. Note that, this
+ // method will *not* trigger the processing of the audio graph.
+ // |GraphManager::Process| must be called prior to this method call to ensure
+ // that the output buffer is up-to-date.
+ //
+ // @return Output audio buffer of the ambisonic mix, or nullptr if no output.
+ const AudioBuffer* GetAmbisonicBuffer() const;
+
+ // Returns the last processed output audio buffer of the stereo (binaural)
+ // mix. Note that, this method will *not* trigger the processing of the audio
+ // graph. |GraphManager::Process| must be called prior to this method call to
+ // ensure that the output buffer is up-to-date.
+ //
+ // @return Output audio buffer of the stereo mix, or nullptr if no output.
+ const AudioBuffer* GetStereoBuffer() const;
+
+ // Returns the last processed buffer containing the reverb data for the room.
+ // The buffer contains stereo data.
+ // Note that, this method will *not* trigger the processing of the audio
+ // graph. |GraphManager::Process| must be called prior to this method call to
+ // ensure that the buffer is up-to-date.
+ //
+ // @return Room reverb audio buffer, or nullptr if no output.
+ const AudioBuffer* GetReverbBuffer() const;
+
+ // Returns the maximum allowed number of ambisonic channels.
+ //
+ // @return Number of channels based on Ambisonic order in the global config.
+ size_t GetNumMaxAmbisonicChannels() const;
+
+ // Returns whether the room effects graph is enabled.
+ //
+ // @return True if room effects are enabled.
+ bool GetRoomEffectsEnabled() const;
+
+ // Updates the room reflections with the current properties for room effects
+ // processing.
+ void UpdateRoomReflections();
+
+ // Updates the room reverb.
+ void UpdateRoomReverb();
+
+ private:
+ // Initializes the Ambisonic renderer subgraph for the speficied Ambisonic
+ // order and connects it to the |StereoMixerNode|.
+ //
+ // Processing graph:
+ //
+ // +------------------+
+ // | |
+ // | AmbisonicMixer |
+ // | |
+ // +--------+---------+
+ // |
+ // |
+ // +------------v-------------+
+ // | |
+ // | AmbisonicBinauralDecoder |
+ // | |
+ // +------------+-------------+
+ // |
+ // |
+ // +-----------v------------+
+ // | |
+ // | StereoMixer |
+ // | |
+ // +------------------------+
+ //
+ // @param ambisonic_order Ambisonic order.
+ // @param sh_hrir_filename Filename to load the HRIR data from.
+ void InitializeAmbisonicRendererGraph(int ambisonic_order,
+ const std::string& sh_hrir_filename);
+
+ // Helper method to lookup a source node with given |source_id|.
+ //
+ // @param source_id Source id.
+ // @returns Shared pointer to source node instance, nullptr if not found.
+ std::shared_ptr<BufferedSourceNode> LookupSourceNode(SourceId source_id);
+
+ // Creates an audio subgraph that renders early reflections based on a room
+ // model on a single mix.
+ //
+ // Processing graph:
+ //
+ // +---------------------------+
+ // | |
+ // | ReflectionsGainMixer |
+ // | |
+ // +-------------+-------------+
+ // |
+ // +----------v----------+
+ // | |
+ // | Reflections |
+ // | |
+ // +----------+----------+
+ // |
+ // +----------v----------+
+ // | |
+ // | AmbisonicMixer |
+ // | |
+ // +----------+----------+
+ //
+ void InitializeReflectionsGraph();
+
+ // Creates an audio subgraph that renders a reverb from a mono mix of all the
+ // sound objects based on a room model.
+ //
+ // Processing graph:
+ //
+ // +-----------------+
+ // | |
+ // | ReverbGainMixer |
+ // | |
+ // +--------+--------+
+ // |
+ // +--------v--------+
+ // | |
+ // | Reverb |
+ // | |
+ // +--------+--------+
+ // |
+ // +--------v--------+
+ // | |
+ // | StereoMixer |
+ // | |
+ // +-----------------+
+ //
+ void InitializeReverbGraph();
+
+ // Flag indicating if room effects are enabled.
+ bool room_effects_enabled_;
+
+ // Mono mixer to accumulate all reverb sources.
+ std::shared_ptr<GainMixerNode> reverb_gain_mixer_node_;
+
+ // Reflections node.
+ std::shared_ptr<ReflectionsNode> reflections_node_;
+
+ // Mono mixer node to accumulate the early reflection sources.
+ std::shared_ptr<GainMixerNode> reflections_gain_mixer_node_;
+
+ // Reverb node.
+ std::shared_ptr<ReverbNode> reverb_node_;
+
+ // Ambisonic output mixer to accumulate incoming ambisonic inputs into a
+ // single ambisonic output buffer.
+ std::unique_ptr<Mixer> ambisonic_output_mixer_;
+
+ // Global config passed in during construction.
+ const GraphManagerConfig config_;
+
+ // Manages system wide settings.
+ const SystemSettings& system_settings_;
+
+ // Provides Ambisonic encoding coefficients.
+ std::unique_ptr<AmbisonicLookupTable> lookup_table_;
+
+ // |FftManager| to be used in nodes that require FFT transformations.
+ FftManager fft_manager_;
+
+ // |Resampler| to be used to convert HRIRs to the system sample rate.
+ Resampler resampler_;
+
+ // Ambisonic mixer nodes per each ambisonic order to accumulate the
+ // ambisonic sources for the corresponding binaural Ambisonic decoders.
+ std::unordered_map<int, std::shared_ptr<MixerNode>> ambisonic_mixer_nodes_;
+
+ // Stereo mixer to combine all the stereo and binaural output.
+ std::shared_ptr<MixerNode> stereo_mixer_node_;
+
+ // Ambisonic mixing encoder node to apply encoding coefficients and accumulate
+ // the Ambisonic buffers.
+ std::unordered_map<int, std::shared_ptr<AmbisonicMixingEncoderNode>>
+ ambisonic_mixing_encoder_nodes_;
+
+ // Stereo mixing panner node to apply stereo panning gains and accumulate the
+ // buffers.
+ std::shared_ptr<StereoMixingPannerNode> stereo_mixing_panner_node_;
+
+ // Output node that enables audio playback of a single audio stream.
+ std::shared_ptr<SinkNode> output_node_;
+
+ // Holds all registered source nodes (independently of their type) and
+ // allows look up by id.
+ std::unordered_map<SourceId, std::shared_ptr<BufferedSourceNode>>
+ source_nodes_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_GRAPH_MANAGER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager_config.h b/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager_config.h
new file mode 100644
index 000000000..f3c310a06
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager_config.h
@@ -0,0 +1,40 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_GRAPH_MANAGER_CONFIG_H_
+#define RESONANCE_AUDIO_GRAPH_GRAPH_MANAGER_CONFIG_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace vraudio {
+
+// Configuration of the GraphManager and the nodes it is instantiating.
+struct GraphManagerConfig {
+ // Configuration name.
+ std::string configuration_name;
+
+ // Maximum ambisonic order allowed.
+ int max_ambisonic_order = 1;
+
+ // HRIR filenames (second element) per ambisonic order (first element).
+ std::vector<std::pair<int, std::string>> sh_hrir_filenames = {};
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_GRAPH_MANAGER_CONFIG_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/hoa_rotator_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/hoa_rotator_node.cc
new file mode 100644
index 000000000..4ffae89c3
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/hoa_rotator_node.cc
@@ -0,0 +1,69 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/hoa_rotator_node.h"
+
+#include "ambisonics/utils.h"
+#include "base/logging.h"
+
+
+namespace vraudio {
+
+HoaRotatorNode::HoaRotatorNode(SourceId source_id,
+ const SystemSettings& system_settings,
+ int ambisonic_order)
+ : system_settings_(system_settings),
+ hoa_rotator_(ambisonic_order),
+ output_buffer_(GetNumPeriphonicComponents(ambisonic_order),
+ system_settings.GetFramesPerBuffer()) {
+ output_buffer_.Clear();
+ output_buffer_.set_source_id(source_id);
+}
+
+const AudioBuffer* HoaRotatorNode::AudioProcess(const NodeInput& input) {
+
+
+ const AudioBuffer* input_buffer = input.GetSingleInput();
+ DCHECK(input_buffer);
+ DCHECK_GT(input_buffer->num_frames(), 0U);
+ DCHECK_GE(input_buffer->num_channels(), 4U);
+ DCHECK_EQ(input_buffer->source_id(), output_buffer_.source_id());
+
+ // Rotate soundfield buffer by the inverse head orientation.
+ const auto source_parameters =
+ system_settings_.GetSourceParameters(input_buffer->source_id());
+ if (source_parameters == nullptr) {
+ LOG(WARNING) << "Could not find source parameters";
+ return nullptr;
+ }
+
+ const WorldRotation& source_rotation =
+ source_parameters->object_transform.rotation;
+ const WorldRotation inverse_head_rotation =
+ system_settings_.GetHeadRotation().conjugate();
+ const WorldRotation rotation = inverse_head_rotation * source_rotation;
+ const bool rotation_applied =
+ hoa_rotator_.Process(rotation, *input_buffer, &output_buffer_);
+
+ if (!rotation_applied) {
+ return input_buffer;
+ }
+
+ // Copy buffer parameters.
+ return &output_buffer_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/hoa_rotator_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/hoa_rotator_node.h
new file mode 100644
index 000000000..1ad3442be
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/hoa_rotator_node.h
@@ -0,0 +1,55 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_HOA_ROTATOR_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_HOA_ROTATOR_NODE_H_
+
+#include <memory>
+
+#include "ambisonics/hoa_rotator.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+// Node that accepts a single PeriphonicSoundfieldBuffer as input and outputs a
+// rotated PeriphonicSoundfieldBuffer of the corresponding soundfield input
+// using head rotation information from the system settings.
+class HoaRotatorNode : public ProcessingNode {
+ public:
+ HoaRotatorNode(SourceId source_id, const SystemSettings& system_settings,
+ int ambisonic_order);
+
+ protected:
+ // Implements ProcessingNode. Returns a null pointer if we are in stereo
+ // loudspeaker or stereo pan mode.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ const SystemSettings& system_settings_;
+
+ // Soundfield rotator used to rotate higher order soundfields.
+ HoaRotator hoa_rotator_;
+
+ // Output buffer.
+ AudioBuffer output_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_HOA_ROTATOR_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/mixer_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/mixer_node.cc
new file mode 100644
index 000000000..8bb91c767
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/mixer_node.cc
@@ -0,0 +1,57 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/mixer_node.h"
+
+
+
+namespace vraudio {
+
+MixerNode::MixerNode(const SystemSettings& system_settings, size_t num_channels)
+ : num_channels_(num_channels),
+ mixer_(num_channels_, system_settings.GetFramesPerBuffer()) {
+ DCHECK_NE(num_channels_, 0U);
+ EnableProcessOnEmptyInput(true);
+}
+
+const AudioBuffer* MixerNode::GetOutputBuffer() const {
+ return mixer_.GetOutput();
+}
+
+bool MixerNode::CleanUp() {
+ CallCleanUpOnInputNodes();
+ // Prevent node from being disconnected when all sources are removed.
+ return false;
+}
+
+const AudioBuffer* MixerNode::AudioProcess(const NodeInput& input) {
+
+
+ mixer_.Reset();
+
+ const auto& input_buffers = input.GetInputBuffers();
+ if (input_buffers.empty()) {
+ return nullptr;
+ }
+
+ for (auto input_buffer : input_buffers) {
+ DCHECK_EQ(input_buffer->num_channels(), num_channels_);
+ mixer_.AddInput(*input_buffer);
+ }
+ return mixer_.GetOutput();
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/mixer_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/mixer_node.h
new file mode 100644
index 000000000..0fe88f98b
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/mixer_node.h
@@ -0,0 +1,54 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_MIXER_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_MIXER_NODE_H_
+
+#include "base/audio_buffer.h"
+#include "dsp/mixer.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+// Accepts multiple input buffers and outputs a downmix to a single output
+// buffer. All input buffers must have the same number of channels and the same
+// number of frames per buffer.
+class MixerNode : public ProcessingNode {
+ public:
+ MixerNode(const SystemSettings& system_settings, size_t num_channels);
+
+ // Returns the current output buffer of the mixer.
+ //
+ // @return Output audio buffer.
+ const AudioBuffer* GetOutputBuffer() const;
+
+ // Node implementation.
+ bool CleanUp() final;
+
+ protected:
+ // Implements ProcessingNode.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ const size_t num_channels_;
+
+ Mixer mixer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_MIXER_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/mixer_node_test.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/mixer_node_test.cc
new file mode 100644
index 000000000..db9e72e17
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/mixer_node_test.cc
@@ -0,0 +1,112 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/mixer_node.h"
+
+#include <algorithm>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/logging.h"
+#include "graph/system_settings.h"
+#include "node/sink_node.h"
+#include "node/source_node.h"
+
+namespace vraudio {
+
+namespace {
+
+// Helper class to detect destruction.
+class DeletionDetector {
+ public:
+ explicit DeletionDetector(bool* node_deletion_flag)
+ : node_deletion_flag_(node_deletion_flag) {}
+ ~DeletionDetector() {
+ if (node_deletion_flag_ != nullptr) {
+ *node_deletion_flag_ = true;
+ }
+ }
+
+ private:
+ bool* node_deletion_flag_;
+};
+
+// Wraps |SourceNode| to detect its deletion.
+class MySourceNode : public SourceNode, DeletionDetector {
+ public:
+ explicit MySourceNode(bool* node_deletion_flag)
+ : SourceNode(), DeletionDetector(node_deletion_flag) {}
+
+ protected:
+ const AudioBuffer* AudioProcess() final { return nullptr; }
+};
+
+// Wraps |MixerNode| to detect its deletion.
+class MyAudioMixerNode : public MixerNode, DeletionDetector {
+ public:
+ explicit MyAudioMixerNode(bool* node_deletion_flag,
+ const SystemSettings& system_settings)
+ : MixerNode(system_settings, kNumMonoChannels),
+ DeletionDetector(node_deletion_flag) {}
+};
+
+// Wraps |SinkNode| to detect its deletion.
+class MySinkNode : public SinkNode, DeletionDetector {
+ public:
+ explicit MySinkNode(bool* node_deletion_flag)
+ : SinkNode(), DeletionDetector(node_deletion_flag) {}
+};
+
+// Tests that the |MixerNode| keeps connected at the moment all of its sources
+// are removed.
+TEST(AudioNodesTest, cleanUpOnEmptyInputTest) {
+ SystemSettings system_settings_(kNumMonoChannels, 128 /* frames_per_buffer */,
+ 48000 /* sample_rate_hz */);
+
+ bool source_node_deleted = false;
+ bool mixer_node_deleted = false;
+ bool sink_node_deleted = false;
+
+ auto sink_node = std::make_shared<MySinkNode>(&sink_node_deleted);
+
+ {
+ // Create a source and mixer node and connect it to sink node.
+ auto source_node = std::make_shared<MySourceNode>(&source_node_deleted);
+ auto mixer_node = std::make_shared<MyAudioMixerNode>(&mixer_node_deleted,
+ system_settings_);
+
+ // Connect nodes.
+ sink_node->Connect(mixer_node);
+ mixer_node->Connect(source_node);
+
+ // End of stream is marked in source node. Do not expect any data anymore.
+ source_node->MarkEndOfStream();
+ }
+
+ EXPECT_FALSE(source_node_deleted);
+ EXPECT_FALSE(mixer_node_deleted);
+ EXPECT_FALSE(sink_node_deleted);
+
+ sink_node->CleanUp();
+
+ EXPECT_TRUE(source_node_deleted);
+ EXPECT_FALSE(mixer_node_deleted);
+ EXPECT_FALSE(sink_node_deleted);
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/mono_from_soundfield_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/mono_from_soundfield_node.cc
new file mode 100644
index 000000000..0519be5ef
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/mono_from_soundfield_node.cc
@@ -0,0 +1,45 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/mono_from_soundfield_node.h"
+
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+MonoFromSoundfieldNode::MonoFromSoundfieldNode(
+ SourceId source_id, const SystemSettings& system_settings)
+ : output_buffer_(kNumMonoChannels, system_settings.GetFramesPerBuffer()) {
+ output_buffer_.set_source_id(source_id);
+ output_buffer_.Clear();
+}
+
+const AudioBuffer* MonoFromSoundfieldNode::AudioProcess(
+ const NodeInput& input) {
+
+
+ const AudioBuffer* input_buffer = input.GetSingleInput();
+ DCHECK(input_buffer);
+ DCHECK_EQ(input_buffer->source_id(), output_buffer_.source_id());
+ DCHECK_NE(input_buffer->num_channels(), 0U);
+ DCHECK_EQ(input_buffer->num_frames(), output_buffer_.num_frames());
+ // Get W channel of the ambisonic input.
+ output_buffer_[0] = (*input_buffer)[0];
+
+ return &output_buffer_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/mono_from_soundfield_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/mono_from_soundfield_node.h
new file mode 100644
index 000000000..6e8525e17
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/mono_from_soundfield_node.h
@@ -0,0 +1,44 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_MONO_FROM_SOUNDFIELD_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_MONO_FROM_SOUNDFIELD_NODE_H_
+
+#include "base/audio_buffer.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+// Node that accepts an ambisonic buffer as input and extracts its W channel
+// onto a mono output buffer.
+class MonoFromSoundfieldNode : public ProcessingNode {
+ public:
+ MonoFromSoundfieldNode(SourceId source_id,
+ const SystemSettings& system_settings);
+
+ protected:
+ // Implements |ProcessingNode|.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ // Mono audio buffer to store output data.
+ AudioBuffer output_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_MONO_FROM_SOUNDFIELD_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/near_field_effect_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/near_field_effect_node.cc
new file mode 100644
index 000000000..2b75ae6e9
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/near_field_effect_node.cc
@@ -0,0 +1,117 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/near_field_effect_node.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/spherical_angle.h"
+
+#include "dsp/distance_attenuation.h"
+#include "dsp/gain.h"
+#include "dsp/stereo_panner.h"
+
+namespace vraudio {
+
+NearFieldEffectNode::NearFieldEffectNode(SourceId source_id,
+ const SystemSettings& system_settings)
+ : pan_gains_({0.0f, 0.0f}),
+ near_field_processor_(system_settings.GetSampleRateHz(),
+ system_settings.GetFramesPerBuffer()),
+ system_settings_(system_settings),
+ output_buffer_(kNumStereoChannels, system_settings.GetFramesPerBuffer()) {
+ output_buffer_.set_source_id(source_id);
+}
+
+const AudioBuffer* NearFieldEffectNode::AudioProcess(const NodeInput& input) {
+
+
+ const AudioBuffer* input_buffer = input.GetSingleInput();
+ DCHECK(input_buffer);
+ DCHECK_EQ(input_buffer->num_channels(), 1U);
+ DCHECK_EQ(input_buffer->source_id(), output_buffer_.source_id());
+
+ const auto source_parameters =
+ system_settings_.GetSourceParameters(input_buffer->source_id());
+ if (source_parameters == nullptr) {
+ LOG(WARNING) << "Could not find source parameters";
+ return nullptr;
+ }
+
+
+ DCHECK_EQ(pan_gains_.size(), kNumStereoChannels);
+ const float near_field_gain = source_parameters->near_field_gain;
+ if (near_field_gain > 0.0f) {
+ const auto& listener_position = system_settings_.GetHeadPosition();
+ const auto& listener_rotation = system_settings_.GetHeadRotation();
+ const auto& source_transform = source_parameters->object_transform;
+ // Compute the relative source direction in spherical angles to calculate
+ // the left and right panner gains.
+ WorldPosition relative_direction;
+ GetRelativeDirection(listener_position, listener_rotation,
+ source_transform.position, &relative_direction);
+ const auto source_direction =
+ SphericalAngle::FromWorldPosition(relative_direction);
+ CalculateStereoPanGains(source_direction, &pan_gains_);
+ // Combine pan gains with per-source near field gain.
+ const float total_near_field_gain =
+ ComputeNearFieldEffectGain(listener_position,
+ source_transform.position) *
+ near_field_gain / kMaxNearFieldEffectGain;
+ for (size_t i = 0; i < pan_gains_.size(); ++i) {
+ pan_gains_[i] *= total_near_field_gain;
+ }
+ } else {
+ // Disable near field effect if |near_field_gain| is zero.
+ std::fill(pan_gains_.begin(), pan_gains_.end(), 0.0f);
+ }
+
+ const float left_current_gain = left_panner_.GetGain();
+ const float right_current_gain = right_panner_.GetGain();
+ const float left_target_gain = pan_gains_[0];
+ const float right_target_gain = pan_gains_[1];
+ const bool is_left_zero_gain =
+ IsGainNearZero(left_current_gain) && IsGainNearZero(left_target_gain);
+ const bool is_right_zero_gain =
+ IsGainNearZero(right_current_gain) && IsGainNearZero(right_target_gain);
+
+ if (is_left_zero_gain && is_right_zero_gain) {
+ // Make sure gain processors are initialized.
+ left_panner_.Reset(0.0f);
+ right_panner_.Reset(0.0f);
+ // Both channels go to zero, there is no need for further processing.
+ return nullptr;
+ }
+
+ const auto& input_channel = (*input_buffer)[0];
+ auto* left_output_channel = &output_buffer_[0];
+ auto* right_output_channel = &output_buffer_[1];
+ // Apply bass boost and delay compensation (if necessary) to the input signal
+ // and place it temporarily in the right output channel. This way we avoid
+ // allocating a temporary buffer.
+ near_field_processor_.Process(input_channel, right_output_channel,
+ source_parameters->enable_hrtf);
+ left_panner_.ApplyGain(left_target_gain, *right_output_channel,
+ left_output_channel, /*accumulate_output=*/false);
+ right_panner_.ApplyGain(right_target_gain, *right_output_channel,
+ right_output_channel, /*accumulate_output=*/false);
+
+ return &output_buffer_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/near_field_effect_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/near_field_effect_node.h
new file mode 100644
index 000000000..83b8c6d69
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/near_field_effect_node.h
@@ -0,0 +1,69 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_NEAR_FIELD_EFFECT_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_NEAR_FIELD_EFFECT_NODE_H_
+
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "dsp/gain_processor.h"
+#include "dsp/near_field_processor.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+// Node that accepts a single mono audio buffer as input, applies an appoximate
+// near field effect and outputs a processed stereo audio buffer. The stereo
+// output buffer can then be combined with a binaural output in order to
+// simulate a sound source which is close (<1m) to the listener's head.
+class NearFieldEffectNode : public ProcessingNode {
+ public:
+ // Constructor.
+ //
+ // @param source_id Output buffer source id.
+ // @param system_settings Global system settings.
+ NearFieldEffectNode(SourceId source_id,
+ const SystemSettings& system_settings);
+
+ protected:
+ // Implements ProcessingNode.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ // Left and right processors apply both near field gain and panner gains.
+ GainProcessor left_panner_;
+ GainProcessor right_panner_;
+
+ // Left and right gains for near field and panning combined.
+ std::vector<float> pan_gains_;
+
+ // Near field processor used to apply approximate near field effect to the
+ // mono source signal.
+ NearFieldProcessor near_field_processor_;
+
+ // Used to obtain head rotation.
+ const SystemSettings& system_settings_;
+
+ // Output buffer.
+ AudioBuffer output_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_NEAR_FIELD_EFFECT_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/near_field_effect_node_test.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/near_field_effect_node_test.cc
new file mode 100644
index 000000000..1117ccd21
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/near_field_effect_node_test.cc
@@ -0,0 +1,127 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/near_field_effect_node.h"
+
+#include <memory>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "dsp/distance_attenuation.h"
+#include "dsp/stereo_panner.h"
+#include "graph/buffered_source_node.h"
+#include "node/sink_node.h"
+#include "node/source_node.h"
+#include "utils/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+// Source Id for use with |BufferedSourceNode|.
+const SourceId kSourceId = 0;
+
+// Number of frames per buffer.
+const size_t kFramesPerBuffer = kUnitRampLength;
+
+// Sampling rate.
+const int kSampleRate = 48000;
+
+// Source distances.
+const size_t kNumDistances = 5;
+const float kDistances[kNumDistances] = {0.0f, 0.25f, 0.5f, 0.75f, 10.0f};
+
+// Maximum expected gain change determines the number of buffers we need to wait
+// for the output sample values to settle.
+const size_t kMaxExpectedGainChange = 9;
+
+// Expected offset due to near field processor delay compensation at 48000kHz.
+const size_t kExpectedDelay = 31;
+
+// Expected Dirac pulse attenuation due to shelf-filtering at 48000kHz.
+const float KExpectedPeakReduction = 0.87319273f;
+
+} // namespace
+
+TEST(NearFieldEffectNodeTest, VariousDistanceTest) {
+ const size_t kDiracOffset = kFramesPerBuffer / 2;
+ // We add one extra buffer since the Dirac is in the middle of the buffer.
+ const size_t kBuffersToSettle = kMaxExpectedGainChange + 1;
+ const auto kIdentityRotation = WorldRotation();
+ SystemSettings system_settings(kNumStereoChannels, kFramesPerBuffer,
+ kSampleRate);
+
+ // Create the simple audio graph.
+ auto near_field_effect_node =
+ std::make_shared<NearFieldEffectNode>(kSourceId, system_settings);
+ auto input_node = std::make_shared<BufferedSourceNode>(
+ kSourceId, kNumMonoChannels, kFramesPerBuffer);
+ auto output_node = std::make_shared<SinkNode>();
+ near_field_effect_node->Connect(input_node);
+ output_node->Connect(near_field_effect_node);
+ auto parameters_manager = system_settings.GetSourceParametersManager();
+ parameters_manager->Register(kSourceId);
+
+ const AudioBuffer* output_buffer;
+ for (size_t i = 0; i < kNumDistances; ++i) {
+ const WorldPosition input_position(kDistances[i], 0.0f, 0.0f);
+ // Loop till gain processors have reached steady state.
+ for (size_t settle = 0; settle < kBuffersToSettle; ++settle) {
+ AudioBuffer* const input_node_buffer =
+ input_node->GetMutableAudioBufferAndSetNewBufferFlag();
+ GenerateDiracImpulseFilter(kDiracOffset, &(*input_node_buffer)[0]);
+
+ auto source_parameters =
+ parameters_manager->GetMutableParameters(kSourceId);
+ source_parameters->object_transform.position = input_position;
+ source_parameters->near_field_gain = kMaxNearFieldEffectGain;
+ // Retrieve the output.
+ const auto& buffer_vector = output_node->ReadInputs();
+ if (!buffer_vector.empty()) {
+ EXPECT_EQ(buffer_vector.size(), 1U);
+ output_buffer = buffer_vector.front();
+ } else {
+ output_buffer = nullptr;
+ }
+ }
+
+ std::vector<float> stereo_pan_gains(kNumStereoChannels, 0.0f);
+ // These methods are tested elsewhere. Their output will be used to
+ // determine if the output from the |NearfieldEffectNode| is correct.
+ WorldPosition relative_direction;
+ GetRelativeDirection(system_settings.GetHeadPosition(), kIdentityRotation,
+ input_position, &relative_direction);
+ const SphericalAngle source_direction =
+ SphericalAngle::FromWorldPosition(relative_direction);
+ CalculateStereoPanGains(source_direction, &stereo_pan_gains);
+ const float near_field_gain = ComputeNearFieldEffectGain(
+ system_settings.GetHeadPosition(), input_position);
+ if (i < kNumDistances - 1) {
+ EXPECT_FALSE(output_buffer == nullptr);
+ EXPECT_NEAR(
+ near_field_gain * stereo_pan_gains[0] * KExpectedPeakReduction,
+ (*output_buffer)[0][kDiracOffset + kExpectedDelay], kEpsilonFloat);
+ EXPECT_NEAR(
+ near_field_gain * stereo_pan_gains[1] * KExpectedPeakReduction,
+ (*output_buffer)[1][kDiracOffset + kExpectedDelay], kEpsilonFloat);
+ } else {
+ EXPECT_TRUE(output_buffer == nullptr);
+ }
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/occlusion_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/occlusion_node.cc
new file mode 100644
index 000000000..3805836f5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/occlusion_node.cc
@@ -0,0 +1,101 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/occlusion_node.h"
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/spherical_angle.h"
+
+#include "dsp/occlusion_calculator.h"
+
+namespace vraudio {
+
+namespace {
+
+// Low pass filter coefficient for smoothing the applied occlusion. This avoids
+// sudden unrealistic changes in the volume of a sound object. Range [0, 1].
+// The value below has been calculated empirically.
+const float kOcclusionSmoothingCoefficient = 0.75f;
+
+// This function provides first order low-pass filtering. It is used to smooth
+// the occlusion parameter.
+float Interpolate(float coefficient, float previous_value, float target_value) {
+ return target_value + coefficient * (previous_value - target_value);
+}
+
+} // namespace
+
+OcclusionNode::OcclusionNode(SourceId source_id,
+ const SystemSettings& system_settings)
+ : system_settings_(system_settings),
+ low_pass_filter_(0.0f),
+ current_occlusion_(0.0f),
+ output_buffer_(kNumMonoChannels, system_settings.GetFramesPerBuffer()) {
+ output_buffer_.Clear();
+ output_buffer_.set_source_id(source_id);
+}
+
+const AudioBuffer* OcclusionNode::AudioProcess(const NodeInput& input) {
+
+ const AudioBuffer* input_buffer = input.GetSingleInput();
+ DCHECK(input_buffer);
+ DCHECK_EQ(input_buffer->source_id(), output_buffer_.source_id());
+
+ const auto source_parameters =
+ system_settings_.GetSourceParameters(input_buffer->source_id());
+ if (source_parameters == nullptr) {
+ LOG(WARNING) << "Could not find source parameters";
+ return nullptr;
+ }
+
+ const WorldPosition& listener_position = system_settings_.GetHeadPosition();
+ const WorldRotation& listener_rotation = system_settings_.GetHeadRotation();
+ const ObjectTransform& source_transform = source_parameters->object_transform;
+ // Compute the relative listener/source direction in spherical angles.
+ WorldPosition relative_direction;
+ GetRelativeDirection(listener_position, listener_rotation,
+ source_transform.position, &relative_direction);
+ const SphericalAngle listener_direction =
+ SphericalAngle::FromWorldPosition(relative_direction);
+
+ GetRelativeDirection(source_transform.position, source_transform.rotation,
+ listener_position, &relative_direction);
+ const SphericalAngle source_direction =
+ SphericalAngle::FromWorldPosition(relative_direction);
+ // Calculate low-pass filter coefficient based on listener/source directivity
+ // and occlusion values.
+ const float listener_directivity = CalculateDirectivity(
+ source_parameters->listener_directivity_alpha,
+ source_parameters->listener_directivity_order, listener_direction);
+ const float source_directivity = CalculateDirectivity(
+ source_parameters->directivity_alpha,
+ source_parameters->directivity_order, source_direction);
+ current_occlusion_ =
+ Interpolate(kOcclusionSmoothingCoefficient, current_occlusion_,
+ source_parameters->occlusion_intensity);
+ const float filter_coefficient = CalculateOcclusionFilterCoefficient(
+ listener_directivity * source_directivity, current_occlusion_);
+ low_pass_filter_.SetCoefficient(filter_coefficient);
+ if (!low_pass_filter_.Filter((*input_buffer)[0], &output_buffer_[0])) {
+ return input_buffer;
+ }
+ // Copy buffer parameters.
+ return &output_buffer_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/occlusion_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/occlusion_node.h
new file mode 100644
index 000000000..316788751
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/occlusion_node.h
@@ -0,0 +1,59 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_OCCLUSION_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_OCCLUSION_NODE_H_
+
+#include "base/audio_buffer.h"
+#include "dsp/mono_pole_filter.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+// Node that accepts a single audio buffer as input and outputs the input buffer
+// with its cuttoff frequency scaled by listener/source directivity and
+// occlusion intensity.
+class OcclusionNode : public ProcessingNode {
+ public:
+ // Constructor.
+ //
+ // @param source_id Output buffer source id.
+ // @param system_settings Global system settings.
+ OcclusionNode(SourceId source_id, const SystemSettings& system_settings);
+
+ protected:
+ // Implements ProcessingNode.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ friend class OcclusionNodeTest;
+
+ const SystemSettings& system_settings_;
+
+ // Used to low-pass input audio when a source is occluded or self-occluded.
+ MonoPoleFilter low_pass_filter_;
+
+ // Occlusion intensity value for the current input buffer.
+ float current_occlusion_;
+
+ // Output buffer.
+ AudioBuffer output_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_OCCLUSION_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/occlusion_node_test.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/occlusion_node_test.cc
new file mode 100644
index 000000000..3dac808c3
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/occlusion_node_test.cc
@@ -0,0 +1,186 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/occlusion_node.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+#include "utils/test_util.h"
+
+namespace vraudio {
+
+namespace {
+
+// Number of frames per buffer.
+const size_t kFramesPerBuffer = 256;
+
+// Sampling rate.
+const int kSampleRate = 48000;
+
+// Generated sawtooth length for test buffers.
+const size_t kSawtoothLength = 16;
+
+// Source id.
+const SourceId kSourceId = 1;
+
+} // namespace
+
+class OcclusionNodeTest : public ::testing::Test {
+ protected:
+ OcclusionNodeTest()
+ : system_settings_(kNumStereoChannels, kFramesPerBuffer, kSampleRate) {}
+
+ void SetUp() override {
+ system_settings_.GetSourceParametersManager()->Register(kSourceId);
+ }
+
+ // Function which wraps the buffer passed in a vector for processing and
+ // returns the output of the occlusion nodes AudioProcess method.
+ const AudioBuffer* GetProcessedData(const AudioBuffer* input_buffer,
+ OcclusionNode* occlusion_node) {
+ std::vector<const AudioBuffer*> input_buffers;
+ input_buffers.push_back(input_buffer);
+ return occlusion_node->AudioProcess(
+ ProcessingNode::NodeInput(input_buffers));
+ }
+
+ // Returns a pointer to the parameters of the source.
+ SourceParameters* GetParameters() {
+ return system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ kSourceId);
+ }
+
+ // System settings.
+ SystemSettings system_settings_;
+};
+
+// Test to ensure that no effect is made on a buffer of input if both occlusion
+// and self-occlusion are absent.
+TEST_F(OcclusionNodeTest, NoOcclusionTest) {
+ OcclusionNode occlusion_processor(kSourceId, system_settings_);
+
+ AudioBuffer input(1, kFramesPerBuffer);
+ input.set_source_id(kSourceId);
+ GenerateSawToothSignal(kSawtoothLength, &input[0]);
+
+ const AudioBuffer* output = GetProcessedData(&input, &occlusion_processor);
+ const bool buffers_identical =
+ CompareAudioBuffers((*output)[0], input[0], kEpsilonFloat);
+ EXPECT_TRUE(buffers_identical);
+}
+
+// Test to ensure that a more heavily occluded object shows a lower energy
+// output.
+TEST_F(OcclusionNodeTest, OcclusionTest) {
+ OcclusionNode occlusion_processor_1(kSourceId, system_settings_);
+ OcclusionNode occlusion_processor_2(kSourceId, system_settings_);
+
+ AudioBuffer input(kNumMonoChannels, kFramesPerBuffer);
+ GenerateSawToothSignal(kSawtoothLength, &input[0]);
+ AudioBuffer input_1;
+ input_1 = input;
+ input_1.set_source_id(kSourceId);
+ AudioBuffer input_2;
+ input_2 = input;
+ input_2.set_source_id(kSourceId);
+
+ SourceParameters* parameters = GetParameters();
+
+ parameters->occlusion_intensity = 0.5f;
+ const AudioBuffer* output_1 =
+ GetProcessedData(&input_1, &occlusion_processor_1);
+ parameters->occlusion_intensity = 1.0f;
+ const AudioBuffer* output_2 =
+ GetProcessedData(&input_2, &occlusion_processor_2);
+ const double output_1_energy = CalculateSignalRms((*output_1)[0]);
+ const double output_2_energy = CalculateSignalRms((*output_2)[0]);
+ const double input_energy = CalculateSignalRms(input[0]);
+
+ EXPECT_LT(output_1_energy, input_energy);
+ EXPECT_LT(output_2_energy, output_1_energy);
+}
+
+// Test to ensure that setting a non-omnidirectional listener directivity
+// pattern shows a lower energy output when the listener orientation is pointing
+// away from a source.
+TEST_F(OcclusionNodeTest, ListenerDirectivityTest) {
+ OcclusionNode occlusion_processor(kSourceId, system_settings_);
+
+ AudioBuffer input(kNumMonoChannels, kFramesPerBuffer);
+ input.set_source_id(kSourceId);
+ GenerateSawToothSignal(kSawtoothLength, &input[0]);
+
+ // Set a hyper-cardioid shaped listener directivity for the source input.
+ SourceParameters* parameters = GetParameters();
+ parameters->listener_directivity_alpha = 0.5f;
+ parameters->listener_directivity_order = 2.0f;
+ // Set listener position to one meter away from the origin in Z axis. This is
+ // required for the listener directivity properties to take effect.
+ system_settings_.SetHeadPosition(WorldPosition(0.0f, 0.0f, 1.0f));
+
+ const double input_energy = CalculateSignalRms(input[0]);
+ // Process input with identity listener orientation.
+ const AudioBuffer* output_default =
+ GetProcessedData(&input, &occlusion_processor);
+ const double output_default_energy = CalculateSignalRms((*output_default)[0]);
+ // Process input with 90 degrees rotated listener orientation about Y axis.
+ system_settings_.SetHeadRotation(
+ WorldRotation(0.0f, kInverseSqrtTwo, 0.0f, kInverseSqrtTwo));
+ const AudioBuffer* output_rotated =
+ GetProcessedData(&input, &occlusion_processor);
+ const double output_rotated_energy = CalculateSignalRms((*output_rotated)[0]);
+
+ // Test if the output energy is lower when the listener was rotated away from
+ // the source.
+ EXPECT_NEAR(output_default_energy, input_energy, kEpsilonFloat);
+ EXPECT_LT(output_rotated_energy, output_default_energy);
+}
+
+// Test to ensure that a more heavily self occluded source shows a lower energy
+// output.
+TEST_F(OcclusionNodeTest, SourceDirectivityTest) {
+ OcclusionNode occlusion_processor_1(kSourceId, system_settings_);
+ OcclusionNode occlusion_processor_2(kSourceId, system_settings_);
+
+ AudioBuffer input(kNumMonoChannels, kFramesPerBuffer);
+ input.set_source_id(kSourceId);
+ GenerateSawToothSignal(kSawtoothLength, &input[0]);
+ AudioBuffer input_1;
+ input_1 = input;
+ AudioBuffer input_2;
+ input_2 = input;
+
+ SourceParameters* parameters = GetParameters();
+
+ parameters->directivity_alpha = 0.25f;
+ parameters->directivity_order = 2.0f;
+ const AudioBuffer* output_1 =
+ GetProcessedData(&input_1, &occlusion_processor_1);
+
+ parameters->directivity_order = 4.0f;
+ parameters->directivity_alpha = 0.25f;
+ const AudioBuffer* output_2 =
+ GetProcessedData(&input_2, &occlusion_processor_2);
+
+ const double output_1_energy = CalculateSignalRms((*output_1)[0]);
+ const double output_2_energy = CalculateSignalRms((*output_2)[0]);
+ const double input_energy = CalculateSignalRms(input[0]);
+
+ EXPECT_LT(output_1_energy, input_energy);
+ EXPECT_LT(output_2_energy, output_1_energy);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/reflections_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/reflections_node.cc
new file mode 100644
index 000000000..1223fe7e8
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/reflections_node.cc
@@ -0,0 +1,113 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/reflections_node.h"
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/misc_math.h"
+
+
+namespace vraudio {
+
+ReflectionsNode::ReflectionsNode(const SystemSettings& system_settings)
+ : system_settings_(system_settings),
+ reflections_processor_(system_settings_.GetSampleRateHz(),
+ system_settings_.GetFramesPerBuffer()),
+ num_frames_processed_on_empty_input_(
+ system_settings_.GetFramesPerBuffer()),
+ output_buffer_(kNumFirstOrderAmbisonicChannels,
+ system_settings_.GetFramesPerBuffer()),
+ silence_mono_buffer_(kNumMonoChannels,
+ system_settings_.GetFramesPerBuffer()) {
+ silence_mono_buffer_.Clear();
+ EnableProcessOnEmptyInput(true);
+}
+
+void ReflectionsNode::Update() {
+
+ const auto& current_reflection_properties = reflection_properties_;
+ const auto& new_reflection_properties =
+ system_settings_.GetReflectionProperties();
+ const bool room_position_changed =
+ !EqualSafe(std::begin(current_reflection_properties.room_position),
+ std::end(current_reflection_properties.room_position),
+ std::begin(new_reflection_properties.room_position),
+ std::end(new_reflection_properties.room_position));
+ const bool room_rotation_changed =
+ !EqualSafe(std::begin(current_reflection_properties.room_rotation),
+ std::end(current_reflection_properties.room_rotation),
+ std::begin(new_reflection_properties.room_rotation),
+ std::end(new_reflection_properties.room_rotation));
+ const bool room_dimensions_changed =
+ !EqualSafe(std::begin(current_reflection_properties.room_dimensions),
+ std::end(current_reflection_properties.room_dimensions),
+ std::begin(new_reflection_properties.room_dimensions),
+ std::end(new_reflection_properties.room_dimensions));
+ const bool cutoff_frequency_changed =
+ current_reflection_properties.cutoff_frequency !=
+ new_reflection_properties.cutoff_frequency;
+ const bool coefficients_changed =
+ !EqualSafe(std::begin(current_reflection_properties.coefficients),
+ std::end(current_reflection_properties.coefficients),
+ std::begin(new_reflection_properties.coefficients),
+ std::end(new_reflection_properties.coefficients));
+ const auto& current_listener_position = listener_position_;
+ const auto& new_listener_position = system_settings_.GetHeadPosition();
+ const bool listener_position_changed =
+ current_listener_position != new_listener_position;
+ if (room_position_changed || room_rotation_changed ||
+ room_dimensions_changed || cutoff_frequency_changed ||
+ coefficients_changed || listener_position_changed) {
+ // Update reflections processor if necessary.
+ reflection_properties_ = new_reflection_properties;
+ listener_position_ = new_listener_position;
+ reflections_processor_.Update(reflection_properties_, listener_position_);
+ }
+}
+
+const AudioBuffer* ReflectionsNode::AudioProcess(const NodeInput& input) {
+
+ const AudioBuffer* input_buffer = input.GetSingleInput();
+ const size_t num_frames = system_settings_.GetFramesPerBuffer();
+ if (input_buffer == nullptr) {
+ // If we have no input, generate a silent input buffer until the node states
+ // are cleared.
+ if (num_frames_processed_on_empty_input_ <
+ reflections_processor_.num_frames_to_process_on_empty_input()) {
+ num_frames_processed_on_empty_input_ += num_frames;
+ input_buffer = &silence_mono_buffer_;
+ } else {
+ // Skip processing entirely when the states are fully cleared.
+ return nullptr;
+ }
+ } else {
+ num_frames_processed_on_empty_input_ = 0;
+ DCHECK_EQ(input_buffer->num_channels(), kNumMonoChannels);
+ }
+ output_buffer_.Clear();
+ reflections_processor_.Process(*input_buffer, &output_buffer_);
+
+ // Rotate the reflections with respect to listener's orientation.
+ const WorldRotation inverse_head_rotation =
+ system_settings_.GetHeadRotation().conjugate();
+ foa_rotator_.Process(inverse_head_rotation, output_buffer_, &output_buffer_);
+
+ // Copy buffer parameters.
+ return &output_buffer_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/reflections_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/reflections_node.h
new file mode 100644
index 000000000..8685759b9
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/reflections_node.h
@@ -0,0 +1,78 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_REFLECTIONS_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_REFLECTIONS_NODE_H_
+
+#include <vector>
+
+#include "ambisonics/foa_rotator.h"
+#include "api/resonance_audio_api.h"
+#include "base/audio_buffer.h"
+#include "base/misc_math.h"
+#include "dsp/reflections_processor.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+// Node that accepts a single mono buffer as input and outputs an ambisonically
+// encoded sound field buffer of the mix of all the early room reflections.
+class ReflectionsNode : public ProcessingNode {
+ public:
+ // Initializes |ReflectionsNode| class.
+ //
+ // @param system_settings Global system configuration.
+ explicit ReflectionsNode(const SystemSettings& system_settings);
+
+ // Updates the reflections. Depending on whether to use RT60s for reverb
+ // according to the global system settings, the reflections are calculated
+ // either by the current room properties or the proxy room properties.
+ void Update();
+
+ protected:
+ // Implements ProcessingNode.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ const SystemSettings& system_settings_;
+
+ // First-order-ambisonics rotator to be used to rotate the reflections with
+ // respect to the listener's orientation.
+ FoaRotator foa_rotator_;
+
+ // Processes and encodes reflections into an ambisonic buffer.
+ ReflectionsProcessor reflections_processor_;
+
+ // Most recently updated reflection properties.
+ ReflectionProperties reflection_properties_;
+
+ // Most recently updated listener position.
+ WorldPosition listener_position_;
+
+ size_t num_frames_processed_on_empty_input_;
+
+ // Ambisonic output buffer.
+ AudioBuffer output_buffer_;
+
+ // Silence mono buffer to render reflection tails during the absence of input
+ // buffers.
+ AudioBuffer silence_mono_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_REFLECTIONS_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.cc
new file mode 100644
index 000000000..f8cc6731b
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.cc
@@ -0,0 +1,586 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/resonance_audio_api_impl.h"
+
+#include <algorithm>
+#include <numeric>
+
+#include "ambisonics/utils.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/misc_math.h"
+#include "base/source_parameters.h"
+
+#include "base/unique_ptr_wrapper.h"
+#include "config/source_config.h"
+#include "dsp/channel_converter.h"
+#include "dsp/distance_attenuation.h"
+#include "graph/source_parameters_manager.h"
+#include "utils/planar_interleaved_conversion.h"
+#include "utils/sample_type_conversion.h"
+
+namespace vraudio {
+
+namespace {
+
+// Support 50 setter calls for 512 sources.
+const size_t kMaxNumTasksOnTaskQueue = 50 * 512;
+
+// User warning/notification messages.
+static const char* kBadInputPointerMessage = "Ignoring nullptr buffer";
+static const char* kBufferSizeMustMatchNumFramesMessage =
+ "Number of frames must match the frames per buffer specified during "
+ "construction - ignoring buffer";
+
+// Helper method to fetch |SourceGraphConfig| from |RenderingMode|.
+SourceGraphConfig GetSourceGraphConfigFromRenderingMode(
+ RenderingMode rendering_mode) {
+ switch (rendering_mode) {
+ case RenderingMode::kStereoPanning:
+ return StereoPanningConfig();
+ case RenderingMode::kBinauralLowQuality:
+ return BinauralLowQualityConfig();
+ case RenderingMode::kBinauralMediumQuality:
+ return BinauralMediumQualityConfig();
+ case RenderingMode::kBinauralHighQuality:
+ return BinauralHighQualityConfig();
+ case RenderingMode::kRoomEffectsOnly:
+ return RoomEffectsOnlyConfig();
+ default:
+ LOG(FATAL) << "Unknown rendering mode";
+ break;
+ }
+ return BinauralHighQualityConfig();
+}
+
+} // namespace
+
+ResonanceAudioApiImpl::ResonanceAudioApiImpl(size_t num_channels,
+ size_t frames_per_buffer,
+ int sample_rate_hz)
+ : system_settings_(num_channels, frames_per_buffer, sample_rate_hz),
+ task_queue_(kMaxNumTasksOnTaskQueue),
+ source_id_counter_(0) {
+ if (num_channels != kNumStereoChannels) {
+ LOG(FATAL) << "Only stereo output is supported";
+ return;
+ }
+
+ if (frames_per_buffer > kMaxSupportedNumFrames) {
+ LOG(FATAL) << "Only frame lengths up to " << kMaxSupportedNumFrames
+ << " are supported.";
+ return;
+ }
+
+ // The pffft library requires a minimum buffer size of 32 samples.
+ if (frames_per_buffer < FftManager::kMinFftSize) {
+ LOG(FATAL) << "The minimum number of frames per buffer is "
+ << FftManager::kMinFftSize << " samples";
+ return;
+ }
+ graph_manager_.reset(new GraphManager(system_settings_));
+}
+
+ResonanceAudioApiImpl::~ResonanceAudioApiImpl() {
+ // Clear task queue before shutting down.
+ task_queue_.Execute();
+}
+
+bool ResonanceAudioApiImpl::FillInterleavedOutputBuffer(size_t num_channels,
+ size_t num_frames,
+ float* buffer_ptr) {
+ DCHECK(buffer_ptr);
+ return FillOutputBuffer<float*>(num_channels, num_frames, buffer_ptr);
+}
+
+bool ResonanceAudioApiImpl::FillInterleavedOutputBuffer(size_t num_channels,
+ size_t num_frames,
+ int16* buffer_ptr) {
+ DCHECK(buffer_ptr);
+ return FillOutputBuffer<int16*>(num_channels, num_frames, buffer_ptr);
+}
+
+bool ResonanceAudioApiImpl::FillPlanarOutputBuffer(size_t num_channels,
+ size_t num_frames,
+ float* const* buffer_ptr) {
+ DCHECK(buffer_ptr);
+ return FillOutputBuffer<float* const*>(num_channels, num_frames, buffer_ptr);
+}
+
+bool ResonanceAudioApiImpl::FillPlanarOutputBuffer(size_t num_channels,
+ size_t num_frames,
+ int16* const* buffer_ptr) {
+ DCHECK(buffer_ptr);
+ return FillOutputBuffer<int16* const*>(num_channels, num_frames, buffer_ptr);
+}
+
+void ResonanceAudioApiImpl::SetHeadPosition(float x, float y, float z) {
+ auto task = [this, x, y, z]() {
+ const WorldPosition head_position(x, y, z);
+ system_settings_.SetHeadPosition(head_position);
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetHeadRotation(float x, float y, float z,
+ float w) {
+ auto task = [this, w, x, y, z]() {
+ const WorldRotation head_rotation(w, x, y, z);
+ system_settings_.SetHeadRotation(head_rotation);
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetMasterVolume(float volume) {
+ auto task = [this, volume]() { system_settings_.SetMasterGain(volume); };
+ task_queue_.Post(task);
+}
+
+int ResonanceAudioApiImpl::CreateAmbisonicSource(size_t num_channels) {
+ if (num_channels < kNumFirstOrderAmbisonicChannels ||
+ !IsValidAmbisonicOrder(num_channels)) {
+ // Invalid number of input channels, don't create the ambisonic source.
+ LOG(ERROR) << "Invalid number of channels for the ambisonic source: "
+ << num_channels;
+ return kInvalidSourceId;
+ }
+
+ const int ambisonic_source_id = source_id_counter_.fetch_add(1);
+
+ const size_t num_valid_channels =
+ std::min(num_channels, graph_manager_->GetNumMaxAmbisonicChannels());
+ if (num_valid_channels < num_channels) {
+ LOG(WARNING) << "Number of ambisonic channels will be diminished to "
+ << num_valid_channels;
+ }
+
+ auto task = [this, ambisonic_source_id, num_valid_channels]() {
+ graph_manager_->CreateAmbisonicSource(ambisonic_source_id,
+ num_valid_channels);
+ system_settings_.GetSourceParametersManager()->Register(
+ ambisonic_source_id);
+ // Overwrite default source parameters for ambisonic source.
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ ambisonic_source_id);
+ source_parameters->room_effects_gain = 0.0f;
+ source_parameters->distance_rolloff_model = DistanceRolloffModel::kNone;
+ source_parameters->distance_attenuation = 1.0f;
+ };
+ task_queue_.Post(task);
+ return ambisonic_source_id;
+}
+
+int ResonanceAudioApiImpl::CreateStereoSource(size_t num_channels) {
+ if (num_channels > kNumStereoChannels) {
+ LOG(ERROR) << "Unsupported number of input channels";
+ return kInvalidSourceId;
+ }
+ const int stereo_source_id = source_id_counter_.fetch_add(1);
+
+ auto task = [this, stereo_source_id]() {
+ graph_manager_->CreateStereoSource(stereo_source_id);
+ system_settings_.GetSourceParametersManager()->Register(stereo_source_id);
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ stereo_source_id);
+ source_parameters->enable_hrtf = false;
+ };
+ task_queue_.Post(task);
+ return stereo_source_id;
+}
+
+int ResonanceAudioApiImpl::CreateSoundObjectSource(
+ RenderingMode rendering_mode) {
+ const int sound_object_source_id = source_id_counter_.fetch_add(1);
+
+ const auto config = GetSourceGraphConfigFromRenderingMode(rendering_mode);
+ auto task = [this, sound_object_source_id, config]() {
+ graph_manager_->CreateSoundObjectSource(
+ sound_object_source_id, config.ambisonic_order, config.enable_hrtf,
+ config.enable_direct_rendering);
+ system_settings_.GetSourceParametersManager()->Register(
+ sound_object_source_id);
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ sound_object_source_id);
+ source_parameters->enable_hrtf = config.enable_hrtf;
+ };
+ task_queue_.Post(task);
+ return sound_object_source_id;
+}
+
+void ResonanceAudioApiImpl::DestroySource(SourceId source_id) {
+ auto task = [this, source_id]() {
+ graph_manager_->DestroySource(source_id);
+ system_settings_.GetSourceParametersManager()->Unregister(source_id);
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetInterleavedBuffer(SourceId source_id,
+ const float* audio_buffer_ptr,
+ size_t num_channels,
+ size_t num_frames) {
+ SetSourceBuffer<const float*>(source_id, audio_buffer_ptr, num_channels,
+ num_frames);
+}
+
+void ResonanceAudioApiImpl::SetInterleavedBuffer(SourceId source_id,
+ const int16* audio_buffer_ptr,
+ size_t num_channels,
+ size_t num_frames) {
+ SetSourceBuffer<const int16*>(source_id, audio_buffer_ptr, num_channels,
+ num_frames);
+}
+
+void ResonanceAudioApiImpl::SetPlanarBuffer(
+ SourceId source_id, const float* const* audio_buffer_ptr,
+ size_t num_channels, size_t num_frames) {
+ SetSourceBuffer<const float* const*>(source_id, audio_buffer_ptr,
+ num_channels, num_frames);
+}
+
+void ResonanceAudioApiImpl::SetPlanarBuffer(
+ SourceId source_id, const int16* const* audio_buffer_ptr,
+ size_t num_channels, size_t num_frames) {
+ SetSourceBuffer<const int16* const*>(source_id, audio_buffer_ptr,
+ num_channels, num_frames);
+}
+
+void ResonanceAudioApiImpl::SetSourceDistanceAttenuation(
+ SourceId source_id, float distance_attenuation) {
+ auto task = [this, source_id, distance_attenuation]() {
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ source_id);
+ if (source_parameters != nullptr) {
+ const auto& rolloff_model = source_parameters->distance_rolloff_model;
+ DCHECK_EQ(rolloff_model, DistanceRolloffModel::kNone);
+ if (rolloff_model != DistanceRolloffModel::kNone) {
+ LOG(WARNING) << "Implicit distance rolloff model is set. The value "
+ "will be overwritten.";
+ }
+ source_parameters->distance_attenuation = distance_attenuation;
+ }
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetSourceDistanceModel(SourceId source_id,
+ DistanceRolloffModel rolloff,
+ float min_distance,
+ float max_distance) {
+ if (max_distance < min_distance && rolloff != DistanceRolloffModel::kNone) {
+ LOG(WARNING) << "max_distance must be larger than min_distance";
+ return;
+ }
+ auto task = [this, source_id, rolloff, min_distance, max_distance]() {
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ source_id);
+ if (source_parameters != nullptr) {
+ source_parameters->distance_rolloff_model = rolloff;
+ source_parameters->minimum_distance = min_distance;
+ source_parameters->maximum_distance = max_distance;
+ }
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetSourcePosition(SourceId source_id, float x,
+ float y, float z) {
+ const WorldPosition position(x, y, z);
+ auto task = [this, source_id, position]() {
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ source_id);
+ if (source_parameters != nullptr) {
+ source_parameters->object_transform.position = position;
+ }
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetSourceRoomEffectsGain(SourceId source_id,
+ float room_effects_gain) {
+ auto task = [this, source_id, room_effects_gain]() {
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ source_id);
+ if (source_parameters != nullptr) {
+ source_parameters->room_effects_gain = room_effects_gain;
+ }
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetSourceRotation(SourceId source_id, float x,
+ float y, float z, float w) {
+ const WorldRotation rotation(w, x, y, z);
+ auto task = [this, source_id, rotation]() {
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ source_id);
+ if (source_parameters != nullptr) {
+ source_parameters->object_transform.rotation = rotation;
+ }
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetSourceVolume(SourceId source_id, float volume) {
+ auto task = [this, source_id, volume]() {
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ source_id);
+ if (source_parameters != nullptr) {
+ source_parameters->gain = volume;
+ }
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetSoundObjectDirectivity(
+ SourceId sound_object_source_id, float alpha, float order) {
+ auto task = [this, sound_object_source_id, alpha, order]() {
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ sound_object_source_id);
+ if (source_parameters != nullptr) {
+ source_parameters->directivity_alpha = alpha;
+ source_parameters->directivity_order = order;
+ }
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetSoundObjectListenerDirectivity(
+ SourceId sound_object_source_id, float alpha, float order) {
+ auto task = [this, sound_object_source_id, alpha, order]() {
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ sound_object_source_id);
+ if (source_parameters != nullptr) {
+ source_parameters->listener_directivity_alpha = alpha;
+ source_parameters->listener_directivity_order = order;
+ }
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetSoundObjectNearFieldEffectGain(
+ SourceId sound_object_source_id, float gain) {
+ auto task = [this, sound_object_source_id, gain]() {
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ sound_object_source_id);
+ if (source_parameters != nullptr) {
+ source_parameters->near_field_gain = gain;
+ }
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetSoundObjectOcclusionIntensity(
+ SourceId sound_object_source_id, float intensity) {
+ auto task = [this, sound_object_source_id, intensity]() {
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ sound_object_source_id);
+ if (source_parameters != nullptr) {
+ source_parameters->occlusion_intensity = intensity;
+ }
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetSoundObjectSpread(
+ SourceId sound_object_source_id, float spread_deg) {
+ auto task = [this, sound_object_source_id, spread_deg]() {
+ auto source_parameters =
+ system_settings_.GetSourceParametersManager()->GetMutableParameters(
+ sound_object_source_id);
+ if (source_parameters != nullptr) {
+ source_parameters->spread_deg = spread_deg;
+ }
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::EnableRoomEffects(bool enable) {
+ auto task = [this, enable]() { graph_manager_->EnableRoomEffects(enable); };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetReflectionProperties(
+ const ReflectionProperties& reflection_properties) {
+ auto task = [this, reflection_properties]() {
+ system_settings_.SetReflectionProperties(reflection_properties);
+ };
+ task_queue_.Post(task);
+}
+
+void ResonanceAudioApiImpl::SetReverbProperties(
+ const ReverbProperties& reverb_properties) {
+ auto task = [this, reverb_properties]() {
+ system_settings_.SetReverbProperties(reverb_properties);
+ };
+ task_queue_.Post(task);
+}
+
+const AudioBuffer* ResonanceAudioApiImpl::GetAmbisonicOutputBuffer() const {
+ return graph_manager_->GetAmbisonicBuffer();
+}
+
+const AudioBuffer* ResonanceAudioApiImpl::GetStereoOutputBuffer() const {
+ return graph_manager_->GetStereoBuffer();
+}
+
+const AudioBuffer *ResonanceAudioApiImpl::GetReverbBuffer() const {
+ return graph_manager_->GetReverbBuffer();
+}
+
+void ResonanceAudioApiImpl::ProcessNextBuffer() {
+#if defined(ENABLE_TRACING) && !ION_PRODUCTION
+ // This enables tracing on the audio thread.
+ auto task = []() { ENABLE_TRACING_ON_CURRENT_THREAD("AudioThread"); };
+ task_queue_.Post(task);
+#endif // defined(ENABLE_TRACING) && !ION_PRODUCTION
+
+
+ task_queue_.Execute();
+
+ // Update room effects only if the pipeline is initialized.
+ if (graph_manager_->GetRoomEffectsEnabled()) {
+ graph_manager_->UpdateRoomReflections();
+ graph_manager_->UpdateRoomReverb();
+ }
+ // Update source attenuation parameters.
+ const auto process = [this](SourceParameters* parameters) {
+ const float master_gain = system_settings_.GetMasterGain();
+ const auto& listener_position = system_settings_.GetHeadPosition();
+ const auto& reflection_properties =
+ system_settings_.GetReflectionProperties();
+ const auto& reverb_properties = system_settings_.GetReverbProperties();
+ UpdateAttenuationParameters(master_gain, reflection_properties.gain,
+ reverb_properties.gain, listener_position,
+ parameters);
+ };
+ system_settings_.GetSourceParametersManager()->ProcessAllParameters(process);
+
+ graph_manager_->Process();
+}
+
+void ResonanceAudioApiImpl::SetStereoSpeakerMode(bool enabled) {
+ auto task = [this, enabled]() {
+ system_settings_.SetStereoSpeakerMode(enabled);
+ };
+ task_queue_.Post(task);
+}
+
+template <typename OutputType>
+bool ResonanceAudioApiImpl::FillOutputBuffer(size_t num_channels,
+ size_t num_frames,
+ OutputType buffer_ptr) {
+
+
+ if (buffer_ptr == nullptr) {
+ LOG(WARNING) << kBadInputPointerMessage;
+ return false;
+ }
+ if (num_channels != kNumStereoChannels) {
+ LOG(WARNING) << "Output buffer must be stereo";
+ return false;
+ }
+ const size_t num_input_samples = num_frames * num_channels;
+ const size_t num_expected_output_samples =
+ system_settings_.GetFramesPerBuffer() * system_settings_.GetNumChannels();
+ if (num_input_samples != num_expected_output_samples) {
+ LOG(WARNING) << "Output buffer size must be " << num_expected_output_samples
+ << " samples";
+ return false;
+ }
+
+ // Get the processed output buffer.
+ ProcessNextBuffer();
+ const AudioBuffer* output_buffer = GetStereoOutputBuffer();
+ if (output_buffer == nullptr) {
+ // This indicates that the graph processing is triggered without having any
+ // connected sources.
+ return false;
+ }
+
+ FillExternalBuffer(*output_buffer, buffer_ptr, num_frames, num_channels);
+ return true;
+}
+
+template <typename SampleType>
+void ResonanceAudioApiImpl::SetSourceBuffer(SourceId source_id,
+ SampleType audio_buffer_ptr,
+ size_t num_input_channels,
+ size_t num_frames) {
+ // Execute task queue to ensure newly created sound sources are initialized.
+ task_queue_.Execute();
+
+ if (audio_buffer_ptr == nullptr) {
+ LOG(WARNING) << kBadInputPointerMessage;
+ return;
+ }
+ if (num_frames != system_settings_.GetFramesPerBuffer()) {
+ LOG(WARNING) << kBufferSizeMustMatchNumFramesMessage;
+ return;
+ }
+
+ AudioBuffer* const output_buffer =
+ graph_manager_->GetMutableAudioBuffer(source_id);
+ if (output_buffer == nullptr) {
+ LOG(WARNING) << "Source audio buffer not found";
+ return;
+ }
+ const size_t num_output_channels = output_buffer->num_channels();
+
+ if (num_input_channels == num_output_channels) {
+ FillAudioBuffer(audio_buffer_ptr, num_frames, num_input_channels,
+ output_buffer);
+
+ return;
+ }
+
+ if ((num_input_channels == kNumMonoChannels) &&
+ (num_output_channels == kNumStereoChannels)) {
+ FillAudioBufferWithChannelRemapping(
+ audio_buffer_ptr, num_frames, num_input_channels,
+ {0, 0} /* channel_map */, output_buffer);
+ return;
+ }
+
+ if (num_input_channels > num_output_channels) {
+ std::vector<size_t> channel_map(num_output_channels);
+ // Fill channel map with increasing indices.
+ std::iota(std::begin(channel_map), std::end(channel_map), 0);
+ FillAudioBufferWithChannelRemapping(audio_buffer_ptr, num_frames,
+ num_input_channels, channel_map,
+ output_buffer);
+ return;
+ }
+
+ LOG(WARNING) << "Number of input channels does not match the number of "
+ "output channels";
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.h b/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.h
new file mode 100644
index 000000000..0adbc1c67
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/resonance_audio_api_impl.h
@@ -0,0 +1,175 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_RESONANCE_AUDIO_API_IMPL_H_
+#define RESONANCE_AUDIO_GRAPH_RESONANCE_AUDIO_API_IMPL_H_
+
+#include <atomic>
+#include <memory>
+#include <vector>
+
+#include "base/integral_types.h"
+#include "api/resonance_audio_api.h"
+#include "base/audio_buffer.h"
+#include "graph/graph_manager.h"
+#include "graph/system_settings.h"
+#include "utils/lockless_task_queue.h"
+
+namespace vraudio {
+
+// Implementation of ResonanceAudioApi interface.
+class ResonanceAudioApiImpl : public ResonanceAudioApi {
+ public:
+ // Constructor that initializes |ResonanceAudioApi| with system configuration.
+ //
+ // @param num_channels Number of channels of audio output.
+ // @param frames_per_buffer Number of frames per buffer.
+ // @param sample_rate_hz System sample rate.
+ ResonanceAudioApiImpl(size_t num_channels, size_t frames_per_buffer,
+ int sample_rate_hz);
+
+ ~ResonanceAudioApiImpl() override;
+
+ //////////////////////////////////
+ // ResonanceAudioApi implementation.
+ //////////////////////////////////
+
+ // Obtain processed output buffers.
+ bool FillInterleavedOutputBuffer(size_t num_channels, size_t num_frames,
+ float* buffer_ptr) override;
+ bool FillInterleavedOutputBuffer(size_t num_channels, size_t num_frames,
+ int16* buffer_ptr) override;
+ bool FillPlanarOutputBuffer(size_t num_channels, size_t num_frames,
+ float* const* buffer_ptr) override;
+ bool FillPlanarOutputBuffer(size_t num_channels, size_t num_frames,
+ int16* const* buffer_ptr) override;
+
+ // Listener configuration.
+ void SetHeadPosition(float x, float y, float z) override;
+ void SetHeadRotation(float x, float y, float z, float w) override;
+ void SetMasterVolume(float volume) override;
+ void SetStereoSpeakerMode(bool enabled) override;
+
+ // Create and destroy sources.
+ SourceId CreateAmbisonicSource(size_t num_channels) override;
+ SourceId CreateStereoSource(size_t num_channels) override;
+ SourceId CreateSoundObjectSource(RenderingMode rendering_mode) override;
+ void DestroySource(SourceId source_id) override;
+
+ // Set source data.
+ void SetInterleavedBuffer(SourceId source_id, const float* audio_buffer_ptr,
+ size_t num_channels, size_t num_frames) override;
+ void SetInterleavedBuffer(SourceId source_id, const int16* audio_buffer_ptr,
+ size_t num_channels, size_t num_frames) override;
+ void SetPlanarBuffer(SourceId source_id, const float* const* audio_buffer_ptr,
+ size_t num_channels, size_t num_frames) override;
+ void SetPlanarBuffer(SourceId source_id, const int16* const* audio_buffer_ptr,
+ size_t num_channels, size_t num_frames) override;
+
+ // Source configuration.
+ void SetSourceDistanceAttenuation(SourceId source_id,
+ float distance_attenuation) override;
+ void SetSourceDistanceModel(SourceId source_id, DistanceRolloffModel rolloff,
+ float min_distance, float max_distance) override;
+ void SetSourcePosition(SourceId source_id, float x, float y,
+ float z) override;
+ void SetSourceRoomEffectsGain(SourceId source_id,
+ float room_effects_gain) override;
+ void SetSourceRotation(SourceId source_id, float x, float y, float z,
+ float w) override;
+ void SetSourceVolume(SourceId source_id, float volume) override;
+
+ // Sound object configuration.
+ void SetSoundObjectDirectivity(SourceId sound_object_source_id, float alpha,
+ float order) override;
+ void SetSoundObjectListenerDirectivity(SourceId sound_object_source_id,
+ float alpha, float order) override;
+ void SetSoundObjectNearFieldEffectGain(SourceId sound_object_source_id,
+ float gain) override;
+ void SetSoundObjectOcclusionIntensity(SourceId sound_object_source_id,
+ float intensity) override;
+ void SetSoundObjectSpread(SourceId sound_object_source_id,
+ float spread_deg) override;
+
+ // Room effects configuration.
+ void EnableRoomEffects(bool enable) override;
+ void SetReflectionProperties(
+ const ReflectionProperties& reflection_properties) override;
+ void SetReverbProperties(const ReverbProperties& reverb_properties) override;
+
+ //////////////////////////////////
+ // Internal API methods.
+ //////////////////////////////////
+
+ // Returns the last processed output buffer of the ambisonic mix.
+ //
+ // @return Pointer to ambisonic output buffer.
+ const AudioBuffer* GetAmbisonicOutputBuffer() const;
+
+ // Returns the last processed output buffer of the stereo (binaural) mix.
+ //
+ // @return Pointer to stereo output buffer.
+ const AudioBuffer* GetStereoOutputBuffer() const;
+
+ // Returns the last processed buffer containing stereo data for the room reverb
+ //
+ // @return Pointer to room reverb stereo buffer.
+ const AudioBuffer* GetReverbBuffer() const;
+
+ // Triggers processing of the audio graph with the updated system properties.
+ void ProcessNextBuffer();
+
+ private:
+ // This method triggers the processing of the audio graph and outputs a
+ // binaural stereo output buffer.
+ //
+ // @tparam OutputType Output sample format, only float and int16 are
+ // supported.
+ // @param num_channels Number of channels in output buffer.
+ // @param num_frames Size of buffer in frames.
+ // @param buffer_ptr Raw pointer to audio buffer.
+ // @return True if a valid output was successfully rendered, false otherwise.
+ template <typename OutputType>
+ bool FillOutputBuffer(size_t num_channels, size_t num_frames,
+ OutputType buffer_ptr);
+
+ // Sets the next audio buffer to a sound source.
+ //
+ // @param source_id Id of sound source.
+ // @param audio_buffer_ptr Pointer to planar or interleaved audio buffer.
+ // @param num_input_channels Number of input channels.
+ // @param num_frames Number of frames per channel / audio buffer.
+ template <typename SampleType>
+ void SetSourceBuffer(SourceId source_id, SampleType audio_buffer_ptr,
+ size_t num_input_channels, size_t num_frames);
+
+ // Graph manager used to create and destroy sound objects.
+ std::unique_ptr<GraphManager> graph_manager_;
+
+ // Manages system wide settings.
+ SystemSettings system_settings_;
+
+ // Task queue to cache manipulation of all the entities in the system. All
+ // tasks are executed from the audio thread.
+ LocklessTaskQueue task_queue_;
+
+ // Incremental source id counter.
+ std::atomic<int> source_id_counter_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_RESONANCE_AUDIO_API_IMPL_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.cc
new file mode 100644
index 000000000..6a9999881
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.cc
@@ -0,0 +1,162 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/reverb_node.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+
+
+namespace vraudio {
+
+namespace {
+
+// Default time in seconds to update the rt60s over.
+const float kUpdateTimeSeconds = 1.0f;
+
+// Interpolates between the current and target values in steps of |update_step|,
+// will set |current| to |target| when the diff between them is less than
+// |update_step|.
+inline void InterpolateFloatParam(float update_step, float target,
+ float* current) {
+ if (std::abs(target - *current) <= std::abs(update_step)) {
+ *current = target;
+ } else {
+ *current += update_step;
+ }
+}
+
+} // namespace
+
+ReverbNode::ReverbNode(const SystemSettings& system_settings,
+ FftManager* fft_manager)
+ : system_settings_(system_settings),
+ rt60_band_update_steps_(kNumReverbOctaveBands, 0.0f),
+ gain_update_step_(0.0f),
+ rt60_updating_(false),
+ gain_updating_(false),
+ buffers_to_update_(
+ static_cast<float>(system_settings_.GetSampleRateHz()) *
+ kUpdateTimeSeconds /
+ static_cast<float>(system_settings_.GetFramesPerBuffer())),
+ spectral_reverb_(system_settings_.GetSampleRateHz(),
+ system_settings_.GetFramesPerBuffer()),
+ onset_compensator_(system_settings_.GetSampleRateHz(),
+ system_settings_.GetFramesPerBuffer(), fft_manager),
+ num_frames_processed_on_empty_input_(0),
+ reverb_length_frames_(0),
+ output_buffer_(kNumStereoChannels, system_settings_.GetFramesPerBuffer()),
+ compensator_output_buffer_(kNumStereoChannels,
+ system_settings_.GetFramesPerBuffer()),
+ silence_mono_buffer_(kNumMonoChannels,
+ system_settings_.GetFramesPerBuffer()) {
+ EnableProcessOnEmptyInput(true);
+ output_buffer_.Clear();
+ silence_mono_buffer_.Clear();
+ Update();
+}
+
+void ReverbNode::Update() {
+ new_reverb_properties_ = system_settings_.GetReverbProperties();
+
+ rt60_updating_ = !EqualSafe(std::begin(reverb_properties_.rt60_values),
+ std::end(reverb_properties_.rt60_values),
+ std::begin(new_reverb_properties_.rt60_values),
+ std::end(new_reverb_properties_.rt60_values));
+ if (rt60_updating_) {
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ rt60_band_update_steps_[i] = (new_reverb_properties_.rt60_values[i] -
+ reverb_properties_.rt60_values[i]) /
+ buffers_to_update_;
+ }
+ }
+ // Update the reverb gain if necessary.
+ gain_updating_ = reverb_properties_.gain != new_reverb_properties_.gain;
+ if (gain_updating_) {
+ gain_update_step_ =
+ (new_reverb_properties_.gain - reverb_properties_.gain) /
+ buffers_to_update_;
+ }
+}
+
+const AudioBuffer* ReverbNode::GetOutputBuffer() const
+{
+ return &output_buffer_;
+}
+
+const AudioBuffer* ReverbNode::AudioProcess(const NodeInput& input) {
+ if (rt60_updating_) {
+ for (size_t i = 0; i < kNumReverbOctaveBands; ++i) {
+ InterpolateFloatParam(rt60_band_update_steps_[i],
+ new_reverb_properties_.rt60_values[i],
+ &reverb_properties_.rt60_values[i]);
+ }
+ spectral_reverb_.SetRt60PerOctaveBand(reverb_properties_.rt60_values);
+ const auto max_rt_it =
+ std::max_element(std::begin(reverb_properties_.rt60_values),
+ std::end(reverb_properties_.rt60_values));
+ reverb_length_frames_ = static_cast<size_t>(
+ *max_rt_it * static_cast<float>(system_settings_.GetSampleRateHz()));
+ onset_compensator_.Update(reverb_properties_.rt60_values,
+ reverb_properties_.gain);
+ // |InterpolateFloatParam| will set the two values below to be equal on
+ // completion of interpolation.
+ rt60_updating_ = !EqualSafe(std::begin(reverb_properties_.rt60_values),
+ std::end(reverb_properties_.rt60_values),
+ std::begin(new_reverb_properties_.rt60_values),
+ std::end(new_reverb_properties_.rt60_values));
+ }
+
+ if (gain_updating_) {
+ InterpolateFloatParam(gain_update_step_, new_reverb_properties_.gain,
+ &reverb_properties_.gain);
+ spectral_reverb_.SetGain(reverb_properties_.gain);
+ onset_compensator_.Update(reverb_properties_.rt60_values,
+ reverb_properties_.gain);
+ // |InterpolateFloatParam| will set the two values below to be equal on
+ // completion of interpolation.
+ gain_updating_ = reverb_properties_.gain != new_reverb_properties_.gain;
+ }
+
+ const AudioBuffer* input_buffer = input.GetSingleInput();
+ if (input_buffer == nullptr) {
+ // If we have no input, generate a silent input buffer until the node states
+ // are cleared.
+ if (num_frames_processed_on_empty_input_ < reverb_length_frames_) {
+ const size_t num_frames = system_settings_.GetFramesPerBuffer();
+ num_frames_processed_on_empty_input_ += num_frames;
+ spectral_reverb_.Process(silence_mono_buffer_[0], &output_buffer_[0],
+ &output_buffer_[1]);
+ return &output_buffer_;
+ } else {
+ // Skip processing entirely when the states are fully cleared.
+ return nullptr;
+ }
+ }
+ DCHECK_EQ(input_buffer->num_channels(), kNumMonoChannels);
+ num_frames_processed_on_empty_input_ = 0;
+ spectral_reverb_.Process((*input_buffer)[0], &output_buffer_[0],
+ &output_buffer_[1]);
+ onset_compensator_.Process(*input_buffer, &compensator_output_buffer_);
+ output_buffer_[0] += compensator_output_buffer_[0];
+ output_buffer_[1] += compensator_output_buffer_[1];
+ return &output_buffer_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.h
new file mode 100644
index 000000000..f9921fa66
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/reverb_node.h
@@ -0,0 +1,99 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_REVERB_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_REVERB_NODE_H_
+
+#include "api/resonance_audio_api.h"
+#include "base/audio_buffer.h"
+#include "dsp/fft_manager.h"
+#include "dsp/reverb_onset_compensator.h"
+#include "dsp/spectral_reverb.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+// Implements a spectral reverb producing a decorrelated stereo output with
+// onset compensated by a pair of convolution filters.
+class ReverbNode : public ProcessingNode {
+ public:
+ // Constructs a |ReverbNode|.
+ //
+ // @param system_settings Global system configuration.
+ // @param fft_manager Pointer to a manager to perform FFT transformations.
+ ReverbNode(const SystemSettings& system_settings, FftManager* fft_manager);
+
+ // Updates the |SpectralReverb| using the current room properties or RT60
+ // values depending on the system settings.
+ void Update();
+
+ const AudioBuffer *GetOutputBuffer() const;
+
+ protected:
+ // Implements ProcessingNode.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ // Global system configuration.
+ const SystemSettings& system_settings_;
+
+ // Current reverb properties.
+ ReverbProperties reverb_properties_;
+
+ // New reverb properties.
+ ReverbProperties new_reverb_properties_;
+
+ // Per band reverb time update step sizes.
+ std::vector<float> rt60_band_update_steps_;
+
+ // Update step size for the gain parameter.
+ float gain_update_step_;
+
+ // Denotes whether the rt60s are currently being updated.
+ bool rt60_updating_;
+
+ // Denotes whether the gain is currently being updated.
+ bool gain_updating_;
+
+ // Number of buffers to updae rt60s over.
+ float buffers_to_update_;
+
+ // DSP class to perform filtering associated with the reverb.
+ SpectralReverb spectral_reverb_;
+
+ // DSP class to perform spectral reverb onset compensation.
+ ReverbOnsetCompensator onset_compensator_;
+
+ // Number of frames of zeroed out data to be processed by the node to ensure
+ // the entire tail is rendered after input has ceased.
+ size_t num_frames_processed_on_empty_input_;
+
+ // Longest current reverb time, across all bands, in frames.
+ size_t reverb_length_frames_;
+
+ // Output buffers for mixing spectral reverb and compensator output.
+ AudioBuffer output_buffer_;
+ AudioBuffer compensator_output_buffer_;
+
+ // Silence mono buffer to render reverb tails during the absence of input
+ // buffers.
+ AudioBuffer silence_mono_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_REVERB_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/source_graph_config.h b/src/3rdparty/resonance-audio/resonance_audio/graph/source_graph_config.h
new file mode 100644
index 000000000..513f9b087
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/source_graph_config.h
@@ -0,0 +1,43 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_SOURCE_GRAPH_CONFIG_H_
+#define RESONANCE_AUDIO_GRAPH_SOURCE_GRAPH_CONFIG_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace vraudio {
+
+// Configuration of a source and the nodes it is instantiating.
+struct SourceGraphConfig {
+ // Configuration name.
+ std::string configuration_name;
+
+ // Ambisonic order to encode to/decode from source.
+ int ambisonic_order = 1;
+
+ // Flag to enable HRTF-based rendering of source.
+ bool enable_hrtf = true;
+
+ // Flag to enable direct rendering of source.
+ bool enable_direct_rendering = true;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_SOURCE_GRAPH_CONFIG_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/source_parameters_manager.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/source_parameters_manager.cc
new file mode 100644
index 000000000..edc903cc5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/source_parameters_manager.cc
@@ -0,0 +1,58 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/source_parameters_manager.h"
+
+#include "base/logging.h"
+
+namespace vraudio {
+
+void SourceParametersManager::Register(SourceId source_id) {
+ DCHECK(parameters_.find(source_id) == parameters_.end());
+ parameters_[source_id] = SourceParameters();
+}
+
+void SourceParametersManager::Unregister(SourceId source_id) {
+ parameters_.erase(source_id);
+}
+
+const SourceParameters* SourceParametersManager::GetParameters(
+ SourceId source_id) const {
+ const auto source_parameters_itr = parameters_.find(source_id);
+ if (source_parameters_itr == parameters_.end()) {
+ LOG(ERROR) << "Source " << source_id << " not found";
+ return nullptr;
+ }
+ return &source_parameters_itr->second;
+}
+
+SourceParameters* SourceParametersManager::GetMutableParameters(
+ SourceId source_id) {
+ auto source_parameters_itr = parameters_.find(source_id);
+ if (source_parameters_itr == parameters_.end()) {
+ LOG(ERROR) << "Source " << source_id << " not found";
+ return nullptr;
+ }
+ return &source_parameters_itr->second;
+}
+
+void SourceParametersManager::ProcessAllParameters(const Process& process) {
+ for (auto& source_parameters_itr : parameters_) {
+ process(&source_parameters_itr.second);
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/source_parameters_manager.h b/src/3rdparty/resonance-audio/resonance_audio/graph/source_parameters_manager.h
new file mode 100644
index 000000000..a6b394fd7
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/source_parameters_manager.h
@@ -0,0 +1,68 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_SOURCE_PARAMETERS_MANAGER_H_
+#define RESONANCE_AUDIO_GRAPH_SOURCE_PARAMETERS_MANAGER_H_
+
+#include <functional>
+#include <unordered_map>
+
+#include "base/constants_and_types.h"
+#include "base/source_parameters.h"
+
+namespace vraudio {
+
+// Class that manages the corresponding parameters of each registered source.
+class SourceParametersManager {
+ public:
+ // Alias for the parameters process closure type.
+ using Process = std::function<void(SourceParameters*)>;
+
+ // Registers new source parameters for given |source_id|.
+ //
+ // @param source_id Source id.
+ void Register(SourceId source_id);
+
+ // Unregisters the source parameters for given |source_id|.
+ //
+ // @param source_id Source id.
+ void Unregister(SourceId source_id);
+
+ // Returns read-only source parameters for given |source_id|.
+ //
+ // @param source_id Source id.
+ // @return Read-only source parameters, nullptr if |source_id| not found.
+ const SourceParameters* GetParameters(SourceId source_id) const;
+
+ // Returns mutable source parameters for given |source_id|.
+ //
+ // @param source_id Source id.
+ // @return Mutable source parameters, nullptr if |source_id| not found.
+ SourceParameters* GetMutableParameters(SourceId source_id);
+
+ // Executes given |process| for the parameters of each registered source.
+ //
+ // @param process Parameters processing method.
+ void ProcessAllParameters(const Process& process);
+
+ private:
+ // Registered source parameters.
+ std::unordered_map<SourceId, SourceParameters> parameters_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_SOURCE_PARAMETERS_MANAGER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/source_parameters_manager_test.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/source_parameters_manager_test.cc
new file mode 100644
index 000000000..59c69eaed
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/source_parameters_manager_test.cc
@@ -0,0 +1,91 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/source_parameters_manager.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+
+namespace vraudio {
+
+namespace {
+
+// Tests that the manager registers/unregisters source parameters as expected
+// for given arbitrary source ids.
+TEST(SourceParametersManagerTest, RegisterUnregisterTest) {
+ const SourceId kSourceIds[] = {0, 1, 5, 10};
+
+ // Initialize a new |SourceParametersManager|.
+ SourceParametersManager source_parameters_manager;
+ for (const auto source_id : kSourceIds) {
+ // Verify that no parameters are registered for given |source_id|.
+ EXPECT_TRUE(source_parameters_manager.GetParameters(source_id) == nullptr);
+ // Verify that the parameters are initialized after |Register|.
+ source_parameters_manager.Register(source_id);
+ EXPECT_FALSE(source_parameters_manager.GetParameters(source_id) == nullptr);
+ // Verify that the parameters are destroyed after |Unregister|.
+ source_parameters_manager.Unregister(source_id);
+ EXPECT_TRUE(source_parameters_manager.GetParameters(source_id) == nullptr);
+ }
+}
+
+// Tests that the manager correctly applies and returns parameter values of a
+// source for a given arbitrary modifier.
+TEST(SourceParametersManagerTest, ParametersAccessTest) {
+ const SourceId kSourceId = 1;
+ const float kSourceGain = 0.25f;
+
+ // Initialize a new |SourceParametersManager| and register the source.
+ SourceParametersManager source_parameters_manager;
+ source_parameters_manager.Register(kSourceId);
+ // Modify the gain parameter.
+ auto mutable_parameters =
+ source_parameters_manager.GetMutableParameters(kSourceId);
+ EXPECT_TRUE(mutable_parameters != nullptr);
+ mutable_parameters->gain = kSourceGain;
+ // Access the parameters to verify the gain value was applied correctly.
+ const auto parameters = source_parameters_manager.GetParameters(kSourceId);
+ EXPECT_TRUE(parameters != nullptr);
+ EXPECT_EQ(kSourceGain, parameters->gain);
+}
+
+// Tests that the manager correctly executes a given arbitrary call to process
+// all parameters for all the sources contained within.
+TEST(SourceParametersManagerTest, ProcessAllParametersTest) {
+ const SourceId kSourceIds[] = {0, 1, 2, 3, 4, 5};
+ const float kDistanceAttenuation = 0.75f;
+ const auto kProcess = [kDistanceAttenuation](SourceParameters* parameters) {
+ parameters->distance_attenuation = kDistanceAttenuation;
+ };
+
+ // Initialize a new |SourceParametersManager| and register all the sources.
+ SourceParametersManager source_parameters_manager;
+ for (const auto source_id : kSourceIds) {
+ source_parameters_manager.Register(source_id);
+ }
+ // Process all parameters to apply the distance attenuation.
+ source_parameters_manager.ProcessAllParameters(kProcess);
+ // Verify that the distance attenuation value was applied correctly to all the
+ // sources.
+ for (const auto source_id : kSourceIds) {
+ const auto parameters = source_parameters_manager.GetParameters(source_id);
+ EXPECT_TRUE(parameters != nullptr);
+ EXPECT_EQ(kDistanceAttenuation, parameters->distance_attenuation);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/stereo_mixing_panner_node.cc b/src/3rdparty/resonance-audio/resonance_audio/graph/stereo_mixing_panner_node.cc
new file mode 100644
index 000000000..d919e94c1
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/stereo_mixing_panner_node.cc
@@ -0,0 +1,65 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "graph/stereo_mixing_panner_node.h"
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/spherical_angle.h"
+
+#include "dsp/stereo_panner.h"
+
+namespace vraudio {
+
+StereoMixingPannerNode::StereoMixingPannerNode(
+ const SystemSettings& system_settings)
+ : system_settings_(system_settings),
+ gain_mixer_(kNumStereoChannels, system_settings_.GetFramesPerBuffer()),
+ coefficients_(kNumStereoChannels) {}
+
+const AudioBuffer* StereoMixingPannerNode::AudioProcess(
+ const NodeInput& input) {
+
+
+ const WorldPosition& listener_position = system_settings_.GetHeadPosition();
+ const WorldRotation& listener_rotation = system_settings_.GetHeadRotation();
+
+ gain_mixer_.Reset();
+ for (auto& input_buffer : input.GetInputBuffers()) {
+ const int source_id = input_buffer->source_id();
+ const auto source_parameters =
+ system_settings_.GetSourceParameters(source_id);
+ DCHECK_NE(source_id, kInvalidSourceId);
+ DCHECK_EQ(input_buffer->num_channels(), 1U);
+
+ // Compute the relative source direction in spherical angles.
+ const ObjectTransform& source_transform =
+ source_parameters->object_transform;
+ WorldPosition relative_direction;
+ GetRelativeDirection(listener_position, listener_rotation,
+ source_transform.position, &relative_direction);
+ const SphericalAngle source_direction =
+ SphericalAngle::FromWorldPosition(relative_direction);
+
+
+ CalculateStereoPanGains(source_direction, &coefficients_);
+
+ gain_mixer_.AddInputChannel((*input_buffer)[0], source_id, coefficients_);
+ }
+ return gain_mixer_.GetOutput();
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/stereo_mixing_panner_node.h b/src/3rdparty/resonance-audio/resonance_audio/graph/stereo_mixing_panner_node.h
new file mode 100644
index 000000000..b465917bd
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/stereo_mixing_panner_node.h
@@ -0,0 +1,61 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_STEREO_MIXING_PANNER_NODE_H_
+#define RESONANCE_AUDIO_GRAPH_STEREO_MIXING_PANNER_NODE_H_
+
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "dsp/gain_mixer.h"
+#include "graph/system_settings.h"
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+// Node that accepts single mono sound object buffer as input and pans into a
+// stereo panorama.
+class StereoMixingPannerNode : public ProcessingNode {
+ public:
+ // Initializes StereoMixingPannerNode class.
+ //
+ // @param system_settings Global system configuration.
+ explicit StereoMixingPannerNode(const SystemSettings& system_settings);
+
+ // Node implementation.
+ bool CleanUp() final {
+ CallCleanUpOnInputNodes();
+ // Prevent node from being disconnected when all sources are removed.
+ return false;
+ }
+
+ protected:
+ // Implements ProcessingNode.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override;
+
+ private:
+ const SystemSettings& system_settings_;
+
+ // |GainMixer| instance.
+ GainMixer gain_mixer_;
+
+ // Panning coefficients to be applied the input.
+ std::vector<float> coefficients_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_STEREO_MIXING_PANNER_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/graph/system_settings.h b/src/3rdparty/resonance-audio/resonance_audio/graph/system_settings.h
new file mode 100644
index 000000000..48573be54
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/graph/system_settings.h
@@ -0,0 +1,189 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_GRAPH_SYSTEM_SETTINGS_H_
+#define RESONANCE_AUDIO_GRAPH_SYSTEM_SETTINGS_H_
+
+#include "api/resonance_audio_api.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+#include "graph/source_parameters_manager.h"
+
+namespace vraudio {
+
+// Contains system-wide settings and parameters. Note that this class is not
+// thread-safe. Updating system parameters must be avoided during the audio
+// graph processing.
+class SystemSettings {
+ public:
+ // Constructor initializes the system configuration.
+ //
+ // @param num_output_channels Number of output channels.
+ // @param frames_per_buffer Buffer size in frames.
+ // @param sample_rate_hz Sample rate.
+ SystemSettings(size_t num_output_channels, size_t frames_per_buffer,
+ int sample_rate_hz)
+ : sample_rate_hz_(sample_rate_hz),
+ frames_per_buffer_(frames_per_buffer),
+ num_channels_(num_output_channels),
+ head_rotation_(WorldRotation::Identity()),
+ head_position_(WorldPosition::Zero()),
+ master_gain_(1.0f),
+ stereo_speaker_mode_(false) {}
+
+ // Sets the listener head orientation.
+ //
+ // @param head_rotation Listener head orientation.
+ void SetHeadRotation(const WorldRotation& head_rotation) {
+ head_rotation_ = head_rotation;
+ }
+
+ // Sets the listener head position.
+ //
+ // @param head_position Listener head position.
+ void SetHeadPosition(const WorldPosition& head_position) {
+ head_position_ = head_position;
+ }
+
+ // Sets the global stereo speaker mode flag. This flag enforces stereo panning
+ // and disables HRTF-based binauralization. The stereo speaker mode is
+ // disabled by default.
+ //
+ // @param enabled Defines the stereo speaker mode state.
+ void SetStereoSpeakerMode(bool enabled) { stereo_speaker_mode_ = enabled; }
+
+ // Returns the source parameters manager.
+ //
+ // @return Mutable source parameters manager.
+ SourceParametersManager* GetSourceParametersManager() {
+ return &source_parameters_manager_;
+ }
+
+ // Returns the parameters of source with given |source_id|.
+ //
+ // @param source_id Source id.
+ // @return Pointer to source parameters, nullptr if |source_id| not found.
+ const SourceParameters* GetSourceParameters(SourceId source_id) const {
+ return source_parameters_manager_.GetParameters(source_id);
+ }
+
+ // Returns the sample rate.
+ //
+ // @return Sample rate in Hertz.
+ int GetSampleRateHz() const { return sample_rate_hz_; }
+
+ // Returns the frames per buffer.
+ //
+ // @return Buffer size in frames.
+ size_t GetFramesPerBuffer() const { return frames_per_buffer_; }
+
+ // Returns the number of output channels.
+ //
+ // @return Number of output channels.
+ size_t GetNumChannels() const { return num_channels_; }
+
+ // Returns the head rotation.
+ //
+ // @return Head orientation.
+ const WorldRotation& GetHeadRotation() const { return head_rotation_; }
+
+ // Returns the head position.
+ //
+ // @return Head position.
+ const WorldPosition& GetHeadPosition() const { return head_position_; }
+
+ // Returns the stereo speaker mode state.
+ //
+ // @return Current stereo speaker mode state.
+ bool IsStereoSpeakerModeEnabled() const { return stereo_speaker_mode_; }
+
+ // Sets the master gain.
+ //
+ // @param master_gain Master output gain.
+ void SetMasterGain(float master_gain) { master_gain_ = master_gain; }
+
+ // Sets current reflection properties.
+ //
+ // @param reflection_properties Reflection properties.
+ void SetReflectionProperties(
+ const ReflectionProperties& reflection_properties) {
+ reflection_properties_ = reflection_properties;
+ }
+
+ // Sets current reverb properties.
+ //
+ // @param reverb_properties Reflection properties.
+ void SetReverbProperties(const ReverbProperties& reverb_properties) {
+ reverb_properties_ = reverb_properties;
+ }
+
+ // Returns the master gain.
+ //
+ // @return Master output gain.
+ float GetMasterGain() const { return master_gain_; }
+
+ // Returns the current reflection properties of the environment.
+ //
+ // @return Current reflection properties.
+ const ReflectionProperties& GetReflectionProperties() const {
+ return reflection_properties_;
+ }
+
+ // Returns the current reverb properties of the environment.
+ //
+ // @return Current reverb properties.
+ const ReverbProperties& GetReverbProperties() const {
+ return reverb_properties_;
+ }
+
+ // Disable copy and assignment operator. Since |SystemSettings| serves as a
+ // global parameter storage, it should never be copied.
+ SystemSettings& operator=(const SystemSettings&) = delete;
+ SystemSettings(const SystemSettings&) = delete;
+
+ private:
+ // Sampling rate.
+ const int sample_rate_hz_;
+
+ // Frames per buffer.
+ const size_t frames_per_buffer_;
+
+ // Number of channels per buffer.
+ const size_t num_channels_;
+
+ // The most recently updated head rotation and position.
+ WorldRotation head_rotation_;
+ WorldPosition head_position_;
+
+ // Source parameters manager.
+ SourceParametersManager source_parameters_manager_;
+
+ // Master gain in amplitude.
+ float master_gain_;
+
+ // Current reflection properties of the environment.
+ ReflectionProperties reflection_properties_;
+
+ // Current reverb properties of the environment.
+ ReverbProperties reverb_properties_;
+
+ // Defines the state of the global speaker mode.
+ bool stereo_speaker_mode_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_GRAPH_SYSTEM_SETTINGS_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/audio_nodes_test.cc b/src/3rdparty/resonance-audio/resonance_audio/node/audio_nodes_test.cc
new file mode 100644
index 000000000..a623f5d1c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/audio_nodes_test.cc
@@ -0,0 +1,407 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include <algorithm>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/logging.h"
+#include "node/processing_node.h"
+#include "node/sink_node.h"
+#include "node/source_node.h"
+
+namespace vraudio {
+
+namespace {
+
+// Number of channels in test buffers.
+static const size_t kNumChannels = 5;
+
+// Number of frames in test buffers.
+static const size_t kNumFrames = 7;
+
+// Helper method to compare two audio buffers.
+bool CompareAudioBuffer(const AudioBuffer& buffer_a,
+ const AudioBuffer& buffer_b) {
+ if (buffer_a.num_channels() != buffer_b.num_channels() ||
+ buffer_a.num_frames() != buffer_b.num_frames()) {
+ return false;
+ }
+ for (size_t channel = 0; channel < buffer_a.num_channels(); ++channel) {
+ const AudioBuffer::Channel& channel_a = buffer_a[channel];
+ const AudioBuffer::Channel& channel_b = buffer_b[channel];
+ for (size_t frame = 0; frame < buffer_a.num_frames(); ++frame) {
+ if (channel_a[frame] != channel_b[frame]) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// Helper method to generate a test AudioBuffer.
+std::unique_ptr<AudioBuffer> GenerateTestAudioBuffer(float factor) {
+ std::unique_ptr<AudioBuffer> new_buffer(
+ new AudioBuffer(kNumChannels, kNumFrames));
+ for (size_t channel = 0; channel < kNumChannels; ++channel) {
+ std::fill((*new_buffer)[channel].begin(), (*new_buffer)[channel].end(),
+ static_cast<float>(channel) * factor);
+ }
+ return new_buffer;
+}
+
+// Simple audio source node that generates AudioData buffers.
+class MySourceNode : public SourceNode {
+ public:
+ MySourceNode(bool output_empty_buffer, bool* node_deletion_flag)
+ : output_empty_buffer_(output_empty_buffer),
+ node_deletion_flag_(node_deletion_flag),
+ audio_buffer_(GenerateTestAudioBuffer(1.0f)) {}
+ ~MySourceNode() final {
+ if (node_deletion_flag_ != nullptr) {
+ *node_deletion_flag_ = true;
+ }
+ }
+
+ protected:
+ // AudioProcess methods outputs kTestAudioData data.
+ const AudioBuffer* AudioProcess() override {
+ if (output_empty_buffer_) {
+ return nullptr;
+ }
+ return audio_buffer_.get();
+ }
+
+ private:
+ const bool output_empty_buffer_;
+ bool* node_deletion_flag_;
+
+ std::unique_ptr<AudioBuffer> audio_buffer_;
+};
+
+// Simple audio processing node that reads from a single input and passes the
+// data to the output.
+class MyProcessingNode : public ProcessingNode {
+ public:
+ MyProcessingNode(bool process_on_empty_input, bool* audio_process_called_flag,
+ bool* node_deletion_flag)
+ : process_on_empty_input_(process_on_empty_input),
+ audio_process_called_flag_(audio_process_called_flag),
+ node_deletion_flag_(node_deletion_flag) {
+ EnableProcessOnEmptyInput(process_on_empty_input_);
+ }
+
+ ~MyProcessingNode() final {
+ if (node_deletion_flag_ != nullptr) {
+ *node_deletion_flag_ = true;
+ }
+ }
+
+ protected:
+ const AudioBuffer* AudioProcess(const NodeInput& input) override {
+ if (audio_process_called_flag_ != nullptr) {
+ *audio_process_called_flag_ = true;
+ }
+
+ if (!process_on_empty_input_) {
+ EXPECT_GT(input.GetInputBuffers().size(), 0U);
+ } else {
+ if (input.GetInputBuffers().empty()) {
+ return nullptr;
+ }
+ }
+
+ return input.GetInputBuffers()[0];
+ }
+
+ private:
+ const bool process_on_empty_input_;
+ bool* const audio_process_called_flag_;
+ bool* const node_deletion_flag_;
+};
+
+// Simple audio mixer node that connects to multiple nodes and outputs the sum
+// of all inputs.
+class MyAudioMixerNode : public ProcessingNode {
+ public:
+ explicit MyAudioMixerNode(bool* node_deletion_flag)
+ : node_deletion_flag_(node_deletion_flag) {}
+ ~MyAudioMixerNode() final {
+ if (node_deletion_flag_ != nullptr) {
+ *node_deletion_flag_ = true;
+ }
+ }
+
+ protected:
+ // AudioProcess performs an element-wise sum from all inputs and outputs a new
+ // AudioData buffer.
+ const AudioBuffer* AudioProcess(const NodeInput& input) override {
+ const auto& input_buffers = input.GetInputBuffers();
+ output_data_ = *input_buffers[0];
+
+ // Iterate over all inputs and add its data to |output_data|.
+ for (size_t buffer = 1; buffer < input_buffers.size(); ++buffer) {
+ const AudioBuffer* accumulate_data = input_buffers[buffer];
+ for (size_t channel = 0; channel < accumulate_data->num_channels();
+ ++channel) {
+ const AudioBuffer::Channel& accumulate_channel =
+ (*accumulate_data)[channel];
+ AudioBuffer::Channel* output_channel = &output_data_[channel];
+ EXPECT_EQ(accumulate_channel.size(), output_channel->size());
+ for (size_t frame = 0; frame < accumulate_channel.size(); ++frame) {
+ (*output_channel)[frame] += accumulate_channel[frame];
+ }
+ }
+ }
+ return &output_data_;
+ }
+
+ private:
+ bool* node_deletion_flag_;
+ AudioBuffer output_data_;
+};
+
+// Simple audio sink node that expects a single input.
+class MySinkNode : public SinkNode {
+ public:
+ explicit MySinkNode(bool* node_deletion_flag)
+ : node_deletion_flag_(node_deletion_flag) {}
+
+ ~MySinkNode() final {
+ if (node_deletion_flag_ != nullptr) {
+ *node_deletion_flag_ = true;
+ }
+ }
+
+ private:
+ bool* node_deletion_flag_;
+};
+
+// Tests a chain of an |SinkNode|, |ProcessingNode| and
+// |SourceNode|.
+TEST(AudioNodesTest, SourceProcessingSinkConnectionTest) {
+ static const bool kOutputEmptyBuffer = false;
+ static const bool kEnableProcessOnEmptyInput = true;
+
+ auto source_node =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto processing_node = std::make_shared<MyProcessingNode>(
+ kEnableProcessOnEmptyInput, nullptr, nullptr);
+ auto sink_node = std::make_shared<MySinkNode>(nullptr);
+
+ // Create chain of nodes.
+ sink_node->Connect(processing_node);
+ processing_node->Connect(source_node);
+
+ // Test output data.
+ const auto& data = sink_node->ReadInputs();
+ EXPECT_GT(data.size(), 0U);
+ EXPECT_TRUE(CompareAudioBuffer(*data[0], *GenerateTestAudioBuffer(1.0f)));
+}
+
+// Tests a chain of an |SinkNode| and |AudioMixerNode| connected to two
+// |SourceNodes|.
+TEST(AudioNodesTest, MixerProcessingConnectionTest) {
+ static const bool kOutputEmptyBuffer = false;
+ auto source_node_a =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto source_node_b =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto mixer_node = std::make_shared<MyAudioMixerNode>(nullptr);
+ auto sink_node = std::make_shared<MySinkNode>(nullptr);
+
+ // Create chain of nodes.
+ sink_node->Connect(mixer_node);
+ mixer_node->Connect(source_node_a);
+ mixer_node->Connect(source_node_b);
+
+ // Test output data.
+ const auto& data = sink_node->ReadInputs();
+ EXPECT_GT(data.size(), 0U);
+ EXPECT_TRUE(CompareAudioBuffer(*data[0], *GenerateTestAudioBuffer(2.0f)));
+}
+
+// Tests if ProcessingNode::AudioProcess() calls are skipped in case of
+// empty input buffers.
+TEST(AudioNodesTest, SkipProcessingOnEmptyInputTest) {
+ static const bool kEnableProcessOnEmptyInput = true;
+
+ bool audio_process_called = false;
+
+ // Tests that ProcessingNode::AudioProcess() is called in case source
+ // nodes do generate output.
+ {
+ static const bool kOutputEmptyBuffer = false;
+ auto source_node_a =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto source_node_b =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto processing_node = std::make_shared<MyProcessingNode>(
+ kEnableProcessOnEmptyInput, &audio_process_called, nullptr);
+ auto sink_node = std::make_shared<MySinkNode>(nullptr);
+
+ // Create chain of nodes.
+ sink_node->Connect(processing_node);
+ processing_node->Connect(source_node_a);
+ processing_node->Connect(source_node_b);
+
+ EXPECT_FALSE(audio_process_called);
+ const auto& data = sink_node->ReadInputs();
+ EXPECT_TRUE(audio_process_called);
+ EXPECT_GT(data.size(), 0U);
+ }
+
+ audio_process_called = false;
+
+ // Tests that ProcessingNode::AudioProcess() is *not* called in case
+ // source nodes do *not* generate output.
+ {
+ static const bool kOutputEmptyBuffer = true;
+ auto source_node_a =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto source_node_b =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto processing_node = std::make_shared<MyProcessingNode>(
+ false, &audio_process_called, nullptr);
+ auto sink_node = std::make_shared<MySinkNode>(nullptr);
+
+ // Create chain of nodes.
+ sink_node->Connect(processing_node);
+ processing_node->Connect(source_node_a);
+ processing_node->Connect(source_node_b);
+
+ EXPECT_FALSE(audio_process_called);
+ const auto& data = sink_node->ReadInputs();
+ EXPECT_FALSE(audio_process_called);
+ EXPECT_EQ(data.size(), 0U);
+ }
+}
+
+// Tests a chain of an |SinkNode|, |ProcessingNode| and
+// |SourceNode| and runs the node clean-up procedure with sources being
+// *not* marked with end-of-stream.
+TEST(AudioNodesTest, NodeCleanUpWithoutMarkEndOfStreamCallTest) {
+ static const bool kEnableProcessOnEmptyInput = true;
+
+ bool source_node_deleted = false;
+ bool processing_node_deleted = false;
+ bool sink_node_deleted = false;
+
+ auto sink_node = std::make_shared<MySinkNode>(&sink_node_deleted);
+
+ {
+ // Create a source and processing node and connect it to sink node.
+ auto source_node = std::make_shared<MySourceNode>(
+ /*output_empty_buffer=*/false, &source_node_deleted);
+ auto processing_node = std::make_shared<MyProcessingNode>(
+ kEnableProcessOnEmptyInput, nullptr, &processing_node_deleted);
+
+ // Connect nodes.
+ sink_node->Connect(processing_node);
+ processing_node->Connect(source_node);
+
+ // End-of-stream is not marked in source node.
+ // source_node->MarkEndOfStream();
+ }
+
+ EXPECT_FALSE(source_node_deleted);
+ EXPECT_FALSE(processing_node_deleted);
+ EXPECT_FALSE(sink_node_deleted);
+
+ sink_node->CleanUp();
+
+ EXPECT_FALSE(source_node_deleted);
+ EXPECT_FALSE(processing_node_deleted);
+ EXPECT_FALSE(sink_node_deleted);
+}
+
+// Tests a chain of an SinkNode, ProcessingNode and SourceNode
+// and runs the node clean-up procedure with sources being marked with
+// end-of-stream.
+TEST(AudioNodesTest, NodeCleanUpTest) {
+ static const bool kEnableProcessOnEmptyInput = true;
+
+ bool source_node_deleted = false;
+ bool processing_node_deleted = false;
+ bool sink_node_deleted = false;
+
+ auto sink_node = std::make_shared<MySinkNode>(&sink_node_deleted);
+
+ {
+ // Create a source and processing node and connect it to sink node.
+ auto source_node = std::make_shared<MySourceNode>(
+ /*output_empty_buffer=*/false, &source_node_deleted);
+ auto processing_node = std::make_shared<MyProcessingNode>(
+ kEnableProcessOnEmptyInput, nullptr, &processing_node_deleted);
+
+ // Connect nodes.
+ sink_node->Connect(processing_node);
+ processing_node->Connect(source_node);
+
+ // End of stream is marked in source node. Do not expect any data anymore.
+ source_node->MarkEndOfStream();
+ }
+
+ EXPECT_FALSE(source_node_deleted);
+ EXPECT_FALSE(processing_node_deleted);
+ EXPECT_FALSE(sink_node_deleted);
+
+ sink_node->CleanUp();
+
+ EXPECT_TRUE(source_node_deleted);
+ EXPECT_TRUE(processing_node_deleted);
+ EXPECT_FALSE(sink_node_deleted);
+}
+
+// Tests ProcessingNode::EnableProcessOnEmptyInput().
+TEST(AudioNodesTest, ProcessOnEmptyInputFlagTest) {
+ bool audio_process_called = false;
+
+ static const bool kEnableProcessOnEmptyInput = true;
+ static const bool kOutputEmptyBuffer = true;
+
+ auto source_node =
+ std::make_shared<MySourceNode>(kOutputEmptyBuffer, nullptr);
+ auto processing_node = std::make_shared<MyProcessingNode>(
+ kEnableProcessOnEmptyInput, &audio_process_called, nullptr);
+ auto sink_node = std::make_shared<MySinkNode>(nullptr);
+
+ // Create chain of nodes.
+ sink_node->Connect(processing_node);
+ processing_node->Connect(source_node);
+
+ EXPECT_FALSE(audio_process_called);
+ const auto& data = sink_node->ReadInputs();
+ EXPECT_TRUE(audio_process_called);
+ EXPECT_EQ(data.size(), 0U);
+}
+
+// Tests a chain of an |SinkNode| and |AudioMixerNode| without a |SourceNode|.
+TEST(AudioNodesTest, MissingSourceConnectionTest) {
+ auto mixer_node = std::make_shared<MyAudioMixerNode>(nullptr);
+ auto sink_node = std::make_shared<MySinkNode>(nullptr);
+
+ // Create chain of nodes.
+ sink_node->Connect(mixer_node);
+
+ // Test output data.
+ const auto& data = sink_node->ReadInputs();
+ EXPECT_EQ(data.size(), 0U);
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/node.h b/src/3rdparty/resonance-audio/resonance_audio/node/node.h
new file mode 100644
index 000000000..e6e10e39d
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/node.h
@@ -0,0 +1,258 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_NODE_NODE_H_
+#define RESONANCE_AUDIO_NODE_NODE_H_
+
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+
+namespace vraudio {
+
+// Implements a processing node in a synchronous processing graph.
+// This processing graph is expected to be directed, acyclic, and
+// accessed from a single thread.
+//
+// Subclasses are expected to implement Process(), which will read
+// from all of the instance's inputs, process the data as necessary,
+// and then write to all of its outputs.
+//
+// Data is passed through unique_ptrs, so nodes are expected to
+// modify data in place whenever it suits their purposes. If an
+// outputs is connected to more than one input, copies will be made for
+// each input.
+//
+// Graphs are managed through shared_ptrs. Orphaned nodes are kept
+// alive as long as they output to a living input. Ownership is
+// unidirectional -- from input to output -- in order to avoid
+// circular dependencies.
+class Node : public std::enable_shared_from_this<Node> {
+ public:
+ virtual ~Node() {}
+ virtual void Process() = 0;
+
+ // Disconnects from input nodes that are marked to be at the end of data
+ // stream.
+ //
+ // @return True if node is does not have any inputs and can be removed, false
+ // otherwise.
+ virtual bool CleanUp() = 0;
+
+ template <class T>
+ class Output;
+
+ // An endpoint for a node, this object consumes data from any connected
+ // outputs. Because an input may be connected to more than one output, it
+ // returns a vector of read data. All outputs must be of the same type.
+ template <typename T>
+ class Input {
+ public:
+ // Unordered map that stores pairs of input Node instances and their
+ // |Output| member.
+ typedef std::unordered_map<Output<T>*, std::shared_ptr<Node>> OutputNodeMap;
+
+ Input() {}
+ ~Input();
+
+ // Returns a vector of computed data, one for each connected output.
+ const std::vector<T>& Read();
+
+ // Connects this input to the specified output.
+ //
+ // @param node The parent of the output.
+ // @param output The output to connect to.
+ void Connect(const std::shared_ptr<Node>& node, Output<T>* output);
+
+ // Disconnects this input from the specified output.
+ //
+ // @param output The output to be disconnected.
+ void Disconnect(Output<T>* output);
+
+ // Returns the number of connected outputs.
+ //
+ // @return Number of connected outputs.
+ size_t GetNumConnections() const;
+
+ // Returns reference to OutputNodeMap map to obtain all connected nodes and
+ // their outputs.
+ const OutputNodeMap& GetConnectedNodeOutputPairs();
+
+ // Disable copy constructor.
+ Input(const Input& that) = delete;
+
+ private:
+ friend class Node::Output<T>;
+
+ void AddOutput(const std::shared_ptr<Node>& node, Output<T>* output);
+ void RemoveOutput(Output<T>* output);
+
+ OutputNodeMap outputs_;
+ std::vector<T> read_data_;
+ };
+
+ // An endpoint for a node, this object produces data for any connected inputs.
+ // Because an output may have more than one input, this object will duplicate
+ // any computed data, once for each connected input. All inputs must be of the
+ // same type.
+ //
+ // If an output does not have any data to deliver, it will ask its parent node
+ // to process more data. It is assumed that after processing, some new data
+ // will be written to this output.
+ template <typename T>
+ class Output {
+ public:
+ explicit Output(Node* node) : parent_(node) {}
+
+ // Parent nodes should call this function to push new data to any connected
+ // inputs. This data will be copied once for each connected input.
+ //
+ // @param data New data to pass to all connected inputs.
+ void Write(T data);
+
+ // Disable copy constructor.
+ Output(const Output& that) = delete;
+
+ private:
+ friend class Node::Input<T>;
+
+ // Signature of copy operator.
+ typedef T (*CopyOperator)(const T&);
+
+ // Returns a single piece of stored processed data. If no data exists,
+ // the parent node is processed to produce more data.
+ T PullData();
+
+ void AddInput(Input<T>* input);
+ bool RemoveInput(Input<T>* input);
+
+ std::set<Input<T>*> inputs_;
+ std::vector<T> written_data_;
+ Node* parent_;
+ };
+};
+
+template <class T>
+Node::Input<T>::~Input() {
+ for (auto& o : outputs_) {
+ CHECK(o.first->RemoveInput(this));
+ }
+}
+
+template <class T>
+const std::vector<T>& Node::Input<T>::Read() {
+ read_data_.clear();
+
+ for (auto& o : outputs_) {
+ // Obtain processed data.
+ T processed_data = o.first->PullData();
+ if (processed_data != nullptr) {
+ read_data_.emplace_back(std::move(processed_data));
+ }
+ }
+
+ return read_data_;
+}
+
+template <class T>
+void Node::Input<T>::Connect(const std::shared_ptr<Node>& node,
+ Output<T>* output) {
+ output->AddInput(this);
+ AddOutput(node, output);
+}
+
+// RemoveOutput(output) may trigger *output be destructed,
+// so we need to call output->RemoveInput(this) first.
+template <class T>
+void Node::Input<T>::Disconnect(Output<T>* output) {
+ output->RemoveInput(this);
+ RemoveOutput(output);
+}
+
+template <class T>
+size_t Node::Input<T>::GetNumConnections() const {
+ return outputs_.size();
+}
+
+template <class T>
+const typename Node::Input<T>::OutputNodeMap&
+Node::Input<T>::GetConnectedNodeOutputPairs() {
+ return outputs_;
+}
+
+template <class T>
+void Node::Input<T>::AddOutput(const std::shared_ptr<Node>& node,
+ Output<T>* output) {
+ outputs_[output] = node;
+
+ DCHECK(outputs_.find(output) != outputs_.end());
+}
+
+template <class T>
+void Node::Input<T>::RemoveOutput(Output<T>* output) {
+ outputs_.erase(output);
+}
+
+template <class T>
+T Node::Output<T>::PullData() {
+ if (written_data_.empty()) {
+ parent_->Process();
+ }
+
+ DCHECK(!written_data_.empty());
+
+ T return_value = std::move(written_data_.back());
+ written_data_.pop_back();
+ return return_value;
+}
+
+template <class T>
+void Node::Output<T>::Write(T data) {
+ DCHECK(written_data_.empty());
+ written_data_.clear();
+ written_data_.emplace_back(std::move(data));
+
+ // If we have more than one connected input, copy the data for each input.
+ for (size_t i = 1; i < inputs_.size(); i++) {
+ written_data_.push_back(written_data_[0]);
+ }
+
+ DCHECK(written_data_.size() == inputs_.size());
+}
+
+template <class T>
+void Node::Output<T>::AddInput(Input<T>* input) {
+ inputs_.insert(input);
+}
+
+template <class T>
+bool Node::Output<T>::RemoveInput(Input<T>* input) {
+ auto it = inputs_.find(input);
+ if (it == inputs_.end()) {
+ return false;
+ }
+
+ inputs_.erase(it);
+ return true;
+}
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_NODE_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/node_test.cc b/src/3rdparty/resonance-audio/resonance_audio/node/node_test.cc
new file mode 100644
index 000000000..e95a68a1c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/node_test.cc
@@ -0,0 +1,330 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "node/node.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+
+namespace vraudio {
+
+namespace {
+
+class SourceNode : public Node {
+ public:
+ explicit SourceNode(bool output_nullptr)
+ : output_nullptr_(output_nullptr), output_(this), next_value_(0) {}
+
+ void Process() final {
+ if (output_nullptr_) {
+ // Output nullptr.
+ output_.Write(nullptr);
+ } else {
+ output_.Write(&next_value_);
+ next_value_++;
+ }
+ }
+
+ bool CleanUp() final { return false; }
+
+ const bool output_nullptr_;
+ Node::Output<int*> output_;
+
+ private:
+ int next_value_;
+};
+
+class PassThrough : public Node {
+ public:
+ PassThrough() : output_(this) {}
+
+ void Process() final { output_.Write(input_.Read()[0]); }
+
+ bool CleanUp() final { return false; }
+
+ Node::Input<int*> input_;
+ Node::Output<int*> output_;
+};
+
+class IncNode : public Node {
+ public:
+ IncNode() : output_(this), inc_value_(0) {}
+
+ void Process() final {
+ inc_value_ = *input_.Read()[0];
+ ++inc_value_;
+ output_.Write(&inc_value_);
+ }
+
+ bool CleanUp() final { return false; }
+
+ Node::Input<int*> input_;
+ Node::Output<int*> output_;
+
+ private:
+ int inc_value_;
+};
+
+class SinkNode : public Node {
+ public:
+ void Process() final {}
+ bool CleanUp() final { return false; }
+
+ Node::Input<int*> input_;
+};
+
+// Class for testing multiple subscriber streams.
+class NodeTest : public ::testing::Test {
+ public:
+ void SetUp() override {}
+};
+
+TEST_F(NodeTest, SourceSink) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto sink_node = std::make_shared<SinkNode>();
+ sink_node->input_.Connect(source_node, &source_node->output_);
+
+ auto& data0 = sink_node->input_.Read();
+ EXPECT_EQ(data0.size(), 1U);
+ EXPECT_EQ(*data0[0], 1);
+
+ auto& data1 = sink_node->input_.Read();
+ EXPECT_EQ(data1.size(), 1U);
+ EXPECT_EQ(*data1[0], 2);
+}
+
+TEST_F(NodeTest, SourcePassThroughSink) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto copy_node = std::make_shared<PassThrough>();
+ auto sink_node = std::make_shared<SinkNode>();
+ sink_node->input_.Connect(copy_node, &copy_node->output_);
+ copy_node->input_.Connect(source_node, &source_node->output_);
+
+ auto& data0 = sink_node->input_.Read();
+ EXPECT_EQ(data0.size(), 1U);
+ EXPECT_EQ(*data0[0], 1);
+
+ auto& data1 = sink_node->input_.Read();
+ EXPECT_EQ(data1.size(), 1U);
+ EXPECT_EQ(*data1[0], 2);
+}
+
+TEST_F(NodeTest, TwoSources) {
+ static const bool kOutputNullptr = false;
+ auto source_node_a = std::make_shared<SourceNode>(kOutputNullptr);
+ auto source_node_b = std::make_shared<SourceNode>(kOutputNullptr);
+ auto sink_node = std::make_shared<SinkNode>();
+
+ sink_node->input_.Connect(source_node_a, &source_node_a->output_);
+ sink_node->input_.Connect(source_node_b, &source_node_b->output_);
+ EXPECT_EQ(source_node_a.use_count(), 2);
+ EXPECT_EQ(source_node_b.use_count(), 2);
+ EXPECT_EQ(sink_node.use_count(), 1);
+
+ auto& data0 = sink_node->input_.Read();
+ EXPECT_EQ(data0.size(), 2U);
+ EXPECT_EQ(*data0[0], 1);
+ EXPECT_EQ(*data0[1], 1);
+
+ auto& data1 = sink_node->input_.Read();
+ EXPECT_EQ(data1.size(), 2U);
+ EXPECT_EQ(*data1[0], 2);
+ EXPECT_EQ(*data1[1], 2);
+
+ sink_node.reset();
+ EXPECT_EQ(source_node_a.use_count(), 1);
+ EXPECT_EQ(source_node_b.use_count(), 1);
+}
+
+TEST_F(NodeTest, DoubleSink) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto sink_node_a = std::make_shared<SinkNode>();
+ auto sink_node_b = std::make_shared<SinkNode>();
+
+ sink_node_a->input_.Connect(source_node, &source_node->output_);
+ sink_node_b->input_.Connect(source_node, &source_node->output_);
+ EXPECT_EQ(sink_node_a.use_count(), 1);
+ EXPECT_EQ(sink_node_b.use_count(), 1);
+ EXPECT_EQ(source_node.use_count(), 3);
+
+ auto& dataA0 = sink_node_a->input_.Read();
+ auto& dataB0 = sink_node_b->input_.Read();
+ EXPECT_EQ(dataA0.size(), 1U);
+ EXPECT_EQ(dataB0.size(), 1U);
+ EXPECT_EQ(*dataA0[0], 1);
+ EXPECT_EQ(*dataB0[0], 1);
+
+ auto& dataA1 = sink_node_a->input_.Read();
+ auto& dataB1 = sink_node_b->input_.Read();
+ EXPECT_EQ(dataA1.size(), 1U);
+ EXPECT_EQ(dataB1.size(), 1U);
+ EXPECT_EQ(*dataA1[0], 2);
+ EXPECT_EQ(*dataB1[0], 2);
+
+ sink_node_a.reset();
+ EXPECT_EQ(source_node.use_count(), 2);
+ sink_node_b.reset();
+ EXPECT_EQ(source_node.use_count(), 1);
+}
+
+TEST_F(NodeTest, DoubleSinkWithIncrement) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto inc_node = std::make_shared<IncNode>();
+ auto sink_node_a = std::make_shared<SinkNode>();
+ auto sink_node_b = std::make_shared<SinkNode>();
+
+ sink_node_a->input_.Connect(source_node, &source_node->output_);
+ sink_node_b->input_.Connect(inc_node, &inc_node->output_);
+ inc_node->input_.Connect(source_node, &source_node->output_);
+
+ auto& dataA0 = sink_node_a->input_.Read();
+ auto& dataB0 = sink_node_b->input_.Read();
+ EXPECT_EQ(dataA0.size(), 1U);
+ EXPECT_EQ(dataB0.size(), 1U);
+ EXPECT_EQ(*dataA0[0], 1);
+ EXPECT_EQ(*dataB0[0], 2);
+
+ auto& dataA1 = sink_node_a->input_.Read();
+ auto& dataB1 = sink_node_b->input_.Read();
+ EXPECT_EQ(dataA1.size(), 1U);
+ EXPECT_EQ(dataB1.size(), 1U);
+ EXPECT_EQ(*dataA1[0], 2);
+ EXPECT_EQ(*dataB1[0], 3);
+}
+
+TEST_F(NodeTest, DisconnectSingleLink) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto sink_node = std::make_shared<SinkNode>();
+
+ sink_node->input_.Connect(source_node, &source_node->output_);
+ EXPECT_EQ(sink_node.use_count(), 1);
+ EXPECT_EQ(source_node.use_count(), 2);
+
+ auto& data0 = sink_node->input_.Read();
+ EXPECT_EQ(data0.size(), 1U);
+ EXPECT_EQ(*data0[0], 1);
+
+ sink_node->input_.Disconnect(&source_node->output_);
+ EXPECT_EQ(sink_node.use_count(), 1);
+ EXPECT_EQ(source_node.use_count(), 1);
+
+ auto& data1 = sink_node->input_.Read();
+ EXPECT_EQ(data1.size(), 0U);
+}
+
+TEST_F(NodeTest, DisconnectIntermediate) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto inc_node = std::make_shared<IncNode>();
+ auto sink_node = std::make_shared<SinkNode>();
+
+ sink_node->input_.Connect(inc_node, &inc_node->output_);
+ inc_node->input_.Connect(source_node, &source_node->output_);
+
+ inc_node->input_.Disconnect(&source_node->output_);
+ inc_node.reset();
+ EXPECT_EQ(sink_node.use_count(), 1);
+ EXPECT_EQ(source_node.use_count(), 1);
+}
+
+TEST_F(NodeTest, DisconnectMultiLink) {
+ static const bool kOutputNullptr = false;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto inc_node = std::make_shared<IncNode>();
+ auto sink_node_a = std::make_shared<SinkNode>();
+ auto sink_node_b = std::make_shared<SinkNode>();
+ auto sink_node_c = std::make_shared<SinkNode>();
+
+ sink_node_a->input_.Connect(source_node, &source_node->output_);
+ sink_node_b->input_.Connect(inc_node, &inc_node->output_);
+ sink_node_c->input_.Connect(inc_node, &inc_node->output_);
+ inc_node->input_.Connect(source_node, &source_node->output_);
+
+ sink_node_a->input_.Disconnect(&source_node->output_);
+ EXPECT_EQ(sink_node_a.use_count(), 1);
+ EXPECT_EQ(sink_node_b.use_count(), 1);
+ EXPECT_EQ(sink_node_c.use_count(), 1);
+ EXPECT_EQ(inc_node.use_count(), 3);
+ EXPECT_EQ(source_node.use_count(), 2);
+
+ auto& dataA0 = sink_node_a->input_.Read();
+ auto& dataB0 = sink_node_b->input_.Read();
+ auto& dataC0 = sink_node_c->input_.Read();
+ EXPECT_EQ(dataA0.size(), 0U);
+ EXPECT_EQ(dataB0.size(), 1U);
+ EXPECT_EQ(*dataB0[0], 2);
+ EXPECT_EQ(dataC0.size(), 1U);
+ EXPECT_EQ(*dataC0[0], 2);
+
+ sink_node_b->input_.Disconnect(&inc_node->output_);
+ EXPECT_EQ(sink_node_a.use_count(), 1);
+ EXPECT_EQ(sink_node_b.use_count(), 1);
+ EXPECT_EQ(sink_node_c.use_count(), 1);
+ EXPECT_EQ(inc_node.use_count(), 2);
+ EXPECT_EQ(source_node.use_count(), 2);
+
+ auto& dataA1 = sink_node_a->input_.Read();
+ auto& dataB1 = sink_node_b->input_.Read();
+ auto& dataC1 = sink_node_c->input_.Read();
+ EXPECT_EQ(dataA1.size(), 0U);
+ EXPECT_EQ(dataB1.size(), 0U);
+ EXPECT_EQ(dataC1.size(), 1U);
+ EXPECT_EQ(*dataC1[0], 3);
+
+ sink_node_c->input_.Disconnect(&inc_node->output_);
+ EXPECT_EQ(sink_node_a.use_count(), 1);
+ EXPECT_EQ(sink_node_b.use_count(), 1);
+ EXPECT_EQ(sink_node_c.use_count(), 1);
+ EXPECT_EQ(inc_node.use_count(), 1);
+ EXPECT_EQ(source_node.use_count(), 2);
+
+ auto& dataA2 = sink_node_a->input_.Read();
+ auto& dataB2 = sink_node_b->input_.Read();
+ auto& dataC2 = sink_node_c->input_.Read();
+ EXPECT_EQ(dataA2.size(), 0U);
+ EXPECT_EQ(dataB2.size(), 0U);
+ EXPECT_EQ(dataC2.size(), 0U);
+
+ inc_node->input_.Disconnect(&source_node->output_);
+ EXPECT_EQ(sink_node_a.use_count(), 1);
+ EXPECT_EQ(sink_node_b.use_count(), 1);
+ EXPECT_EQ(inc_node.use_count(), 1);
+ EXPECT_EQ(source_node.use_count(), 1);
+}
+
+TEST_F(NodeTest, NullPtrSourceMultipleClients) {
+ static const bool kOutputNullptr = true;
+ auto source_node = std::make_shared<SourceNode>(kOutputNullptr);
+ auto sink_node_a = std::make_shared<SinkNode>();
+ auto sink_node_b = std::make_shared<SinkNode>();
+
+ sink_node_a->input_.Connect(source_node, &source_node->output_);
+ sink_node_b->input_.Connect(source_node, &source_node->output_);
+
+ auto& dataA = sink_node_a->input_.Read();
+ auto& dataB = sink_node_b->input_.Read();
+
+ EXPECT_TRUE(dataA.empty());
+ EXPECT_TRUE(dataB.empty());
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/processing_node.cc b/src/3rdparty/resonance-audio/resonance_audio/node/processing_node.cc
new file mode 100644
index 000000000..3053e8e6b
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/processing_node.cc
@@ -0,0 +1,89 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "node/processing_node.h"
+
+namespace vraudio {
+
+ProcessingNode::NodeInput::NodeInput(
+ const std::vector<const AudioBuffer*>& input_vector)
+ : input_vector_(input_vector) {}
+
+const AudioBuffer* ProcessingNode::NodeInput::GetSingleInput() const {
+ if (input_vector_.size() == 1) {
+ return input_vector_[0];
+ }
+ if (input_vector_.size() > 1) {
+ LOG(WARNING) << "GetSingleInput() called on multi buffer input";
+ }
+ return nullptr;
+}
+
+const std::vector<const AudioBuffer*>&
+ProcessingNode::NodeInput::GetInputBuffers() const {
+ return input_vector_;
+}
+
+ProcessingNode::ProcessingNode()
+ : Node(), output_stream_(this), process_on_no_input_(false) {}
+
+void ProcessingNode::Connect(
+ const std::shared_ptr<PublisherNodeType>& publisher_node) {
+ input_stream_.Connect(publisher_node->GetSharedNodePtr(),
+ publisher_node->GetOutput());
+}
+
+void ProcessingNode::Process() {
+ NodeInput input(input_stream_.Read());
+ const AudioBuffer* output = nullptr;
+ // Only call AudioProcess if input data is available.
+ if (process_on_no_input_ || !input.GetInputBuffers().empty()) {
+ output = AudioProcess(input);
+ }
+ output_stream_.Write(output);
+}
+
+bool ProcessingNode::CleanUp() {
+ CallCleanUpOnInputNodes();
+ return (input_stream_.GetNumConnections() == 0);
+}
+
+void ProcessingNode::EnableProcessOnEmptyInput(bool enable) {
+ process_on_no_input_ = enable;
+}
+
+void ProcessingNode::CallCleanUpOnInputNodes() {
+ // We need to make a copy of the OutputNodeMap map since it changes due to
+ // Disconnect() calls.
+ const auto connected_nodes = input_stream_.GetConnectedNodeOutputPairs();
+ for (const auto& input_node : connected_nodes) {
+ Output<const AudioBuffer*>* output = input_node.first;
+ std::shared_ptr<Node> node = input_node.second;
+ const bool is_ready_to_be_disconnected = node->CleanUp();
+ if (is_ready_to_be_disconnected) {
+ input_stream_.Disconnect(output);
+ }
+ }
+}
+
+std::shared_ptr<Node> ProcessingNode::GetSharedNodePtr() {
+ return shared_from_this();
+}
+Node::Output<const AudioBuffer*>* ProcessingNode::GetOutput() {
+ return &output_stream_;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/processing_node.h b/src/3rdparty/resonance-audio/resonance_audio/node/processing_node.h
new file mode 100644
index 000000000..6142c794c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/processing_node.h
@@ -0,0 +1,115 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_NODE_PROCESSING_NODE_H_
+#define RESONANCE_AUDIO_NODE_PROCESSING_NODE_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "node/publisher_node.h"
+#include "node/subscriber_node.h"
+
+namespace vraudio {
+
+// Audio processing node that reads from multiple inputs, processes the
+// received data and outputs its result.
+class ProcessingNode : public Node,
+ public SubscriberNode<const AudioBuffer*>,
+ public PublisherNode<const AudioBuffer*> {
+ public:
+ typedef SubscriberNode<const AudioBuffer*> SubscriberNodeType;
+ typedef PublisherNode<const AudioBuffer*> PublisherNodeType;
+
+ // Helper class to manage incoming |AudioBuffer|s.
+ class NodeInput {
+ public:
+ // Constructor.
+ //
+ // @param input_vector Vector containing pointers to incoming
+ // |AudioBuffer|s.
+ explicit NodeInput(const std::vector<const AudioBuffer*>& input_vector);
+
+ // Returns a nullptr if zero or more than one input buffers are available.
+ // Otherwise a pointer to the single input |AudioBuffer| is returned. This
+ // method should be used if only a single input |AudioBuffer| is expected.
+ //
+ // @return Pointer to single input |AudioBuffer|.
+ const AudioBuffer* GetSingleInput() const;
+
+ // Returns vector with input |AudioBuffer|s.
+ //
+ // @return Pointer to single input |AudioBuffer|.
+ const std::vector<const AudioBuffer*>& GetInputBuffers() const;
+
+ // Delete copy constructor.
+ NodeInput(const NodeInput& that) = delete;
+
+ private:
+ // Const reference to vector of input |AudioBuffer|s.
+ const std::vector<const AudioBuffer*>& input_vector_;
+ };
+
+ ProcessingNode();
+
+ // SubscriberNode<InputType> implementation.
+ void Connect(
+ const std::shared_ptr<PublisherNodeType>& publisher_node) override;
+
+ // Node implementation.
+ void Process() final;
+ bool CleanUp() override;
+
+ // By default, calls to AudioProcess() are skipped in case of empty input
+ // buffers. This enables this node to process audio buffers in the absence of
+ // input data (which is needed for instance for a reverberation effect).
+ void EnableProcessOnEmptyInput(bool enable);
+
+ // Disable copy constructor.
+ ProcessingNode(const ProcessingNode& that) = delete;
+
+ protected:
+ // Calls |CleanUp| on all connected input nodes.
+ void CallCleanUpOnInputNodes();
+
+ // Pure virtual method to implement the audio processing method. This method
+ // receives a vector of all input arguments to be processed and requires to
+ // output a single output buffer.
+ //
+ // @param input Input instance to receive pointers to input |AudioBuffer|s.
+ // @return Returns output data.
+ virtual const AudioBuffer* AudioProcess(const NodeInput& input) = 0;
+
+ private:
+ // PublisherNode<OutputType> implementation.
+ std::shared_ptr<Node> GetSharedNodePtr() final;
+ Node::Output<const AudioBuffer*>* GetOutput() final;
+
+ // Input stream to poll for incoming data.
+ Node::Input<const AudioBuffer*> input_stream_;
+
+ // Output stream to write processed data to.
+ Node::Output<const AudioBuffer*> output_stream_;
+
+ // Flag that indicates if |AudioProcess| should be called in case no input
+ // data is available.
+ bool process_on_no_input_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_NODE_PROCESSING_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/publisher_node.h b/src/3rdparty/resonance-audio/resonance_audio/node/publisher_node.h
new file mode 100644
index 000000000..a6935da75
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/publisher_node.h
@@ -0,0 +1,51 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_NODE_PUBLISHER_NODE_H_
+#define RESONANCE_AUDIO_NODE_PUBLISHER_NODE_H_
+
+#include <memory>
+
+#include "base/logging.h"
+#include "node/node.h"
+
+namespace vraudio {
+
+// Interface for publisher nodes that declares helper methods required to
+// connect to a publisher node. All publishing nodes need to implement this
+// interface.
+//
+// @tparam OutputType Type of the output container being streamed.
+// @interface
+template <typename OutputType>
+class PublisherNode {
+ public:
+ virtual ~PublisherNode() {}
+
+ // Creates a shared pointer of the Node instance.
+ //
+ // @return Returns a shared pointer the of Node instance.
+ virtual std::shared_ptr<Node> GetSharedNodePtr() = 0;
+
+ // Get internal Node::Output instance.
+ //
+ // @return Returns a pointer to the internal Node::Output instance.
+ virtual Node::Output<OutputType>* GetOutput() = 0;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_NODE_PUBLISHER_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/sink_node.cc b/src/3rdparty/resonance-audio/resonance_audio/node/sink_node.cc
new file mode 100644
index 000000000..2d003009c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/sink_node.cc
@@ -0,0 +1,54 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "node/sink_node.h"
+
+#include "base/logging.h"
+
+namespace vraudio {
+
+SinkNode::SinkNode() : Node() {}
+
+const std::vector<const AudioBuffer*>& SinkNode::ReadInputs() {
+ return input_stream_.Read();
+}
+
+void SinkNode::Connect(
+ const std::shared_ptr<PublisherNodeType>& publisher_node) {
+ input_stream_.Connect(publisher_node->GetSharedNodePtr(),
+ publisher_node->GetOutput());
+}
+
+void SinkNode::Process() {
+ LOG(FATAL) << "Process should not be called on audio sink node.";
+}
+
+bool SinkNode::CleanUp() {
+ // We need to make a copy of the OutputNodeMap map since it might change due
+ // to Disconnect() calls.
+ const auto connected_nodes = input_stream_.GetConnectedNodeOutputPairs();
+ for (const auto& input_node : connected_nodes) {
+ Output<const AudioBuffer*>* output = input_node.first;
+ std::shared_ptr<Node> node = input_node.second;
+ const bool is_orphaned = node->CleanUp();
+ if (is_orphaned) {
+ input_stream_.Disconnect(output);
+ }
+ }
+ return false;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/sink_node.h b/src/3rdparty/resonance-audio/resonance_audio/node/sink_node.h
new file mode 100644
index 000000000..4bb3ade06
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/sink_node.h
@@ -0,0 +1,59 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_NODE_SINK_NODE_H_
+#define RESONANCE_AUDIO_NODE_SINK_NODE_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "node/publisher_node.h"
+#include "node/subscriber_node.h"
+
+namespace vraudio {
+
+// Audio sink node that reads from multiple inputs.
+class SinkNode : public Node, public SubscriberNode<const AudioBuffer*> {
+ public:
+ typedef PublisherNode<const AudioBuffer*> PublisherNodeType;
+
+ SinkNode();
+
+ // Polls for data on all inputs of the sink node.
+ //
+ // @return A vector of input data.
+ const std::vector<const AudioBuffer*>& ReadInputs();
+
+ // SubscriberNode<AudioBuffer> implementation.
+ void Connect(
+ const std::shared_ptr<PublisherNodeType>& publisher_node) override;
+
+ // Node implementation.
+ void Process() final;
+ bool CleanUp() final;
+
+ // Disable copy constructor.
+ SinkNode(const SinkNode& that) = delete;
+
+ private:
+ // Input stream to poll for incoming data.
+ Node::Input<const AudioBuffer*> input_stream_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_NODE_SINK_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/source_node.cc b/src/3rdparty/resonance-audio/resonance_audio/node/source_node.cc
new file mode 100644
index 000000000..8567df6c6
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/source_node.cc
@@ -0,0 +1,41 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "node/source_node.h"
+
+namespace vraudio {
+
+SourceNode::SourceNode()
+ : Node(), output_stream_(this), end_of_stream_(false) {}
+
+void SourceNode::Process() {
+ const AudioBuffer* output = AudioProcess();
+ output_stream_.Write(output);
+}
+
+bool SourceNode::CleanUp() { return end_of_stream_; }
+
+std::shared_ptr<Node> SourceNode::GetSharedNodePtr() {
+ return shared_from_this();
+}
+
+Node::Output<const AudioBuffer*>* SourceNode::GetOutput() {
+ return &output_stream_;
+}
+
+void SourceNode::MarkEndOfStream() { end_of_stream_ = true; }
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/source_node.h b/src/3rdparty/resonance-audio/resonance_audio/node/source_node.h
new file mode 100644
index 000000000..73a598637
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/source_node.h
@@ -0,0 +1,68 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_NODE_SOURCE_NODE_H_
+#define RESONANCE_AUDIO_NODE_SOURCE_NODE_H_
+
+#include <atomic>
+#include <memory>
+
+#include "base/audio_buffer.h"
+#include "node/publisher_node.h"
+
+namespace vraudio {
+
+// Audio source node that outputs data via the AudioProcess() method.
+class SourceNode : public Node, public PublisherNode<const AudioBuffer*> {
+ public:
+ typedef PublisherNode<const AudioBuffer*> PublisherNodeType;
+
+ SourceNode();
+
+ // Node implementation.
+ void Process() final;
+ bool CleanUp() final;
+
+ // PublisherNode<OutputType> implementation.
+ std::shared_ptr<Node> GetSharedNodePtr() final;
+ Node::Output<const AudioBuffer*>* GetOutput() final;
+
+ // Marks this node as being out of data and to be removed during the next
+ // clean-up cycle.
+ void MarkEndOfStream();
+
+ // Disable copy constructor.
+ SourceNode(const SourceNode& that) = delete;
+
+ protected:
+ // Pure virtual method to implement the audio processing method. This method
+ // requires to return a single output buffer that can be processed by the node
+ // subscribers.
+ //
+ // @return Returns output data.
+ virtual const AudioBuffer* AudioProcess() = 0;
+
+ private:
+ // Output stream to write processed data to.
+ Node::Output<const AudioBuffer*> output_stream_;
+
+ // Flag indicating if this source node can be removed.
+ std::atomic<bool> end_of_stream_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_NODE_SOURCE_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/node/subscriber_node.h b/src/3rdparty/resonance-audio/resonance_audio/node/subscriber_node.h
new file mode 100644
index 000000000..f297ce9e4
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/node/subscriber_node.h
@@ -0,0 +1,48 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_NODE_SUBSCRIBER_NODE_H_
+#define RESONANCE_AUDIO_NODE_SUBSCRIBER_NODE_H_
+
+#include <memory>
+
+#include "base/logging.h"
+#include "node/publisher_node.h"
+
+namespace vraudio {
+
+// Interface for subscriber nodes that declares the connection and
+// disconnection methods. All subscribing nodes need to implement this
+// interface.
+//
+// @tparam InputType Input data type, i. e., the output data type of nodes to
+// connect to.
+// @interface
+template <typename InputType>
+class SubscriberNode {
+ public:
+ virtual ~SubscriberNode() {}
+
+ // Connects this node to |publisher_node|.
+ //
+ // @param publisher_node Publisher node to connect to.
+ virtual void Connect(
+ const std::shared_ptr<PublisherNode<InputType>>& publisher_node) = 0;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_NODE_SUBSCRIBER_NODE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_crossfader.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_crossfader.cc
new file mode 100644
index 000000000..01aa0f473
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_crossfader.cc
@@ -0,0 +1,66 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/buffer_crossfader.h"
+
+#include "base/constants_and_types.h"
+#include "base/simd_utils.h"
+
+namespace vraudio {
+
+BufferCrossfader::BufferCrossfader(size_t num_frames)
+ : crossfade_buffer_(kNumStereoChannels, num_frames) {
+ DCHECK_NE(num_frames, 0);
+ // Initialize the |crossfade_buffer_|.
+ auto* fade_in_channel = &crossfade_buffer_[0];
+ auto* fade_out_channel = &crossfade_buffer_[1];
+ for (size_t frame = 0; frame < num_frames; ++frame) {
+ const float crossfade_factor =
+ static_cast<float>(frame) / static_cast<float>(num_frames);
+ (*fade_in_channel)[frame] = crossfade_factor;
+ (*fade_out_channel)[frame] = 1.0f - crossfade_factor;
+ }
+}
+
+void BufferCrossfader::ApplyLinearCrossfade(const AudioBuffer& input_fade_in,
+ const AudioBuffer& input_fade_out,
+ AudioBuffer* output) const {
+ DCHECK(output);
+ DCHECK_NE(output, &input_fade_in);
+ DCHECK_NE(output, &input_fade_out);
+
+ const size_t num_channels = input_fade_in.num_channels();
+ const size_t num_frames = input_fade_in.num_frames();
+ DCHECK_EQ(num_channels, input_fade_out.num_channels());
+ DCHECK_EQ(num_channels, output->num_channels());
+ DCHECK_EQ(num_frames, input_fade_out.num_frames());
+ DCHECK_EQ(num_frames, output->num_frames());
+ DCHECK_EQ(num_frames, crossfade_buffer_.num_frames());
+
+ const auto* gain_fade_in_channel = crossfade_buffer_[0].begin();
+ const auto* gain_fade_out_channel = crossfade_buffer_[1].begin();
+ for (size_t channel = 0; channel < num_channels; ++channel) {
+ const auto* input_fade_in_channel = input_fade_in[channel].begin();
+ const auto* input_fade_out_channel = input_fade_out[channel].begin();
+ auto* output_channel = ((*output)[channel]).begin();
+ MultiplyPointwise(num_frames, gain_fade_in_channel, input_fade_in_channel,
+ output_channel);
+ MultiplyAndAccumulatePointwise(num_frames, gain_fade_out_channel,
+ input_fade_out_channel, output_channel);
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_crossfader.h b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_crossfader.h
new file mode 100644
index 000000000..25502df3a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_crossfader.h
@@ -0,0 +1,48 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_BUFFER_CROSSFADER_H_
+#define RESONANCE_AUDIO_UTILS_BUFFER_CROSSFADER_H_
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Class that takes two input buffers and produces an output buffer by applying
+// linear crossfade between the inputs.
+class BufferCrossfader {
+ public:
+ explicit BufferCrossfader(size_t num_frames);
+
+ // Applies linear crossfade for given input buffers and stores the result in
+ // |output| buffer. Note that, in-place processing is *not* supported by this
+ // method, the output buffer must be different than the input buffers.
+ //
+ // @param input_fade_in Input buffer to fade-in to.
+ // @param input_fade_out Input buffer to fade-out from.
+ // @param output Output buffer to store the crossfaded result.
+ void ApplyLinearCrossfade(const AudioBuffer& input_fade_in,
+ const AudioBuffer& input_fade_out,
+ AudioBuffer* output) const;
+
+ private:
+ // Stereo audio buffer to store crossfade decay and growth multipliers.
+ AudioBuffer crossfade_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_BUFFER_CROSSFADER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_crossfader_test.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_crossfader_test.cc
new file mode 100644
index 000000000..c14a0e950
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_crossfader_test.cc
@@ -0,0 +1,53 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/buffer_crossfader.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+namespace {
+
+// Tests that the linear crossfade is correctly applied to an input pair.
+TEST(BufferCrossfaderTest, ApplyLinearCrossfadeTest) {
+ const size_t kNumFrames = 5;
+ const size_t kTestValue = 1.0f;
+ // Initialize input buffers.
+ AudioBuffer input_fade_in(kNumMonoChannels, kNumFrames);
+ AudioBuffer input_fade_out(kNumMonoChannels, kNumFrames);
+ for (size_t i = 0; i < kNumFrames; ++i) {
+ input_fade_in[0][i] = kTestValue;
+ input_fade_out[0][i] = kTestValue;
+ }
+ // Initialize output buffer.
+ AudioBuffer output(kNumMonoChannels, kNumFrames);
+ output.Clear();
+ // Initialize a new crossfader and apply linear crossfade.
+ BufferCrossfader crossfader(kNumFrames);
+ crossfader.ApplyLinearCrossfade(input_fade_in, input_fade_out, &output);
+ // Verify that the output buffer is correctly filled in as expected.
+ for (size_t i = 0; i < kNumFrames; ++i) {
+ EXPECT_FLOAT_EQ(kTestValue, output[0][i]);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_partitioner.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_partitioner.cc
new file mode 100644
index 000000000..91023fed5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_partitioner.cc
@@ -0,0 +1,160 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "utils/buffer_partitioner.h"
+
+#include "utils/planar_interleaved_conversion.h"
+
+namespace vraudio {
+
+BufferPartitioner::BufferPartitioner(size_t num_channels,
+ size_t frames_per_buffer,
+ NewBufferCallback buffer_callback)
+ : num_channels_(num_channels),
+ frames_per_buffer_(frames_per_buffer),
+ buffer_callback_(std::move(buffer_callback)),
+ current_buffer_ptr_(nullptr),
+ current_buffer_write_position_frames_(0),
+ planar_channel_ptrs_(num_channels) {}
+
+size_t BufferPartitioner::GetNumGeneratedBuffersForNumInputFrames(
+ size_t num_input_frames) const {
+ return (current_buffer_write_position_frames_ + num_input_frames) /
+ frames_per_buffer_;
+}
+
+void BufferPartitioner::AddBuffer(const int16* interleaved_buffer,
+ size_t num_channels, size_t num_frames) {
+ AddBufferTemplated<const int16*>(interleaved_buffer, num_channels,
+ num_frames);
+}
+
+void BufferPartitioner::AddBuffer(const float* interleaved_buffer,
+ size_t num_channels, size_t num_frames) {
+ AddBufferTemplated<const float*>(interleaved_buffer, num_channels,
+ num_frames);
+}
+
+void BufferPartitioner::AddBuffer(const float* const* planar_buffer,
+ size_t num_channels, size_t num_frames) {
+ AddBufferTemplated<const float* const*>(planar_buffer, num_channels,
+ num_frames);
+}
+
+void BufferPartitioner::AddBuffer(const int16* const* planar_buffer,
+ size_t num_channels, size_t num_frames) {
+ AddBufferTemplated<const int16* const*>(planar_buffer, num_channels,
+ num_frames);
+}
+
+void BufferPartitioner::AddBuffer(const AudioBuffer& audio_buffer) {
+ AddBuffer(audio_buffer.num_frames(), audio_buffer);
+}
+
+void BufferPartitioner::AddBuffer(size_t num_valid_frames,
+ const AudioBuffer& audio_buffer) {
+ DCHECK_EQ(audio_buffer.num_channels(), num_channels_);
+ DCHECK_LE(num_valid_frames, audio_buffer.num_frames());
+ for (size_t channel = 0; channel < num_channels_; ++channel) {
+ planar_channel_ptrs_[channel] = &audio_buffer[channel][0];
+ }
+ AddBuffer(planar_channel_ptrs_.data(), audio_buffer.num_channels(),
+ num_valid_frames);
+}
+
+size_t BufferPartitioner::GetNumBufferedFrames() const {
+ return current_buffer_write_position_frames_;
+}
+
+size_t BufferPartitioner::Flush() {
+ if (current_buffer_write_position_frames_ == 0 ||
+ current_buffer_ptr_ == nullptr) {
+ return 0;
+ }
+ DCHECK_LE(current_buffer_write_position_frames_,
+ current_buffer_ptr_->num_frames());
+ const size_t num_zeropadded_frames =
+ current_buffer_ptr_->num_frames() - current_buffer_write_position_frames_;
+ for (AudioBuffer::Channel& channel : *current_buffer_ptr_) {
+ DCHECK_LE(current_buffer_write_position_frames_, channel.size());
+ std::fill(channel.begin() + current_buffer_write_position_frames_,
+ channel.end(), 0.0f);
+ }
+ current_buffer_ptr_ = buffer_callback_(current_buffer_ptr_);
+ current_buffer_write_position_frames_ = 0;
+ return num_zeropadded_frames;
+}
+
+void BufferPartitioner::Clear() {
+ current_buffer_ptr_ = nullptr;
+ current_buffer_write_position_frames_ = 0;
+}
+
+template <typename BufferType>
+void BufferPartitioner::AddBufferTemplated(BufferType buffer,
+ size_t num_channels,
+ size_t num_frames) {
+ DCHECK_EQ(num_channels, num_channels_);
+
+ size_t input_read_frame = 0;
+ while (input_read_frame < num_frames) {
+ if (current_buffer_ptr_ == nullptr) {
+ current_buffer_ptr_ = buffer_callback_(nullptr);
+ if (current_buffer_ptr_ == nullptr) {
+ LOG(WARNING) << "No input buffer received";
+ return;
+ }
+ DCHECK_EQ(current_buffer_ptr_->num_frames(), frames_per_buffer_);
+ DCHECK_EQ(current_buffer_ptr_->num_channels(), num_channels_);
+ current_buffer_write_position_frames_ = 0;
+ }
+ DCHECK_GT(frames_per_buffer_, current_buffer_write_position_frames_);
+ const size_t remaining_frames_in_temp_buffer =
+ frames_per_buffer_ - current_buffer_write_position_frames_;
+ const size_t remaining_frames_in_input_buffer =
+ num_frames - input_read_frame;
+ const size_t num_frames_to_process = std::min(
+ remaining_frames_in_temp_buffer, remaining_frames_in_input_buffer);
+
+ FillAudioBufferWithOffset(buffer, num_frames, num_channels_,
+ input_read_frame,
+ current_buffer_write_position_frames_,
+ num_frames_to_process, current_buffer_ptr_);
+
+ input_read_frame += num_frames_to_process;
+
+ // Update write pointers in temporary buffer.
+ current_buffer_write_position_frames_ += num_frames_to_process;
+ if (current_buffer_write_position_frames_ == frames_per_buffer_) {
+ // Current buffer is filled with data -> pass it to callback.
+ current_buffer_ptr_ = buffer_callback_(current_buffer_ptr_);
+ current_buffer_write_position_frames_ = 0;
+ if (current_buffer_ptr_ == nullptr) {
+ LOG(WARNING) << "No input buffer received";
+ return;
+ }
+ DCHECK_EQ(current_buffer_ptr_->num_frames(), frames_per_buffer_);
+ DCHECK_EQ(current_buffer_ptr_->num_channels(), num_channels_);
+ }
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_partitioner.h b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_partitioner.h
new file mode 100644
index 000000000..208ad886c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_partitioner.h
@@ -0,0 +1,152 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_BUFFER_PARTITIONER_H_
+#define RESONANCE_AUDIO_UTILS_BUFFER_PARTITIONER_H_
+
+#include <functional>
+#include <memory>
+
+#include "base/audio_buffer.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+// Packages input buffers of arbitrary sizes into fixed size |AudioBuffer|s.
+class BufferPartitioner {
+ public:
+ // Callback to receive processed and assign empty |AudioBuffer|s while
+ // processing input buffers.
+ //
+ // @param output Pointer to partitioned |AudioBuffer| with input audio data.
+ // @return Pointer to the next |AudioBuffer| to be filled.
+ typedef std::function<AudioBuffer*(AudioBuffer* output)> NewBufferCallback;
+
+ // Constructor.
+ //
+ // @param num_channels Number of audio channels in input and output buffers.
+ // @param frames_per_buffer Number of frames in output |AudioBuffer|s.
+ // @param buffer_callback Callback to receive output |AudioBuffer|s.
+ BufferPartitioner(size_t num_channels, size_t frames_per_buffer,
+ NewBufferCallback buffer_callback);
+
+ // Predicts the number of generated buffers for a given number of input frames
+ // and based on the current fill state of the internal |temp_buffer_|.
+ //
+ // @param num_input_frames Number of input frames.
+ // @return Number of generated output buffers.
+ size_t GetNumGeneratedBuffersForNumInputFrames(size_t num_input_frames) const;
+
+ // Returns the number of buffered frames in internal |temp_buffer_|.
+ size_t GetNumBufferedFrames() const;
+
+ // Adds an interleaved int16 input buffer. This method triggers
+ // |NewBufferCallback| whenever a new |AudioBuffer| has been generated.
+ //
+ // @param interleaved_buffer Interleaved input buffer.
+ // @param num_channels Number of channels in input buffer.
+ // @param num_frames Number of frames in input buffer.
+ void AddBuffer(const int16* interleaved_buffer, size_t num_channels,
+ size_t num_frames);
+
+ // Adds an interleaved float input buffer. This method triggers
+ // |NewBufferCallback| whenever a new |AudioBuffer| has been generated.
+ //
+ // @param interleaved_buffer Interleaved input buffer.
+ // @param num_channels Number of channels in input buffer.
+ // @param num_frames Number of frames in input buffer.
+ void AddBuffer(const float* interleaved_buffer, size_t num_channels,
+ size_t num_frames);
+
+ // Adds a planar float input buffer. This method triggers
+ // |NewBufferCallback| whenever a new |AudioBuffer| has been generated.
+ //
+ // @param planar_buffer Pointer to array of pointers for each audio channel.
+ // @param num_channels Number of channels in input buffer.
+ // @param num_frames Number of frames in input buffer.
+ void AddBuffer(const float* const* planar_buffer, size_t num_channels,
+ size_t num_frames);
+
+ // Adds a planar int16 input buffer. This method triggers
+ // |NewBufferCallback| whenever a new |AudioBuffer| has been generated.
+ //
+ // @param planar_buffer Pointer to array of pointers for each audio channel.
+ // @param num_channels Number of channels in input buffer.
+ // @param num_frames Number of frames in input buffer.
+ void AddBuffer(const int16* const* planar_buffer, size_t num_channels,
+ size_t num_frames);
+
+ // Adds an |AudioBuffer|. This method triggers |NewBufferCallback| whenever a
+ // new |AudioBuffer| has been generated.
+ //
+ // @param audio_buffer Planar |AudioBuffer| input.
+ void AddBuffer(const AudioBuffer& audio_buffer);
+
+ // Adds an |AudioBuffer| and specifies how many of that audio buffers frames
+ // are valid. This method triggers |NewBufferCallback| whenever a new
+ // |AudioBuffer| has been generated.
+ //
+ // @param num_valid_frames Indicates the number of frames which are actually
+ // valid in the audio buffer passed. Frames after this will be ignored.
+ // @param audio_buffer Planar |AudioBuffer| input.
+ void AddBuffer(size_t num_valid_frames, const AudioBuffer& audio_buffer);
+
+ // Flushes the internal temporary |AudioBuffer| by filling the remaining
+ // audio frames with silence.
+ //
+ // @return Number of zero padded audio frames. Zero if the internal buffer
+ // is empty.
+ size_t Flush();
+
+ // Clears internal temporary buffer that holds remaining audio frames.
+ void Clear();
+
+ private:
+ // Adds an interleaved and planar float and int16 input buffer as well as
+ // planar |AudioBuffer| input. This method triggers |NewBufferCallback|
+ // whenever a new |AudioBuffer| has been generated.
+ //
+ // @tparam BufferType Input buffer type.
+ // @param buffer Input buffer.
+ // @param num_channels Number of channels in input buffer.
+ // @param num_frames Number of frames in input buffer.
+ template <typename BufferType>
+ void AddBufferTemplated(BufferType buffer, size_t num_channels,
+ size_t num_frames);
+
+ // Number of channels in input buffers.
+ const size_t num_channels_;
+
+ // Number of frames per buffer in output buffers.
+ const size_t frames_per_buffer_;
+
+ // Callback to output generated |AudioBuffer|s.
+ const NewBufferCallback buffer_callback_;
+
+ // Temporary buffer to remaining samples from input buffers.
+ AudioBuffer* current_buffer_ptr_; // Not owned.
+
+ // Current write position in frames in temporary buffer.
+ size_t current_buffer_write_position_frames_;
+
+ // Helper vector to obtain an array of planar channel pointers from an
+ // |AudioBuffer|.
+ std::vector<const float*> planar_channel_ptrs_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_BUFFER_PARTITIONER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_partitioner_test.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_partitioner_test.cc
new file mode 100644
index 000000000..a486b1f23
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_partitioner_test.cc
@@ -0,0 +1,241 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/buffer_partitioner.h"
+
+#include <memory>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+#include "utils/planar_interleaved_conversion.h"
+#include "utils/test_util.h"
+
+namespace vraudio {
+
+const size_t kNumFramesPerBuffer = 64;
+
+using vraudio::AudioBuffer;
+
+class BufferPartitionerTest : public ::testing::Test {
+ public:
+ // Public validation method. This is called for every generated |AudioBuffer|.
+ AudioBuffer* ValidateAudioBufferCallback(AudioBuffer* audio_buffer) {
+ if (audio_buffer != nullptr) {
+ std::vector<float> received_buffer_interleaved;
+ FillExternalBuffer(*audio_buffer, &received_buffer_interleaved);
+ total_received_buffer_interleaved_.insert(
+ total_received_buffer_interleaved_.end(),
+ received_buffer_interleaved.begin(),
+ received_buffer_interleaved.end());
+
+ ++num_total_buffers_received_;
+ num_total_frames_received_ += audio_buffer->num_frames();
+ }
+ ++num_total_callbacks_triggered_;
+ return &partitioned_buffer_;
+ }
+
+ protected:
+ BufferPartitionerTest()
+ : partitioned_buffer_(kNumMonoChannels, kNumFramesPerBuffer),
+ partitioner_(
+ kNumMonoChannels, kNumFramesPerBuffer,
+ std::bind(&BufferPartitionerTest::ValidateAudioBufferCallback, this,
+ std::placeholders::_1)),
+ num_total_frames_received_(0),
+ num_total_buffers_received_(0),
+ num_total_callbacks_triggered_(0) {}
+ ~BufferPartitionerTest() override {}
+
+ // Returns a section of an |AudioBuffer| in interleaved format.
+ template <typename T>
+ std::vector<T> GetInterleavedVectorFromAudioBufferSection(
+ const AudioBuffer& planar_buffer, size_t frame_offset,
+ size_t num_frames) {
+ CHECK_GE(planar_buffer.num_frames(), frame_offset + num_frames);
+ std::vector<T> return_vector(planar_buffer.num_channels() * num_frames);
+
+ FillExternalBufferWithOffset(
+ planar_buffer, frame_offset, return_vector.data(), num_frames,
+ planar_buffer.num_channels(), 0 /* output_offset_frames */, num_frames);
+
+ return return_vector;
+ }
+
+ template <typename T>
+ void SmallBufferInputTest(float buffer_epsilon) {
+ ResetTest();
+
+ const size_t kNumInputBuffers = 8;
+ const size_t kNumFramesPerTestBuffer =
+ kNumFramesPerBuffer / kNumInputBuffers;
+
+ AudioBuffer test_buffer(vraudio::kNumMonoChannels, kNumFramesPerBuffer);
+ GenerateSawToothSignal(kNumFramesPerBuffer, &test_buffer[0]);
+
+ for (size_t i = 0; i < kNumInputBuffers; ++i) {
+ const std::vector<T> input_interleaved =
+ GetInterleavedVectorFromAudioBufferSection<T>(
+ test_buffer, kNumFramesPerTestBuffer * i,
+ kNumFramesPerTestBuffer);
+
+ partitioner_.AddBuffer(input_interleaved.data(), kNumMonoChannels,
+ kNumFramesPerTestBuffer);
+ }
+ EXPECT_EQ(num_total_buffers_received_, 1U);
+ EXPECT_EQ(num_total_callbacks_triggered_, num_total_buffers_received_ + 1);
+ EXPECT_EQ(num_total_frames_received_, kNumFramesPerBuffer);
+
+ AudioBuffer reconstructed_buffer(kNumMonoChannels, kNumFramesPerBuffer);
+ FillAudioBuffer(total_received_buffer_interleaved_, kNumMonoChannels,
+ &reconstructed_buffer);
+
+ EXPECT_TRUE(CompareAudioBuffers(test_buffer[0], reconstructed_buffer[0],
+ buffer_epsilon));
+ }
+
+ template <typename T>
+ void LargeBufferTest(float buffer_epsilon) {
+ ResetTest();
+ const size_t kNumOutputBuffers = 8;
+ const size_t kNumFramesPerTestBuffer =
+ kNumFramesPerBuffer * kNumOutputBuffers;
+
+ AudioBuffer test_buffer(vraudio::kNumMonoChannels, kNumFramesPerTestBuffer);
+ GenerateSawToothSignal(kNumFramesPerTestBuffer, &test_buffer[0]);
+
+ const std::vector<T> input_interleaved =
+ GetInterleavedVectorFromAudioBufferSection<T>(test_buffer, 0,
+ kNumFramesPerTestBuffer);
+
+ partitioner_.AddBuffer(input_interleaved.data(), kNumMonoChannels,
+ kNumFramesPerTestBuffer);
+
+ EXPECT_EQ(num_total_buffers_received_, kNumOutputBuffers);
+ EXPECT_EQ(num_total_callbacks_triggered_, num_total_buffers_received_ + 1);
+ EXPECT_EQ(num_total_frames_received_, kNumFramesPerTestBuffer);
+
+ AudioBuffer reconstructed_buffer(kNumMonoChannels, kNumFramesPerTestBuffer);
+ FillAudioBuffer(total_received_buffer_interleaved_, kNumMonoChannels,
+ &reconstructed_buffer);
+
+ EXPECT_TRUE(CompareAudioBuffers(test_buffer[0], reconstructed_buffer[0],
+ buffer_epsilon));
+ }
+
+ void ResetTest() {
+ num_total_frames_received_ = 0;
+ num_total_buffers_received_ = 0;
+ num_total_callbacks_triggered_ = 0;
+ total_received_buffer_interleaved_.clear();
+
+ partitioner_.Clear();
+ }
+
+ AudioBuffer partitioned_buffer_;
+ std::vector<float> total_received_buffer_interleaved_;
+
+ BufferPartitioner partitioner_;
+
+ // Stores the total number of frames received in
+ // |ValidateAudioBufferCallback|.
+ size_t num_total_frames_received_;
+
+ // Stores the total number of buffers received in
+ // |ValidateAudioBufferCallback|.
+ size_t num_total_buffers_received_;
+
+ // Stores the total number of callbacks being triggered in
+ // |ValidateAudioBufferCallback|.
+ size_t num_total_callbacks_triggered_;
+};
+
+// Tests the concatenation of multiple small buffers into a single
+// |AudioBuffer|.
+TEST_F(BufferPartitionerTest, TestSmallBufferInput) {
+ // int16 to float conversions introduce rounding errors.
+ SmallBufferInputTest<int16>(1e-4f /* buffer_epsilon */);
+ SmallBufferInputTest<float>(1e-6f /* buffer_epsilon */);
+}
+
+// Tests the splitting of a large input buffer into multiple |AudioBuffer|s.
+TEST_F(BufferPartitionerTest, TestLargeBufferInput) {
+ // int16 to float conversions introduce rounding errors.
+ LargeBufferTest<int16>(1e-4f /* buffer_epsilon */);
+ LargeBufferTest<float>(1e-6f /* buffer_epsilon */);
+}
+
+// Tests the GetNumBufferedFramesTest() and
+// GetNumGeneratedBuffersForNumInputFrames() methods.
+TEST_F(BufferPartitionerTest, GetNumBufferedFramesTest) {
+ const std::vector<float> input_interleaved(1, 1.0f);
+
+ EXPECT_EQ(partitioner_.GetNumGeneratedBuffersForNumInputFrames(
+ kNumFramesPerBuffer - 1),
+ 0U);
+ EXPECT_EQ(
+ partitioner_.GetNumGeneratedBuffersForNumInputFrames(kNumFramesPerBuffer),
+ 1U);
+
+ for (size_t i = 0; i < kNumFramesPerBuffer * 2; ++i) {
+ EXPECT_EQ(partitioner_.GetNumBufferedFrames(), i % kNumFramesPerBuffer);
+
+ const size_t large_random_frame_number =
+ i * 7 * kNumFramesPerBuffer + 13 * i;
+ // Expected number of generated buffers is based on
+ // |large_random_frame_number| and the internally stored remainder |i %
+ // kNumFramesPerBuffer|.
+ const size_t expected_num_generated_buffers =
+ (large_random_frame_number + partitioner_.GetNumBufferedFrames()) /
+ kNumFramesPerBuffer;
+
+ EXPECT_EQ(partitioner_.GetNumGeneratedBuffersForNumInputFrames(
+ large_random_frame_number),
+ expected_num_generated_buffers);
+
+ // Add single frame to |partinioner_|.
+ partitioner_.AddBuffer(input_interleaved.data(), kNumMonoChannels, 1);
+ }
+}
+
+// Tests the Flush() method.
+TEST_F(BufferPartitionerTest, FlushTest) {
+ AudioBuffer test_buffer(vraudio::kNumMonoChannels, kNumFramesPerBuffer);
+ GenerateDiracImpulseFilter(0, &test_buffer[0]);
+
+ const std::vector<float> input_interleaved =
+ GetInterleavedVectorFromAudioBufferSection<float>(
+ test_buffer, 0 /* frame_offset */, kNumFramesPerBuffer);
+
+ // Add only the first sample that contains the dirac impulse.
+ partitioner_.AddBuffer(input_interleaved.data(), kNumMonoChannels, 1);
+ partitioner_.Flush();
+
+ EXPECT_EQ(num_total_buffers_received_, 1U);
+ EXPECT_EQ(num_total_callbacks_triggered_, num_total_buffers_received_ + 1);
+ EXPECT_EQ(num_total_frames_received_, kNumFramesPerBuffer);
+
+ AudioBuffer reconstructed_buffer(kNumMonoChannels, kNumFramesPerBuffer);
+ FillAudioBuffer(total_received_buffer_interleaved_, kNumMonoChannels,
+ &reconstructed_buffer);
+
+ EXPECT_TRUE(CompareAudioBuffers(test_buffer[0], reconstructed_buffer[0],
+ kEpsilonFloat));
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_unpartitioner.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_unpartitioner.cc
new file mode 100644
index 000000000..fb4f51b36
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_unpartitioner.cc
@@ -0,0 +1,132 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "utils/buffer_unpartitioner.h"
+
+#include "utils/planar_interleaved_conversion.h"
+#include "utils/sample_type_conversion.h"
+
+namespace vraudio {
+
+BufferUnpartitioner::BufferUnpartitioner(size_t num_channels,
+ size_t frames_per_buffer,
+ GetBufferCallback buffer_callback)
+ : num_channels_(num_channels),
+ frames_per_buffer_(frames_per_buffer),
+ buffer_callback_(std::move(buffer_callback)),
+ current_input_buffer_(nullptr),
+ current_buffer_read_offset_frames_(0) {
+ DCHECK_GT(frames_per_buffer_, 0);
+ DCHECK_GT(num_channels, 0);
+}
+
+size_t BufferUnpartitioner::GetNumBuffersRequestedForNumInputFrames(
+ size_t num_output_frames) const {
+ if (num_output_frames == 0) {
+ return 0;
+ }
+ return (num_output_frames - GetNumFramesAvailableInBuffer() +
+ frames_per_buffer_ - 1) /
+ frames_per_buffer_;
+}
+
+size_t BufferUnpartitioner::GetBuffer(int16* output_buffer, size_t num_channels,
+ size_t num_frames) {
+ return GetBufferTemplated<int16*>(output_buffer, num_channels, num_frames);
+}
+
+size_t BufferUnpartitioner::GetBuffer(float* output_buffer, size_t num_channels,
+ size_t num_frames) {
+ return GetBufferTemplated<float*>(output_buffer, num_channels, num_frames);
+}
+
+size_t BufferUnpartitioner::GetBuffer(int16** output_buffer,
+ size_t num_channels, size_t num_frames) {
+ return GetBufferTemplated<int16**>(output_buffer, num_channels, num_frames);
+}
+
+size_t BufferUnpartitioner::GetBuffer(float** output_buffer,
+ size_t num_channels, size_t num_frames) {
+ return GetBufferTemplated<float**>(output_buffer, num_channels, num_frames);
+}
+
+size_t BufferUnpartitioner::GetNumBufferedFrames() const {
+ return current_buffer_read_offset_frames_;
+}
+
+size_t BufferUnpartitioner::GetNumFramesAvailableInBuffer() const {
+ if (current_input_buffer_ == nullptr) {
+ return 0;
+ }
+ DCHECK_GE(current_input_buffer_->num_frames(),
+ current_buffer_read_offset_frames_);
+ return current_input_buffer_->num_frames() -
+ current_buffer_read_offset_frames_;
+}
+
+void BufferUnpartitioner::Clear() {
+ current_input_buffer_ = nullptr;
+ current_buffer_read_offset_frames_ = 0;
+}
+
+template <typename BufferType>
+size_t BufferUnpartitioner::GetBufferTemplated(BufferType buffer,
+ size_t num_channels,
+ size_t num_frames) {
+ DCHECK_EQ(num_channels, num_channels_);
+
+ size_t num_copied_frames = 0;
+ while (num_copied_frames < num_frames) {
+ if (current_input_buffer_ == nullptr) {
+ current_input_buffer_ = buffer_callback_();
+ if (current_input_buffer_ == nullptr) {
+ // No more input |AudioBuffer|s are available.
+ return num_copied_frames;
+ }
+ DCHECK_EQ(frames_per_buffer_, current_input_buffer_->num_frames());
+ current_buffer_read_offset_frames_ = 0;
+ }
+ DCHECK_GE(frames_per_buffer_, current_buffer_read_offset_frames_);
+ const size_t remaining_frames_in_input_buffer =
+ num_frames - num_copied_frames;
+ DCHECK_GE(current_input_buffer_->num_frames(),
+ current_buffer_read_offset_frames_);
+ const size_t num_frames_to_process =
+ std::min(current_input_buffer_->num_frames() -
+ current_buffer_read_offset_frames_,
+ remaining_frames_in_input_buffer);
+
+ FillExternalBufferWithOffset(
+ *current_input_buffer_, current_buffer_read_offset_frames_, buffer,
+ num_frames, num_channels, num_copied_frames, num_frames_to_process);
+
+ num_copied_frames += num_frames_to_process;
+
+ current_buffer_read_offset_frames_ += num_frames_to_process;
+ if (current_buffer_read_offset_frames_ ==
+ current_input_buffer_->num_frames()) {
+ current_input_buffer_ = nullptr;
+ }
+ }
+ return num_copied_frames;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_unpartitioner.h b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_unpartitioner.h
new file mode 100644
index 000000000..51a5793d3
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_unpartitioner.h
@@ -0,0 +1,135 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_BUFFER_UNPARTITIONER_H_
+#define RESONANCE_AUDIO_UTILS_BUFFER_UNPARTITIONER_H_
+
+#include <functional>
+#include <memory>
+
+#include "base/audio_buffer.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+// Unpackages |AudioBuffer| into output buffers of arbitrary sizes.
+class BufferUnpartitioner {
+ public:
+ // Callback to obtain input |AudioBuffer|s. Returns a nullptr if no buffers
+ // are available.
+ typedef std::function<const AudioBuffer*()> GetBufferCallback;
+
+ // Constructor.
+ //
+ // @param num_channels Number of audio channels in input and output buffers.
+ // @param frames_per_buffer Number of frames per input buffer.
+ // @param buffer_callback Callback to receive output |AudioBuffer|s.
+ BufferUnpartitioner(size_t num_channels, size_t frames_per_buffer,
+ GetBufferCallback buffer_callback);
+
+ // Returns the number of input buffers required for a given number of output
+ // frames and based on the current fill state of the internal |temp_buffer_|.
+ //
+ // @param num_output_frames Number of output frames.
+ // @return Number of required input |AudioBuffer|s.
+ size_t GetNumBuffersRequestedForNumInputFrames(
+ size_t num_output_frames) const;
+
+ // Returns the number of buffered frames in internal |temp_buffer_|.
+ size_t GetNumBufferedFrames() const;
+
+ // Requests an interleaved int16 output buffer. This method triggers
+ // |GetBufferCallback|.
+ //
+ // @param output_buffer Interleaved output buffer to write to.
+ // @param num_channels Number of channels in output buffer.
+ // @param num_frames Number of frames in output buffer.
+ // @return Number of frames actually written.
+ size_t GetBuffer(int16* output_buffer, size_t num_channels,
+ size_t num_frames);
+
+ // Requests an interleaved float output buffer. This method triggers
+ // |GetBufferCallback|.
+ //
+ // @param output_buffer Interleaved output buffer to write to.
+ // @param num_channels Number of channels in output buffer.
+ // @param num_frames Number of frames in output buffer.
+ // @return Number of frames actually written.
+ size_t GetBuffer(float* output_buffer, size_t num_channels,
+ size_t num_frames);
+
+ // Requests a planar int16 output buffer. This method triggers
+ // |GetBufferCallback|.
+ //
+ // @param output_buffer Planar output buffer to write to.
+ // @param num_channels Number of channels in output buffer.
+ // @param num_frames Number of frames in output buffer.
+ // @return Number of frames actually written.
+ size_t GetBuffer(int16** output_buffer, size_t num_channels,
+ size_t num_frames);
+
+ // Requests a planar float output buffer. This method triggers
+ // |GetBufferCallback|.
+ //
+ // @param output_buffer Planar output buffer to write to.
+ // @param num_channels Number of channels in output buffer.
+ // @param num_frames Number of frames in output buffer.
+ // @return Number of frames actually written.
+ size_t GetBuffer(float** output_buffer, size_t num_channels,
+ size_t num_frames);
+
+ // Clears internal temporary buffer that holds remaining audio frames.
+ void Clear();
+
+ private:
+ // Returns the number of frames that are buffered in |current_input_buffer_|.
+ //
+ // @return Number of frames in |current_input_buffer_|. If
+ // |current_input_buffer_| is undefined, it returns 0.
+ size_t GetNumFramesAvailableInBuffer() const;
+
+ // Writes output buffer into target buffer. Supported buffer types are planar
+ // and interleaved floating point abd interleaved int16 output. This method
+ // triggers |GetBufferCallback|.
+ //
+ // @tparam BufferType Output buffer type.
+ // @param buffer Output buffer to write to.
+ // @param num_channels Number of channels in output buffer.
+ // @param num_frames Number of frames in output buffer.
+ // @return Number of frames actually written.
+ template <typename BufferType>
+ size_t GetBufferTemplated(BufferType buffer, size_t num_channels,
+ size_t num_frames);
+
+ // Number of channels in output buffers.
+ const size_t num_channels_;
+
+ // Number of frames per buffer in output buffers.
+ const size_t frames_per_buffer_;
+
+ // Callback to request input |AudioBuffer|s.
+ const GetBufferCallback buffer_callback_;
+
+ // Temporary buffer containing remaining audio frames.
+ const AudioBuffer* current_input_buffer_; // Not owned.
+
+ // Current read position in |current_input_buffer_|.
+ size_t current_buffer_read_offset_frames_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_BUFFER_UNPARTITIONER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_unpartitioner_test.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_unpartitioner_test.cc
new file mode 100644
index 000000000..48f5701fa
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/buffer_unpartitioner_test.cc
@@ -0,0 +1,294 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/buffer_unpartitioner.h"
+
+#include <memory>
+#include <numeric>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+#include "utils/planar_interleaved_conversion.h"
+#include "utils/sample_type_conversion.h"
+#include "utils/test_util.h"
+
+namespace vraudio {
+
+// Number of channels in test audio buffers.
+const size_t kNumChannels = vraudio::kNumStereoChannels;
+
+// Number of frames per test audio buffer.
+const size_t kNumFramesPerBuffer = 64;
+
+using vraudio::AudioBuffer;
+
+class BufferUnpartitionerTest : public ::testing::Test {
+ public:
+ // Generates a vector of int16 values with arbitrary data (vec[i] = i).
+ std::vector<int16> GenerateTestData(size_t num_channels, size_t num_frames) {
+ std::vector<int16> test_data(num_channels * num_frames);
+ std::iota(std::begin(test_data), std::end(test_data), 0);
+ return test_data;
+ }
+
+ // Generates an AudioBuffer from a segment of an interleaved std::vector.
+ AudioBuffer GetAudioBufferFromTestData(const std::vector<int16>& test_data,
+ size_t num_channels,
+ size_t num_frame_offset,
+ size_t num_audio_buffer_frames) {
+ const size_t num_input_frames = test_data.size() / num_channels;
+ AudioBuffer test_audio_buffer(num_channels, num_audio_buffer_frames);
+ FillAudioBufferWithOffset(&test_data[0], num_input_frames, num_channels,
+ num_frame_offset, 0 /* output_frame_offset */,
+ num_audio_buffer_frames, &test_audio_buffer);
+ return test_audio_buffer;
+ }
+
+ protected:
+ BufferUnpartitionerTest() {}
+ ~BufferUnpartitionerTest() override {}
+
+ const AudioBuffer* PassNextAudioBufferToBufferUnpartitioner(
+ size_t num_input_buffer_size_frames) {
+ input_audio_buffer_ = GetAudioBufferFromTestData(
+ input_audio_vector_, kNumChannels,
+ num_input_buffer_size_frames * num_callback_calls_,
+ num_input_buffer_size_frames);
+ ++num_callback_calls_;
+ return &input_audio_buffer_;
+ }
+
+ void InitBufferUnpartitioner(size_t num_total_frames_to_test,
+ size_t num_input_buffer_size_frames) {
+ input_audio_vector_ =
+ GenerateTestData(kNumChannels, num_total_frames_to_test);
+
+ num_callback_calls_ = 0;
+ unpartitioner_.reset(new BufferUnpartitioner(
+ kNumChannels, num_input_buffer_size_frames,
+ std::bind(
+ &BufferUnpartitionerTest::PassNextAudioBufferToBufferUnpartitioner,
+ this, num_input_buffer_size_frames)));
+ }
+
+ // Returns the number of triggered callback calls.
+ template <typename T>
+ size_t TestInterleavedBufferOutputTest(size_t num_input_buffer_size_frames,
+ size_t num_output_buffer_size_frames,
+ size_t num_total_frames_to_test,
+ float buffer_epsilon) {
+ InitBufferUnpartitioner(num_total_frames_to_test,
+ num_input_buffer_size_frames);
+
+ std::vector<T> output_vector(input_audio_vector_.size(), static_cast<T>(0));
+ for (size_t b = 0;
+ b < num_total_frames_to_test / num_output_buffer_size_frames; ++b) {
+ EXPECT_EQ(
+ num_output_buffer_size_frames,
+ unpartitioner_->GetBuffer(
+ &output_vector[b * num_output_buffer_size_frames * kNumChannels],
+ kNumChannels, num_output_buffer_size_frames));
+ }
+
+ AudioBuffer input(kNumChannels, num_total_frames_to_test);
+ FillAudioBuffer(input_audio_vector_, kNumChannels, &input);
+
+ AudioBuffer output(kNumChannels, num_total_frames_to_test);
+ FillAudioBuffer(output_vector, kNumChannels, &output);
+
+ for (size_t channel = 0; channel < kNumChannels; ++channel) {
+ EXPECT_TRUE(
+ CompareAudioBuffers(input[channel], output[channel], buffer_epsilon));
+ }
+
+ EXPECT_EQ(unpartitioner_->GetNumBuffersRequestedForNumInputFrames(
+ num_total_frames_to_test),
+ num_callback_calls_);
+ return num_callback_calls_;
+ }
+
+ // Returns the number of triggered callback calls.
+ template <typename T>
+ size_t TestPlanarBufferOutputTest(size_t input_buffer_size_frames,
+ size_t num_output_buffer_size_frames,
+ size_t num_total_frames_to_test,
+ float buffer_epsilon) {
+ InitBufferUnpartitioner(num_total_frames_to_test, input_buffer_size_frames);
+
+ std::vector<std::vector<T>> planar_output_vector(
+ kNumChannels,
+ std::vector<T>(num_total_frames_to_test, static_cast<T>(0)));
+ std::vector<T*> planar_output_vector_ptrs(kNumChannels);
+ for (size_t channel = 0; channel < kNumChannels; ++channel) {
+ planar_output_vector_ptrs[channel] = &planar_output_vector[channel][0];
+ }
+
+ const size_t num_total_buffers =
+ num_total_frames_to_test / num_output_buffer_size_frames;
+ for (size_t buffer = 0; buffer < num_total_buffers; ++buffer) {
+ EXPECT_EQ(num_output_buffer_size_frames,
+ unpartitioner_->GetBuffer(planar_output_vector_ptrs.data(),
+ kNumChannels,
+ num_output_buffer_size_frames));
+ for (T*& planar_output_vector_ptr : planar_output_vector_ptrs) {
+ planar_output_vector_ptr += num_output_buffer_size_frames;
+ }
+ }
+ AudioBuffer input(kNumChannels, num_total_frames_to_test);
+ FillAudioBuffer(input_audio_vector_, kNumChannels, &input);
+
+ AudioBuffer output(kNumChannels, num_total_frames_to_test);
+ for (size_t channel = 0; channel < kNumChannels; ++channel) {
+ ConvertPlanarSamples(num_total_frames_to_test,
+ &planar_output_vector[channel][0],
+ &output[channel][0]);
+ }
+
+ for (size_t channel = 0; channel < kNumChannels; ++channel) {
+ EXPECT_TRUE(
+ CompareAudioBuffers(input[channel], output[channel], buffer_epsilon));
+ }
+
+ EXPECT_EQ(unpartitioner_->GetNumBuffersRequestedForNumInputFrames(
+ num_total_frames_to_test),
+ num_callback_calls_);
+ return num_callback_calls_;
+ }
+
+ size_t num_callback_calls_;
+ AudioBuffer input_audio_buffer_;
+ std::vector<int16> input_audio_vector_;
+
+ std::unique_ptr<BufferUnpartitioner> unpartitioner_;
+};
+
+TEST_F(BufferUnpartitionerTest, TestInterleavedBufferOutputTest) {
+ const size_t kNumInputBuffers = 8;
+ EXPECT_EQ(kNumInputBuffers,
+ TestInterleavedBufferOutputTest<int16>(
+ kNumFramesPerBuffer / kNumInputBuffers /* input_buffer_size */,
+ kNumFramesPerBuffer /* output_buffer_size */,
+ kNumFramesPerBuffer /* num_frames_to_test */,
+ 1e-4f /* buffer_epsilon */));
+
+ EXPECT_EQ(kNumInputBuffers,
+ TestInterleavedBufferOutputTest<float>(
+ kNumFramesPerBuffer / kNumInputBuffers /* input_buffer_size */,
+ kNumFramesPerBuffer /* output_buffer_size */,
+ kNumFramesPerBuffer /* num_frames_to_test */,
+ 1e-6f /* buffer_epsilon */));
+
+ EXPECT_EQ(1U, // Single callback expected.
+ TestInterleavedBufferOutputTest<int16>(
+ kNumFramesPerBuffer /* input_buffer_size */,
+ kNumFramesPerBuffer / kNumInputBuffers /* output_buffer_size */,
+ kNumFramesPerBuffer /* num_frames_to_test */,
+ 1e-4f /* buffer_epsilon */));
+
+ EXPECT_EQ(1U, // Single callback expected.
+ TestInterleavedBufferOutputTest<float>(
+ kNumFramesPerBuffer /* input_buffer_size */,
+ kNumFramesPerBuffer / kNumInputBuffers /* output_buffer_size */,
+ kNumFramesPerBuffer /* num_frames_to_test */,
+ 1e-6f /* buffer_epsilon */));
+}
+
+TEST_F(BufferUnpartitionerTest, TestPlanarBufferOutputTest) {
+ const size_t kNumInputBuffers = 8;
+ EXPECT_EQ(kNumInputBuffers,
+ TestPlanarBufferOutputTest<int16>(
+ kNumFramesPerBuffer / kNumInputBuffers /* input_buffer_size */,
+ kNumFramesPerBuffer /* output_buffer_size */,
+ kNumFramesPerBuffer /* num_frames_to_test */,
+ 5e-3f /* buffer_epsilon */));
+
+ EXPECT_EQ(kNumInputBuffers,
+ TestPlanarBufferOutputTest<float>(
+ kNumFramesPerBuffer / kNumInputBuffers /* input_buffer_size */,
+ kNumFramesPerBuffer /* output_buffer_size */,
+ kNumFramesPerBuffer /* num_frames_to_test */,
+ 1e-6f /* buffer_epsilon */));
+
+ EXPECT_EQ(1U, // Single callback expected.
+ TestPlanarBufferOutputTest<int16>(
+ kNumFramesPerBuffer /* input_buffer_size */,
+ kNumFramesPerBuffer / kNumInputBuffers /* output_buffer_size */,
+ kNumFramesPerBuffer /* num_frames_to_test */,
+ 5e-3f /* buffer_epsilon */));
+
+ EXPECT_EQ(1U, // Single callback expected.
+ TestPlanarBufferOutputTest<float>(
+ kNumFramesPerBuffer /* input_buffer_size */,
+ kNumFramesPerBuffer / kNumInputBuffers /* output_buffer_size */,
+ kNumFramesPerBuffer /* num_frames_to_test */,
+ 1e-6f /* buffer_epsilon */));
+}
+
+TEST_F(BufferUnpartitionerTest, GetNumBuffersRequestedForNumInputFramesTest) {
+ AudioBuffer input_audio_buffer(kNumChannels, kNumFramesPerBuffer);
+ size_t num_callback_calls = 0;
+ const auto input_callback = [this, &input_audio_buffer,
+ &num_callback_calls]() -> AudioBuffer* {
+ ++num_callback_calls;
+ return &input_audio_buffer;
+ };
+ unpartitioner_.reset(new BufferUnpartitioner(
+ kNumChannels, kNumFramesPerBuffer, input_callback));
+
+ EXPECT_EQ(unpartitioner_->GetNumBuffersRequestedForNumInputFrames(0), 0U);
+ EXPECT_EQ(unpartitioner_->GetNumBuffersRequestedForNumInputFrames(1), 1U);
+ EXPECT_EQ(unpartitioner_->GetNumBuffersRequestedForNumInputFrames(
+ kNumFramesPerBuffer - 1),
+ 1U);
+ EXPECT_EQ(unpartitioner_->GetNumBuffersRequestedForNumInputFrames(
+ kNumFramesPerBuffer),
+ 1U);
+ EXPECT_EQ(unpartitioner_->GetNumBuffersRequestedForNumInputFrames(
+ kNumFramesPerBuffer + 1),
+ 2U);
+
+ const auto GetLargeRandomFrameNumber = [](size_t i) -> size_t {
+ return i * 7 * kNumFramesPerBuffer + 13 * i;
+ };
+
+ const size_t kMaximumInputSize =
+ kNumChannels * GetLargeRandomFrameNumber(kNumFramesPerBuffer * 2);
+ std::vector<float> input(kMaximumInputSize);
+
+ for (size_t i = 0; i < kNumFramesPerBuffer * 2; ++i) {
+ // Reset unpartitioner.
+ unpartitioner_->Clear();
+
+ // Simulate initial read of |i| frames.
+ EXPECT_EQ(i, unpartitioner_->GetBuffer(&input[0], kNumChannels, i));
+
+ const size_t large_random_frame_number = GetLargeRandomFrameNumber(i);
+ const size_t expected_num_buffer_requests =
+ unpartitioner_->GetNumBuffersRequestedForNumInputFrames(
+ large_random_frame_number);
+
+ num_callback_calls = 0;
+ EXPECT_EQ(large_random_frame_number,
+ unpartitioner_->GetBuffer(&input[0], kNumChannels,
+ large_random_frame_number));
+
+ EXPECT_EQ(expected_num_buffer_requests, num_callback_calls);
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/lockless_task_queue.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/lockless_task_queue.cc
new file mode 100644
index 000000000..3edb75dc3
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/lockless_task_queue.cc
@@ -0,0 +1,156 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/lockless_task_queue.h"
+
+#include <limits>
+
+#include "base/logging.h"
+
+namespace vraudio {
+
+namespace {
+
+// Reserved index representing an invalid list index.
+constexpr uint64_t kInvalidIndex = std::numeric_limits<uint32_t>::max();
+
+// Maximum number of producers.
+constexpr uint64_t kMaxProducers = kInvalidIndex - 1;
+
+} // namespace
+
+LocklessTaskQueue::LocklessTaskQueue(size_t max_tasks) {
+ CHECK_GT(max_tasks, 0U);
+ CHECK_LE(max_tasks, kMaxProducers);
+ Init(max_tasks);
+}
+
+LocklessTaskQueue::~LocklessTaskQueue() { Clear(); }
+
+void LocklessTaskQueue::Post(Task&& task) {
+ const TagAndIndex free_node_idx = PopNodeFromList(&free_list_head_idx_);
+ if (GetIndex(free_node_idx) == kInvalidIndex) {
+ LOG(WARNING) << "Queue capacity reached - dropping task";
+ return;
+ }
+ nodes_[GetIndex(free_node_idx)].task = std::move(task);
+ PushNodeToList(&task_list_head_idx_, free_node_idx);
+}
+
+void LocklessTaskQueue::Execute() {
+ const TagAndIndex old_flag_with_invalid_index =
+ (GetFlag(task_list_head_idx_) << 32) + kInvalidIndex;
+ const TagAndIndex old_task_list_head_idx =
+ task_list_head_idx_.exchange(old_flag_with_invalid_index);
+ ProcessTaskList(old_task_list_head_idx, true /*execute_tasks*/);
+}
+
+void LocklessTaskQueue::Clear() {
+ const TagAndIndex old_flag_with_invalid_index =
+ (GetFlag(task_list_head_idx_) << 32) + kInvalidIndex;
+ const TagAndIndex old_task_list_head_idx =
+ task_list_head_idx_.exchange(old_flag_with_invalid_index);
+ ProcessTaskList(old_task_list_head_idx, false /*execute_tasks*/);
+}
+
+LocklessTaskQueue::TagAndIndex LocklessTaskQueue::IncreaseTag(
+ TagAndIndex tag_and_index) {
+ // The most significant 32 bits a reserved for tagging. Overflows are
+ // acceptable.
+ return tag_and_index + (static_cast<uint64_t>(1) << 32);
+}
+
+LocklessTaskQueue::TagAndIndex LocklessTaskQueue::GetIndex(
+ TagAndIndex tag_and_index) {
+ // The least significant 32 bits a reserved for the index.
+ return tag_and_index & std::numeric_limits<uint32_t>::max();
+}
+
+// Extracts the flag in the most significant 32 bits from a TagAndIndex;
+LocklessTaskQueue::TagAndIndex LocklessTaskQueue::GetFlag(
+ TagAndIndex tag_and_index) {
+ // The most significant 32 bits a reserved for the flag.
+ return tag_and_index >> 32;
+}
+
+void LocklessTaskQueue::PushNodeToList(
+ std::atomic<TagAndIndex>* list_head_idx_ptr, TagAndIndex node_idx) {
+ DCHECK(list_head_idx_ptr);
+ TagAndIndex list_head_idx;
+ do {
+ list_head_idx = list_head_idx_ptr->load();
+ nodes_[GetIndex(node_idx)].next = list_head_idx;
+ } while (!std::atomic_compare_exchange_strong(list_head_idx_ptr,
+ &list_head_idx, node_idx));
+}
+
+LocklessTaskQueue::TagAndIndex LocklessTaskQueue::PopNodeFromList(
+ std::atomic<TagAndIndex>* list_head_idx_ptr) {
+ DCHECK(list_head_idx_ptr);
+ TagAndIndex list_head_idx;
+ TagAndIndex list_head_next;
+ do {
+ list_head_idx = list_head_idx_ptr->load();
+ if (GetIndex(list_head_idx) == kInvalidIndex) {
+ // End of list reached.
+ return kInvalidIndex;
+ }
+ list_head_next = nodes_[GetIndex(list_head_idx)].next;
+ } while (!std::atomic_compare_exchange_strong(
+ list_head_idx_ptr, &list_head_idx, list_head_next));
+ return IncreaseTag(list_head_idx);
+}
+
+void LocklessTaskQueue::ProcessTaskList(TagAndIndex list_head_idx,
+ bool execute) {
+ TagAndIndex node_itr = list_head_idx;
+ while (GetIndex(node_itr) != kInvalidIndex) {
+ Node* node = &nodes_[GetIndex(node_itr)];
+ TagAndIndex next_node = node->next;
+ temp_tasks_.emplace_back(std::move(node->task));
+ node->task = nullptr;
+ PushNodeToList(&free_list_head_idx_, node_itr);
+ node_itr = next_node;
+ }
+
+ if (execute) {
+ // Execute tasks in reverse order.
+ for (std::vector<Task>::reverse_iterator task_itr = temp_tasks_.rbegin();
+ task_itr != temp_tasks_.rend(); ++task_itr) {
+ if (*task_itr != nullptr) {
+ (*task_itr)();
+ }
+ }
+ }
+ temp_tasks_.clear();
+}
+
+void LocklessTaskQueue::Init(size_t num_nodes) {
+ nodes_.resize(num_nodes);
+ temp_tasks_.reserve(num_nodes);
+
+ // Initialize free list.
+ free_list_head_idx_ = 0;
+ for (size_t i = 0; i < num_nodes - 1; ++i) {
+ nodes_[i].next = i + 1;
+ }
+ nodes_[num_nodes - 1].next = kInvalidIndex;
+
+ // Initialize task list.
+ task_list_head_idx_ = kInvalidIndex;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/lockless_task_queue.h b/src/3rdparty/resonance-audio/resonance_audio/utils/lockless_task_queue.h
new file mode 100644
index 000000000..e041c1fa4
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/lockless_task_queue.h
@@ -0,0 +1,124 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_LOCKLESS_TASK_QUEUE_H_
+#define RESONANCE_AUDIO_UTILS_LOCKLESS_TASK_QUEUE_H_
+
+#include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <vector>
+
+namespace vraudio {
+
+// Lock-less task queue which is thread safe for concurrent task producers and
+// single task consumers.
+class LocklessTaskQueue {
+ public:
+ // Alias for the task closure type.
+ typedef std::function<void()> Task;
+
+ // Constructor. Preallocates nodes on the task queue list.
+ //
+ // @param max_tasks Maximum number of tasks on the task queue.
+ explicit LocklessTaskQueue(size_t max_tasks);
+
+ ~LocklessTaskQueue();
+
+ // Posts a new task to task queue.
+ //
+ // @param task Task to process.
+ void Post(Task&& task);
+
+ // Executes all tasks on the task queue.
+ void Execute();
+
+ // Removes all tasks on the task queue.
+ void Clear();
+
+ private:
+ // To prevent ABA problems during thread synchronization, the most significant
+ // 32 bits of this index type are reserved for a continuously increasing
+ // tag counter. This prevents cases where nodes on the head appears to be
+ // untouched during the preparation of a push operation but instead they have
+ // been popped and pushed back during a context switch.
+ typedef uint64_t TagAndIndex;
+
+ // Node to model a single-linked list.
+ struct Node {
+ Node() = default;
+
+ // Dummy copy constructor to enable vector::resize allocation.
+ Node(const Node& node) : next() {}
+
+ // User task.
+ LocklessTaskQueue::Task task;
+
+ // Index to next node.
+ std::atomic<TagAndIndex> next;
+ };
+
+ // Returned a TagAndIndex with increased tag.
+ TagAndIndex IncreaseTag(TagAndIndex tag_and_index);
+
+ // Extracts the index in the least significant 32 bits from a TagAndIndex.
+ TagAndIndex GetIndex(TagAndIndex tag_and_index);
+
+ // Extracts the flag in the most significant 32 bits from a TagAndIndex.
+ TagAndIndex GetFlag(TagAndIndex tag_and_index);
+
+ // Pushes a node to the front of a list.
+ //
+ // @param list_head Index to list head.
+ // @param node Index of node to be pushed to the front of the list.
+ void PushNodeToList(std::atomic<TagAndIndex>* list_head, TagAndIndex node);
+
+ // Pops a node from the front of a list.
+ //
+ // @param list_head Index to list head.
+ // @return Index of front node, kInvalidIndex if list is empty.
+ TagAndIndex PopNodeFromList(std::atomic<TagAndIndex>* list_head);
+
+ // Iterates over list and moves all tasks to |temp_tasks_| to be executed in
+ // FIFO order. All processed nodes are pushed back to the free list.
+ //
+ // @param list_head Index of head node of list to be processed.
+ // @param execute If true, tasks on task list are executed.
+ void ProcessTaskList(TagAndIndex list_head, bool execute);
+
+ // Initializes task queue structures and preallocates task queue nodes.
+ //
+ // @param num_nodes Number of nodes to be initialized on free list.
+ void Init(size_t num_nodes);
+
+ // Index to head node of free list.
+ std::atomic<TagAndIndex> free_list_head_idx_;
+
+ // Index to head node of task list.
+ std::atomic<TagAndIndex> task_list_head_idx_;
+
+ // Holds preallocated nodes.
+ std::vector<Node> nodes_;
+
+ // Temporary vector to hold |Task|s in order to execute them in reverse order
+ // (FIFO, instead of LIFO).
+ std::vector<Task> temp_tasks_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_LOCKLESS_TASK_QUEUE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/lockless_task_queue_test.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/lockless_task_queue_test.cc
new file mode 100644
index 000000000..3168a92a1
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/lockless_task_queue_test.cc
@@ -0,0 +1,215 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/lockless_task_queue.h"
+
+#include <atomic>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+namespace {
+// Number of task producer threads.
+static const size_t kNumTaskProducers = 5;
+
+// Atomic thread counter used to trigger a simultaneous execution of all
+// threads.
+static std::atomic<size_t> s_thread_count(0);
+
+// Waits until all threads are initialized.
+static void WaitForProducerThreads() {
+ static std::mutex mutex;
+ static std::condition_variable cond_var;
+ std::unique_lock<std::mutex> lock(mutex);
+
+ if (++s_thread_count < kNumTaskProducers) {
+ cond_var.wait(lock);
+ } else {
+ cond_var.notify_all();
+ }
+}
+
+static void IncVectorAtIndex(std::vector<size_t>* work_vector_ptr,
+ size_t index) {
+ ++(*work_vector_ptr)[index];
+}
+
+class TaskProducer {
+ public:
+ TaskProducer(LocklessTaskQueue* queue, std::vector<size_t>* work_vector_ptr,
+ int delay_ms)
+ : producer_thread_(new std::thread(std::bind(
+ &TaskProducer::Produce, this, queue, work_vector_ptr, delay_ms))) {
+ }
+
+ TaskProducer(TaskProducer&& task)
+ : producer_thread_(std::move(task.producer_thread_)) {}
+
+ void Join() {
+ if (producer_thread_->joinable()) {
+ producer_thread_->join();
+ }
+ }
+
+ private:
+ void Produce(LocklessTaskQueue* queue, std::vector<size_t>* work_vector_ptr,
+ int delay_ms) {
+ WaitForProducerThreads();
+
+ for (size_t i = 0; i < work_vector_ptr->size(); ++i) {
+ queue->Post(std::bind(IncVectorAtIndex, work_vector_ptr, i));
+ if (delay_ms > 0) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
+ }
+ }
+ }
+ std::unique_ptr<std::thread> producer_thread_;
+};
+
+} // namespace
+
+class LocklessTaskQueueTest : public ::testing::Test {
+ protected:
+ // Virtual methods from ::testing::Test
+ ~LocklessTaskQueueTest() override {}
+ void SetUp() override {}
+ void TearDown() override {}
+
+ // Helper method to initialize and run the concurrency test with multiple
+ // task producers and a single task executor.
+ void ConcurrentThreadsMultipleTaskProducerSingleTaskExecutorTest(
+ int producer_delay_ms) {
+ s_thread_count = 0;
+ const size_t kTasksPerProducer = 50;
+ work_vector_.resize(kNumTaskProducers);
+ std::fill(work_vector_.begin(), work_vector_.end(), 0);
+
+ LocklessTaskQueue task_queue(kNumTaskProducers * kTasksPerProducer);
+
+ std::vector<TaskProducer> task_producer_tasks;
+ for (size_t i = 0; i < kNumTaskProducers; ++i) {
+ task_producer_tasks.emplace_back(&task_queue, &work_vector_,
+ producer_delay_ms);
+ }
+ WaitForProducerThreads();
+ task_queue.Execute();
+
+ for (auto& producer : task_producer_tasks) {
+ producer.Join();
+ }
+ task_queue.Execute();
+
+ for (size_t worker_count : work_vector_) {
+ EXPECT_EQ(worker_count, kNumTaskProducers);
+ }
+ }
+
+ std::vector<size_t> work_vector_;
+};
+
+TEST_F(LocklessTaskQueueTest, MaxTasks) {
+ LocklessTaskQueue task_queue(1);
+
+ work_vector_.resize(1, 0);
+
+ task_queue.Execute();
+
+ task_queue.Post(std::bind(IncVectorAtIndex, &work_vector_, 0));
+ // Second task should be dropped since queue is initialized with size 1.
+ task_queue.Post(std::bind(IncVectorAtIndex, &work_vector_, 0));
+ task_queue.Execute();
+
+ EXPECT_EQ(work_vector_[0], 1U);
+
+ task_queue.Post(std::bind(IncVectorAtIndex, &work_vector_, 0));
+ // Second task should be dropped since queue is initialized with size 1.
+ task_queue.Post(std::bind(IncVectorAtIndex, &work_vector_, 0));
+ task_queue.Execute();
+
+ EXPECT_EQ(work_vector_[0], 2U);
+}
+
+TEST_F(LocklessTaskQueueTest, Clear) {
+ LocklessTaskQueue task_queue(1);
+
+ work_vector_.resize(1, 0);
+
+ task_queue.Post(std::bind(IncVectorAtIndex, &work_vector_, 0));
+ task_queue.Clear();
+ task_queue.Execute();
+
+ EXPECT_EQ(work_vector_[0], 0U);
+}
+
+TEST_F(LocklessTaskQueueTest, SynchronousTaskExecution) {
+ const size_t kNumRounds = 5;
+ const size_t kNumTasksPerRound = 20;
+
+ LocklessTaskQueue task_queue(kNumTasksPerRound);
+
+ work_vector_.resize(kNumTasksPerRound, 0);
+
+ for (size_t r = 0; r < kNumRounds; ++r) {
+ for (size_t t = 0; t < kNumTasksPerRound; ++t) {
+ task_queue.Post(std::bind(IncVectorAtIndex, &work_vector_, t));
+ }
+ task_queue.Execute();
+ }
+
+ for (size_t t = 0; t < kNumTasksPerRound; ++t) {
+ EXPECT_EQ(work_vector_[t], kNumRounds);
+ }
+}
+
+TEST_F(LocklessTaskQueueTest, SynchronousInOrderTaskExecution) {
+ const size_t kNumTasksPerRound = 20;
+
+ LocklessTaskQueue task_queue(kNumTasksPerRound);
+
+ work_vector_.resize(kNumTasksPerRound, 0);
+ work_vector_[0] = 1;
+
+ const auto accumulate_from_lower_index_task = [this](size_t index) {
+ work_vector_[index] += work_vector_[index - 1];
+ };
+ for (size_t t = 1; t < kNumTasksPerRound; ++t) {
+ task_queue.Post(std::bind(accumulate_from_lower_index_task, t));
+ }
+ task_queue.Execute();
+
+ for (size_t t = 0; t < kNumTasksPerRound; ++t) {
+ EXPECT_EQ(work_vector_[t], 1U);
+ }
+}
+
+// Tests concurrency of multiple producers and a single executor.
+TEST_F(LocklessTaskQueueTest,
+ ConcurrentThreadsMultipleFastProducersSingleExecutor) {
+ // Test fast producers and a fast consumer.
+ ConcurrentThreadsMultipleTaskProducerSingleTaskExecutorTest(
+ 0 /* producer delay in ms */);
+ ConcurrentThreadsMultipleTaskProducerSingleTaskExecutorTest(
+ 1 /* producer delay in ms */);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/ogg_vorbis_recorder.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/ogg_vorbis_recorder.cc
new file mode 100644
index 000000000..26596abab
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/ogg_vorbis_recorder.cc
@@ -0,0 +1,107 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/ogg_vorbis_recorder.h"
+
+#include "base/logging.h"
+#include "utils/planar_interleaved_conversion.h"
+
+namespace vraudio {
+
+OggVorbisRecorder::OggVorbisRecorder(int sample_rate_hz, size_t num_channels,
+ size_t num_frames, size_t max_num_buffers)
+ : sample_rate_hz_(sample_rate_hz),
+ num_channels_(num_channels),
+ num_frames_(num_frames),
+ max_num_buffers_(max_num_buffers),
+ temp_data_channel_ptrs_(num_channels),
+ crossfader_(num_frames_),
+ crossfade_buffer_(num_channels_, num_frames_) {
+ DCHECK_GT(sample_rate_hz_, 0);
+ DCHECK_NE(num_channels_, 0U);
+ DCHECK_NE(num_frames_, 0U);
+ CHECK_NE(max_num_buffers_, 0U);
+}
+
+void OggVorbisRecorder::AddInput(std::unique_ptr<AudioBuffer> input_buffer) {
+ DCHECK(input_buffer);
+ DCHECK_EQ(input_buffer->num_channels(), num_channels_);
+ DCHECK_EQ(input_buffer->num_frames(), num_frames_);
+
+ if (data_.size() == max_num_buffers_) {
+ LOG(WARNING) << "Maximum input buffer limit reached, overwriting data";
+ data_.erase(data_.begin());
+ }
+ data_.push_back(std::move(input_buffer));
+}
+
+void OggVorbisRecorder::Reset() { data_.clear(); }
+
+bool OggVorbisRecorder::WriteToFile(const std::string& file_path, float quality,
+ bool seamless) {
+ if (data_.empty()) {
+ LOG(WARNING) << "No recorded data";
+ return false;
+ }
+
+ if (seamless) {
+ MakeSeamless();
+ }
+
+ if (!encoder_.InitializeForFile(
+ file_path, num_channels_, sample_rate_hz_,
+ VorbisStreamEncoder::EncodingMode::kVariableBitRate, 0 /* bitrate */,
+ quality)) {
+ LOG(WARNING) << "Cannot initialize file to record: " << file_path;
+ Reset();
+ return false;
+ }
+ for (const auto& audio_buffer : data_) {
+ GetRawChannelDataPointersFromAudioBuffer(*audio_buffer,
+ &temp_data_channel_ptrs_);
+ if (!encoder_.AddPlanarBuffer(temp_data_channel_ptrs_.data(), num_channels_,
+ num_frames_)) {
+ LOG(WARNING) << "Failed to write buffer into file: " << file_path;
+ Reset();
+ return false;
+ }
+ }
+ if (!encoder_.FlushAndClose()) {
+ LOG(WARNING) << "Failed to safely close file: " << file_path;
+ Reset();
+ return false;
+ }
+
+ Reset();
+ return true;
+}
+
+void OggVorbisRecorder::MakeSeamless() {
+ if (data_.size() == 1) {
+ LOG(WARNING) << "Not enough data to make seamless file";
+ return;
+ }
+ // Apply crossfade between the beginning and the end buffers of |data_|.
+ auto* front_buffer = data_.front().get();
+ const auto& back_buffer = *data_.back();
+ crossfader_.ApplyLinearCrossfade(*front_buffer, back_buffer,
+ &crossfade_buffer_);
+
+ *front_buffer = crossfade_buffer_;
+ data_.pop_back();
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/ogg_vorbis_recorder.h b/src/3rdparty/resonance-audio/resonance_audio/utils/ogg_vorbis_recorder.h
new file mode 100644
index 000000000..2e47947ff
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/ogg_vorbis_recorder.h
@@ -0,0 +1,94 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_OGG_VORBIS_RECORDER_H_
+#define RESONANCE_AUDIO_UTILS_OGG_VORBIS_RECORDER_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/audio_buffer.h"
+#include "utils/buffer_crossfader.h"
+#include "utils/vorbis_stream_encoder.h"
+
+namespace vraudio {
+
+// Class that takes audio buffers as input and writes them into a compressed OGG
+// Vorbis file.
+
+class OggVorbisRecorder {
+ public:
+ // Constructs a new recorder with given sampling rate and number of channels.
+
+ OggVorbisRecorder(int sample_rate_hz, size_t num_channels, size_t num_frames,
+ size_t max_num_buffers);
+
+ // Adds next input buffer at the end of the record data.
+ //
+ // @param input_buffer Next audio buffer to be recorded.
+ void AddInput(std::unique_ptr<AudioBuffer> input_buffer);
+
+ // Flushes the record data.
+ void Reset();
+
+ // Writes the current record data into a file and resets the recorder.
+ //
+ // @param file_path Full path of the file to be recorded into.
+ // @param quality Compression quality of the record. The usable range is from
+ // -0.1 (lowest quality, smallest file) to 1.0 (highest quality, largest
+ // file).
+ // @param seamless True to record seamlessly for looping. Note that this
+ // option will truncate the record length by |num_frames_| samples.
+ // @return False if fails to successfully write data into |file_path|.
+ bool WriteToFile(const std::string& file_path, float quality, bool seamless);
+
+ private:
+ // Helper method to make record data seamless.
+ void MakeSeamless();
+
+ // Record sampling rate.
+ const int sample_rate_hz_;
+
+ // Record number of channels.
+ const size_t num_channels_;
+
+ // Record number of frames per buffer.
+ const size_t num_frames_;
+
+ // Maximum number of input buffers allowed to record.
+ const size_t max_num_buffers_;
+
+ // Record data that is stored as a list of planar audio buffers.
+ std::vector<std::unique_ptr<AudioBuffer>> data_;
+
+ // Temporary vector to extract pointers to planar channels in an
+ // |AudioBuffer|.
+ std::vector<const float*> temp_data_channel_ptrs_;
+
+ // Buffer crossfader that is used to create seamless loop.
+ BufferCrossfader crossfader_;
+
+ // Temporary buffer to store the crossfaded output.
+
+ AudioBuffer crossfade_buffer_;
+
+ // OGG Vorbis encoder to write record data into file in compressed format.
+ VorbisStreamEncoder encoder_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_OGG_VORBIS_RECORDER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/planar_interleaved_conversion.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/planar_interleaved_conversion.cc
new file mode 100644
index 000000000..be50b23f7
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/planar_interleaved_conversion.cc
@@ -0,0 +1,505 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "utils/planar_interleaved_conversion.h"
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/simd_utils.h"
+
+#include "utils/sample_type_conversion.h"
+
+namespace vraudio {
+
+namespace {
+
+template <typename InputType, typename OutputType>
+void ConvertInterleavedToPlanarTemplated(
+ InputType interleaved_buffer, size_t num_input_frames,
+ size_t num_input_channels, size_t input_offset_frames,
+ const std::vector<size_t>* channel_map, OutputType output_buffer,
+ size_t num_output_frames, size_t num_output_channels,
+ size_t output_offset_frames, size_t num_frames_to_copy) {
+ DCHECK_GE(num_input_frames, input_offset_frames);
+ const size_t max_num_input_frames = num_input_frames - input_offset_frames;
+ DCHECK_GE(num_output_frames, output_offset_frames);
+ const size_t max_num_output_frames = num_output_frames - output_offset_frames;
+ DCHECK_GE(max_num_input_frames, num_frames_to_copy);
+ DCHECK_GE(max_num_output_frames, num_frames_to_copy);
+
+ if (channel_map == nullptr) {
+ DCHECK_EQ(num_input_channels, num_output_channels);
+ } else {
+ DCHECK_GE(channel_map->size(), num_output_channels);
+ }
+
+ InputType interleaved_buffer_with_offset =
+ interleaved_buffer + input_offset_frames * num_input_channels;
+
+ if (num_input_channels == kNumStereoChannels &&
+ num_output_channels == kNumStereoChannels) {
+ if (channel_map == nullptr) {
+ DeinterleaveStereo(num_frames_to_copy, interleaved_buffer_with_offset,
+ &output_buffer[0][output_offset_frames],
+ &output_buffer[1][output_offset_frames]);
+ } else {
+ DCHECK_LT((*channel_map)[0], kNumStereoChannels);
+ DCHECK_LT((*channel_map)[1], kNumStereoChannels);
+ DeinterleaveStereo(
+ num_input_frames, interleaved_buffer_with_offset,
+ &output_buffer[(*channel_map)[0]][output_offset_frames],
+ &output_buffer[(*channel_map)[1]][output_offset_frames]);
+ }
+ } else {
+ for (size_t channel_idx = 0; channel_idx < num_output_channels;
+ ++channel_idx) {
+ const size_t input_channel =
+ channel_map != nullptr ? (*channel_map)[channel_idx] : channel_idx;
+ DCHECK_LT(input_channel, num_input_channels);
+ InputType input_ptr = &interleaved_buffer_with_offset[input_channel];
+ float* output_ptr = &output_buffer[channel_idx][output_offset_frames];
+ for (size_t frame = 0; frame < num_frames_to_copy; ++frame) {
+ ConvertSampleToFloatFormat(*input_ptr, output_ptr);
+ input_ptr += num_input_channels;
+ ++output_ptr;
+ }
+ }
+ }
+}
+
+template <typename PlanarInputType, typename PlanarOutputType>
+void ConvertPlanarToPlanarTemplated(
+ PlanarInputType input, size_t num_input_frames, size_t num_input_channels,
+ size_t input_offset_frames, const std::vector<size_t>* channel_map,
+ PlanarOutputType planar_output_ptrs, size_t num_output_frames,
+ size_t num_output_channels, size_t output_offset_frames,
+ size_t num_frames_convert_and_copy) {
+ DCHECK_GE(num_input_frames, input_offset_frames);
+ const size_t max_num_input_frames = num_input_frames - input_offset_frames;
+ DCHECK_GE(num_output_frames, output_offset_frames);
+ const size_t max_num_output_frames = num_output_frames - output_offset_frames;
+ DCHECK_GE(max_num_input_frames, num_frames_convert_and_copy);
+ DCHECK_GE(max_num_output_frames, num_frames_convert_and_copy);
+
+ if (channel_map == nullptr) {
+ DCHECK_EQ(num_input_channels, num_output_channels);
+ } else {
+ DCHECK_GE(channel_map->size(), num_output_channels);
+ }
+
+ for (size_t channel = 0; channel < num_output_channels; ++channel) {
+ const size_t input_channel =
+ channel_map != nullptr ? (*channel_map)[channel] : channel;
+ DCHECK_LT(input_channel, num_input_channels);
+ ConvertPlanarSamples(num_frames_convert_and_copy,
+ &input[input_channel][input_offset_frames],
+ &planar_output_ptrs[channel][output_offset_frames]);
+ }
+}
+
+template <typename PlanarInputType, typename InterleavedOutputType>
+void ConvertPlanarToInterleavedTemplated(
+ PlanarInputType input, size_t num_input_frames, size_t num_input_channels,
+ size_t input_offset_frames, InterleavedOutputType interleaved_output_ptr,
+ size_t num_output_frames, size_t num_output_channels,
+ size_t output_offset_frames, size_t num_frames_convert_and_copy) {
+ DCHECK(interleaved_output_ptr);
+ DCHECK_GE(num_input_frames, input_offset_frames);
+ const size_t max_num_input_frames = num_input_frames - input_offset_frames;
+ DCHECK_GE(num_output_frames, output_offset_frames);
+ const size_t max_num_output_frames = num_output_frames - output_offset_frames;
+ DCHECK_GE(max_num_input_frames, num_frames_convert_and_copy);
+ DCHECK_GE(max_num_output_frames, num_frames_convert_and_copy);
+ DCHECK_EQ(num_input_channels, num_output_channels);
+
+ InterleavedOutputType interleaved_output_ptr_with_offset =
+ interleaved_output_ptr + output_offset_frames * num_output_channels;
+
+ if (num_input_channels == kNumStereoChannels &&
+ num_output_channels == kNumStereoChannels) {
+ const float* left_ptr = &input[0][input_offset_frames];
+ const float* right_ptr = &input[1][input_offset_frames];
+ InterleaveStereo(num_frames_convert_and_copy, left_ptr, right_ptr,
+ interleaved_output_ptr_with_offset);
+ } else {
+ for (size_t channel = 0; channel < num_output_channels; ++channel) {
+ const float* input_channel_ptr = &input[channel][input_offset_frames];
+ size_t interleaved_index = channel;
+ for (size_t frame = 0; frame < num_frames_convert_and_copy; ++frame) {
+ ConvertSampleFromFloatFormat(
+ input_channel_ptr[frame],
+ &interleaved_output_ptr_with_offset[interleaved_index]);
+ interleaved_index += num_output_channels;
+ }
+ }
+ }
+}
+
+} // namespace
+
+void PlanarFromInterleaved(const float* interleaved_buffer,
+ size_t num_input_frames, size_t num_input_channels,
+ const std::vector<float*>& planar_buffer_ptr,
+ size_t num_output_frames) {
+ DCHECK(interleaved_buffer);
+ DCHECK_GT(planar_buffer_ptr.size(), 0);
+
+ const size_t num_frames_to_copy =
+ std::min(num_input_frames, num_output_frames);
+ ConvertInterleavedToPlanarTemplated<const float*, float* const*>(
+ interleaved_buffer, num_input_frames, num_input_channels,
+ 0 /* input_offset_frames */, nullptr /* channel_map*/,
+ planar_buffer_ptr.data(), num_output_frames, planar_buffer_ptr.size(),
+ 0 /* output_offset_frames */, num_frames_to_copy);
+}
+
+void PlanarFromInterleaved(const int16* interleaved_buffer,
+ size_t num_input_frames, size_t num_input_channels,
+ const std::vector<float*>& planar_buffer_ptr,
+ size_t num_output_frames) {
+ DCHECK(interleaved_buffer);
+ DCHECK_GT(planar_buffer_ptr.size(), 0);
+
+ const size_t num_frames_to_copy =
+ std::min(num_input_frames, num_output_frames);
+ ConvertInterleavedToPlanarTemplated<const int16*, float* const*>(
+ interleaved_buffer, num_input_frames, num_input_channels,
+ 0 /* input_offset_frames */, nullptr /* channel_map*/,
+ planar_buffer_ptr.data(), num_output_frames, planar_buffer_ptr.size(),
+ 0 /* output_offset_frames */, num_frames_to_copy);
+}
+
+void FillAudioBuffer(const float* interleaved_buffer, size_t num_input_frames,
+ size_t num_input_channels, AudioBuffer* output) {
+ DCHECK(interleaved_buffer);
+ DCHECK(output);
+ const size_t num_frames_to_copy =
+ std::min(num_input_frames, output->num_frames());
+ ConvertInterleavedToPlanarTemplated<const float*, AudioBuffer&>(
+ interleaved_buffer, num_input_frames, num_input_channels,
+ 0 /* input_offset_frames */, nullptr /* channel_map*/, *output,
+ output->num_frames(), output->num_channels(),
+ 0 /* output_offset_frames */, num_frames_to_copy);
+}
+
+void FillAudioBuffer(const int16* interleaved_buffer, size_t num_input_frames,
+ size_t num_input_channels, AudioBuffer* output) {
+ DCHECK(interleaved_buffer);
+ DCHECK(output);
+ const size_t num_frames_to_copy =
+ std::min(num_input_frames, output->num_frames());
+
+ ConvertInterleavedToPlanarTemplated<const int16*, AudioBuffer&>(
+ interleaved_buffer, num_input_frames, num_input_channels,
+ 0 /* input_offset_frames */, nullptr /* channel_map*/, *output,
+ output->num_frames(), output->num_channels(),
+ 0 /* output_offset_frames */, num_frames_to_copy);
+}
+
+void FillAudioBuffer(const std::vector<float>& interleaved_buffer,
+ size_t num_input_channels, AudioBuffer* output) {
+ DCHECK(output);
+ DCHECK_EQ(interleaved_buffer.size() % num_input_channels, 0);
+ const size_t num_frames_to_copy = std::min(
+ interleaved_buffer.size() / num_input_channels, output->num_frames());
+ FillAudioBuffer(&interleaved_buffer[0], num_frames_to_copy,
+ num_input_channels, output);
+}
+
+void FillAudioBuffer(const std::vector<int16>& interleaved_buffer,
+ size_t num_input_channels, AudioBuffer* output) {
+ DCHECK(output);
+ DCHECK_EQ(interleaved_buffer.size() % num_input_channels, 0);
+ const size_t num_frames_to_copy = std::min(
+ interleaved_buffer.size() / num_input_channels, output->num_frames());
+ FillAudioBuffer(&interleaved_buffer[0], num_frames_to_copy,
+ num_input_channels, output);
+}
+
+void FillAudioBuffer(const float* const* planar_ptrs, size_t num_input_frames,
+ size_t num_input_channels, AudioBuffer* output) {
+ DCHECK(planar_ptrs);
+ DCHECK(output);
+ const size_t num_frames_to_copy =
+ std::min(num_input_frames, output->num_frames());
+ ConvertPlanarToPlanarTemplated<const float* const*, AudioBuffer&>(
+ planar_ptrs, num_input_frames, num_input_channels,
+ 0 /* input_offset_frames */, nullptr /* channel_map*/, *output,
+ output->num_frames(), output->num_channels(),
+ 0 /* output_offset_frames */, num_frames_to_copy);
+}
+
+void FillAudioBuffer(const int16* const* planar_ptrs, size_t num_input_frames,
+ size_t num_input_channels, AudioBuffer* output) {
+ DCHECK(planar_ptrs);
+ DCHECK(output);
+ const size_t num_frames_to_copy =
+ std::min(num_input_frames, output->num_frames());
+ ConvertPlanarToPlanarTemplated<const int16* const*, AudioBuffer&>(
+ planar_ptrs, num_input_frames, num_input_channels,
+ 0 /* input_offset_frames */, nullptr /* channel_map*/, *output,
+ output->num_frames(), output->num_channels(),
+ 0 /* output_offset_frames */, num_frames_to_copy);
+}
+
+void FillAudioBufferWithOffset(const float* interleaved_buffer,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ size_t input_frame_offset,
+ size_t output_frame_offset,
+ size_t num_frames_to_copy, AudioBuffer* output) {
+ DCHECK(interleaved_buffer);
+ DCHECK(output);
+ ConvertInterleavedToPlanarTemplated<const float*, AudioBuffer&>(
+ interleaved_buffer, num_input_frames, num_input_channels,
+ input_frame_offset, nullptr /* channel_map*/, *output,
+ output->num_frames(), output->num_channels(), output_frame_offset,
+ num_frames_to_copy);
+}
+
+void FillAudioBufferWithOffset(const int16* interleaved_buffer,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ size_t input_frame_offset,
+ size_t output_frame_offset,
+ size_t num_frames_to_copy, AudioBuffer* output) {
+ DCHECK(interleaved_buffer);
+ DCHECK(output);
+ ConvertInterleavedToPlanarTemplated<const int16*, AudioBuffer&>(
+ interleaved_buffer, num_input_frames, num_input_channels,
+ input_frame_offset, nullptr /* channel_map*/, *output,
+ output->num_frames(), output->num_channels(), output_frame_offset,
+ num_frames_to_copy);
+}
+
+void FillAudioBufferWithOffset(const float* const* planar_ptrs,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ size_t input_frame_offset,
+ size_t output_frame_offset,
+ size_t num_frames_to_copy, AudioBuffer* output) {
+ DCHECK(planar_ptrs);
+ DCHECK(output);
+ ConvertPlanarToPlanarTemplated<const float* const*, AudioBuffer&>(
+ planar_ptrs, num_input_frames, num_input_channels, input_frame_offset,
+ nullptr /* channel_map*/, *output, output->num_frames(),
+ output->num_channels(), output_frame_offset, num_frames_to_copy);
+}
+
+void FillAudioBufferWithOffset(const int16* const* planar_ptrs,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ size_t input_frame_offset,
+ size_t output_frame_offset,
+ size_t num_frames_to_copy, AudioBuffer* output) {
+ DCHECK(planar_ptrs);
+ DCHECK(output);
+ ConvertPlanarToPlanarTemplated<const int16* const*, AudioBuffer&>(
+ planar_ptrs, num_input_frames, num_input_channels, input_frame_offset,
+ nullptr /* channel_map*/, *output, output->num_frames(),
+ output->num_channels(), output_frame_offset, num_frames_to_copy);
+}
+
+void FillAudioBufferWithChannelRemapping(const int16* interleaved_buffer,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ const std::vector<size_t>& channel_map,
+ AudioBuffer* output) {
+ DCHECK(interleaved_buffer);
+ DCHECK(output);
+ const size_t num_frames_to_copy =
+ std::min(num_input_frames, output->num_frames());
+ ConvertInterleavedToPlanarTemplated<const int16*, AudioBuffer&>(
+ interleaved_buffer, num_input_frames, num_input_channels,
+ 0 /*input_frame_offset*/, &channel_map, *output, output->num_frames(),
+ output->num_channels(), 0 /*output_frame_offset*/, num_frames_to_copy);
+}
+
+void FillAudioBufferWithChannelRemapping(const float* interleaved_buffer,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ const std::vector<size_t>& channel_map,
+ AudioBuffer* output) {
+ DCHECK(interleaved_buffer);
+ DCHECK(output);
+ const size_t num_frames_to_copy =
+ std::min(num_input_frames, output->num_frames());
+ ConvertInterleavedToPlanarTemplated<const float*, AudioBuffer&>(
+ interleaved_buffer, num_input_frames, num_input_channels,
+ 0 /*input_frame_offset*/, &channel_map, *output, output->num_frames(),
+ output->num_channels(), 0 /*output_frame_offset*/, num_frames_to_copy);
+}
+
+void FillAudioBufferWithChannelRemapping(const float* const* planar_ptrs,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ const std::vector<size_t>& channel_map,
+ AudioBuffer* output) {
+ DCHECK(planar_ptrs);
+ DCHECK(output);
+ const size_t num_frames_to_copy =
+ std::min(num_input_frames, output->num_frames());
+ ConvertPlanarToPlanarTemplated<const float* const*, AudioBuffer&>(
+ planar_ptrs, num_input_frames, num_input_channels,
+ 0 /*input_offset_frames*/, &channel_map, *output, output->num_frames(),
+ output->num_channels(), 0 /* output_offset_frames*/, num_frames_to_copy);
+}
+
+void FillAudioBufferWithChannelRemapping(const int16* const* planar_ptr,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ const std::vector<size_t>& channel_map,
+ AudioBuffer* output) {
+ DCHECK(planar_ptr);
+ DCHECK(output);
+ const size_t num_frames_to_copy =
+ std::min(num_input_frames, output->num_frames());
+ ConvertPlanarToPlanarTemplated<const int16* const*, AudioBuffer&>(
+ planar_ptr, num_input_frames, num_input_channels,
+ 0 /*input_offset_frames*/, &channel_map, *output, output->num_frames(),
+ output->num_channels(), 0 /* output_offset_frames*/, num_frames_to_copy);
+}
+
+void FillExternalBuffer(const AudioBuffer& input, std::vector<float>* output) {
+ DCHECK(output);
+ output->resize(input.num_frames() * input.num_channels());
+ FillExternalBuffer(input, output->data(), input.num_frames(),
+ input.num_channels());
+}
+
+void FillExternalBuffer(const AudioBuffer& input, std::vector<int16>* output) {
+ DCHECK(output);
+ output->resize(input.num_frames() * input.num_channels());
+ FillExternalBuffer(input, output->data(), input.num_frames(),
+ input.num_channels());
+}
+
+void FillExternalBuffer(const AudioBuffer& input,
+ int16* const* planar_output_ptrs,
+ size_t num_output_frames, size_t num_output_channels) {
+ ConvertPlanarToPlanarTemplated<const AudioBuffer&, int16* const*>(
+ input, input.num_frames(), input.num_channels(),
+ 0 /*input_offset_frames*/, nullptr /* channel_map*/, planar_output_ptrs,
+ num_output_frames, num_output_channels, 0 /* output_offset_frames*/,
+ num_output_frames);
+}
+
+void FillExternalBuffer(const AudioBuffer& input,
+ float* const* planar_output_ptrs,
+ size_t num_output_frames, size_t num_output_channels) {
+ ConvertPlanarToPlanarTemplated<const AudioBuffer&, float* const*>(
+ input, input.num_frames(), input.num_channels(),
+ 0 /*input_offset_frames*/, nullptr /* channel_map*/, planar_output_ptrs,
+ num_output_frames, num_output_channels, 0 /* output_offset_frames*/,
+ num_output_frames);
+}
+
+void FillExternalBuffer(const AudioBuffer& input,
+ int16* interleaved_output_buffer,
+ size_t num_output_frames, size_t num_output_channels) {
+ ConvertPlanarToInterleavedTemplated<const AudioBuffer&, int16*>(
+ input, input.num_frames(), input.num_channels(),
+ 0 /*input_offset_frames*/, interleaved_output_buffer, num_output_frames,
+ num_output_channels, 0 /* output_offset_frames*/, num_output_frames);
+}
+
+void FillExternalBuffer(const AudioBuffer& input,
+ float* interleaved_output_buffer,
+ size_t num_output_frames, size_t num_output_channels) {
+ ConvertPlanarToInterleavedTemplated<const AudioBuffer&, float*>(
+ input, input.num_frames(), input.num_channels(),
+ 0 /*input_offset_frames*/, interleaved_output_buffer, num_output_frames,
+ num_output_channels, 0 /* output_offset_frames*/, num_output_frames);
+}
+
+void FillExternalBufferWithOffset(const AudioBuffer& input,
+ size_t input_offset_frames,
+ int16* const* planar_output_ptrs,
+ size_t num_output_frames,
+ size_t num_output_channels,
+ size_t output_offset_frames,
+ size_t num_frames_convert_and_copy) {
+ ConvertPlanarToPlanarTemplated<const AudioBuffer&, int16* const*>(
+ input, input.num_frames(), input.num_channels(), input_offset_frames,
+ nullptr /* channel_map */, planar_output_ptrs, num_output_frames,
+ num_output_channels, output_offset_frames, num_frames_convert_and_copy);
+}
+
+void FillExternalBufferWithOffset(const AudioBuffer& input,
+ size_t input_offset_frames,
+ float* const* planar_output_ptrs,
+ size_t num_output_frames,
+ size_t num_output_channels,
+ size_t output_offset_frames,
+ size_t num_frames_convert_and_copy) {
+ ConvertPlanarToPlanarTemplated<const AudioBuffer&, float* const*>(
+ input, input.num_frames(), input.num_channels(), input_offset_frames,
+ nullptr /* channel_map */, planar_output_ptrs, num_output_frames,
+ num_output_channels, output_offset_frames, num_frames_convert_and_copy);
+}
+
+void FillExternalBufferWithOffset(const AudioBuffer& input,
+ size_t input_offset_frames,
+ int16* interleaved_output_buffer,
+ size_t num_output_frames,
+ size_t num_output_channels,
+ size_t output_offset_frames,
+ size_t num_frames_convert_and_copy) {
+ ConvertPlanarToInterleavedTemplated<const AudioBuffer&, int16*>(
+ input, input.num_frames(), input.num_channels(), input_offset_frames,
+ interleaved_output_buffer, num_output_frames, num_output_channels,
+ output_offset_frames, num_frames_convert_and_copy);
+}
+
+void FillExternalBufferWithOffset(const AudioBuffer& input,
+ size_t input_offset_frames,
+ float* interleaved_output_buffer,
+ size_t num_output_frames,
+ size_t num_output_channels,
+ size_t output_offset_frames,
+ size_t num_frames_convert_and_copy) {
+ ConvertPlanarToInterleavedTemplated<const AudioBuffer&, float*>(
+ input, input.num_frames(), input.num_channels(), input_offset_frames,
+ interleaved_output_buffer, num_output_frames, num_output_channels,
+ output_offset_frames, num_frames_convert_and_copy);
+}
+
+void GetRawChannelDataPointersFromAudioBuffer(
+ AudioBuffer* audio_buffer, std::vector<float*>* channel_ptr_vector) {
+ DCHECK(audio_buffer);
+ DCHECK(channel_ptr_vector);
+ DCHECK_EQ(audio_buffer->num_channels(), channel_ptr_vector->size());
+ for (size_t i = 0; i < audio_buffer->num_channels(); ++i) {
+ (*channel_ptr_vector)[i] = &(*audio_buffer)[i][0];
+ }
+}
+
+void GetRawChannelDataPointersFromAudioBuffer(
+ const AudioBuffer& audio_buffer,
+ std::vector<const float*>* channel_ptr_vector) {
+ DCHECK(channel_ptr_vector);
+ DCHECK_EQ(audio_buffer.num_channels(), channel_ptr_vector->size());
+ for (size_t i = 0; i < audio_buffer.num_channels(); ++i) {
+ (*channel_ptr_vector)[i] = &audio_buffer[i][0];
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/planar_interleaved_conversion.h b/src/3rdparty/resonance-audio/resonance_audio/utils/planar_interleaved_conversion.h
new file mode 100644
index 000000000..ed30be30e
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/planar_interleaved_conversion.h
@@ -0,0 +1,421 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_PLANAR_INTERLEAVED_CONVERSION_H_
+#define RESONANCE_AUDIO_UTILS_PLANAR_INTERLEAVED_CONVERSION_H_
+
+#include <vector>
+
+#include "base/integral_types.h"
+#include "base/audio_buffer.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+// Copies interleaved audio data from a raw float pointer into separate channel
+// buffers specified by a vector of raw float pointers.
+//
+// @param interleaved_buffer Raw float pointer to interleaved audio data.
+// @param num_input_frames Size of |interleaved_buffer| in frames.
+// @param num_input_channels Number of channels in interleaved audio data.
+// @param planar_buffer_ptr Raw float pointers to each planar channel buffer.
+// @param num_output_frames Number of frames per channel in output buffer.
+void PlanarFromInterleaved(const float* interleaved_buffer,
+ size_t num_input_frames, size_t num_input_channels,
+ const std::vector<float*>& planar_buffer_ptr,
+ size_t num_output_frames);
+
+// Copies interleaved audio data from a raw int16 pointer into separate channel
+// buffers specified by a vector of raw float pointers.
+//
+// @param interleaved_buffer Raw int16 pointer to interleaved audio data.
+// @param num_input_frames Size of |interleaved_buffer| in frames.
+// @param num_input_channels Number of channels in interleaved audio data.
+// @param planar_buffer_ptr Raw float pointers to each planar channel buffer.
+// @param num_output_frames Number of frames per channel in output buffer.
+void PlanarFromInterleaved(const int16* interleaved_buffer,
+ size_t num_input_frames, size_t num_input_channels,
+ const std::vector<float*>& planar_buffer_ptr,
+ size_t num_output_frames);
+
+// Copies interleaved audio data from a raw float pointer into a planar
+// |AudioBuffer|. Note that the number of output channels and frames is defined
+// by the target |AudioBuffer| instance.
+//
+// @param interleaved_buffer Raw float pointer to interleaved audio data.
+// @param num_input_frames Size of interleaved_buffer in frames.
+// @param num_input_channels Number of channels in interleaved audio data.
+// @param output Target output buffer.
+void FillAudioBuffer(const float* interleaved_buffer, size_t num_input_frames,
+ size_t num_input_channels, AudioBuffer* output);
+
+// Copies interleaved audio data from a raw int16 pointer into a planar
+// |AudioBuffer|. Note that the number of output channels and frames is defined
+// by the target |AudioBuffer| instance.
+//
+// @param interleaved_buffer Raw int16 pointer to interleaved audio data.
+// @param num_input_frames Size of interleaved_buffer in frames.
+// @param num_input_channels Number of channels in interleaved audio data.
+// @param output Target output buffer.
+void FillAudioBuffer(const int16* interleaved_buffer, size_t num_input_frames,
+ size_t num_input_channels, AudioBuffer* output);
+
+// Copies interleaved audio data from a float vector into a planar
+// |AudioBuffer|. Note that the number of output channels and frames is defined
+// by the target |AudioBuffer| instance.
+//
+// @param interleaved_buffer Interleaved audio data.
+// @param num_input_channels Number of channels in interleaved audio data.
+// @param output Target output buffer.
+void FillAudioBuffer(const std::vector<float>& interleaved_buffer,
+ size_t num_input_channels, AudioBuffer* output);
+
+// Copies interleaved audio data from a int16 vector into a planar
+// |AudioBuffer|. Note that the number of output channels and frames is defined
+// by the target |AudioBuffer| instance.
+//
+// @param interleaved_buffer Interleaved audio data.
+// @param num_input_channels Number of channels in interleaved audio data.
+// @param output Target output buffer.
+void FillAudioBuffer(const std::vector<int16>& interleaved_buffer,
+ size_t num_input_channels, AudioBuffer* output);
+
+// Copies raw planar float audio data into a planar |AudioBuffer|. Note that the
+// number of output channels and frames is defined by the target |AudioBuffer|
+// instance.
+//
+// @param planar_ptrs Pointer to an array of pointers to raw float channel data.
+// @param num_input_frames Size of planar input in frames.
+// @param num_input_channels Number of channels in planar input buffer.
+// @param output Target output buffer.
+void FillAudioBuffer(const float* const* planar_ptrs, size_t num_input_frames,
+ size_t num_input_channels, AudioBuffer* output);
+
+// Copies raw planar int16 audio data into a planar |AudioBuffer|. Note that the
+// number of output channels and frames is defined by the target |AudioBuffer|
+// instance.
+//
+// @param planar_ptrs Pointer to an array of pointers to raw int16 channel data.
+// @param num_input_frames Size of planar input in frames.
+// @param num_input_channels Number of channels in planar input buffer.
+// @param output Target output buffer.
+void FillAudioBuffer(const int16* const* planar_ptrs, size_t num_input_frames,
+ size_t num_input_channels, AudioBuffer* output);
+
+// Copies interleaved audio data from a raw float pointer into a planar
+// |AudioBuffer| with a specified output frame offset. Note that the
+// number of output channels is defined by the target |AudioBuffer| instance.
+//
+// @param interleaved_buffer Raw float pointer to interleaved audio data.
+// @param num_input_frames Size of |interleaved_buffer| in frames.
+// @param num_input_channels Number of channels in interleaved audio data.
+// @param input_frame_offset First source frame position in input.
+// @param output_frame_offset First frame destination in output.
+// @param num_frames_to_copy Number of frames per copy.
+// @param output Target output buffer.
+void FillAudioBufferWithOffset(const float* interleaved_buffer,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ size_t input_frame_offset,
+ size_t output_frame_offset,
+ size_t num_frames_to_copy, AudioBuffer* output);
+
+// Copies interleaved audio data from a raw int16 pointer into a planar
+// |AudioBuffer| with a specified output frame offset. Note that the
+// number of output channels is defined by the target |AudioBuffer| instance.
+//
+// @param interleaved_buffer Raw int16 pointer to interleaved audio data.
+// @param num_input_frames Size of |interleaved_buffer| in frames.
+// @param num_input_channels Number of channels in interleaved audio data.
+// @param input_frame_offset First source frame position in input.
+// @param output_frame_offset First frame destination in output.
+// @param num_frames_to_copy Number of frames per copy.
+// @param output Target output buffer.
+void FillAudioBufferWithOffset(const int16* interleaved_buffer,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ size_t input_frame_offset,
+ size_t output_frame_offset,
+ size_t num_frames_to_copy, AudioBuffer* output);
+
+// Copies raw planar float audio data into a planar |AudioBuffer| with a
+// specified output frame offset. Note that the number of output channels is
+// defined by the target |AudioBuffer| instance.
+//
+// @param planar_ptrs Pointer to an array of pointers to raw float channel data.
+// @param num_input_frames Size of |interleaved_buffer| in frames.
+// @param num_input_channels Number of channels in interleaved audio data.
+// @param input_frame_offset First source frame position in input.
+// @param output_frame_offset First frame destination in output.
+// @param num_frames_to_copy Number of frames per copy.
+// @param output Target output buffer.
+void FillAudioBufferWithOffset(const float* const* planar_ptrs,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ size_t input_frame_offset,
+ size_t output_frame_offset,
+ size_t num_frames_to_copy, AudioBuffer* output);
+
+// Copies raw planar int16 audio data into a planar |AudioBuffer| with a
+// specified output frame offset. Note that the number of output channels is
+// defined by the target |AudioBuffer| instance.
+//
+// @param planar_ptrs Pointer to an array of pointers to raw int16 channel data.
+// @param num_input_frames Size of |interleaved_buffer| in frames.
+// @param num_input_channels Number of channels in interleaved audio data.
+// @param input_frame_offset First source frame position in input.
+// @param output_frame_offset First frame destination in output.
+// @param num_frames_to_copy Number of frames per copy.
+// @param output Target output buffer.
+void FillAudioBufferWithOffset(const int16* const* planar_ptrs,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ size_t input_frame_offset,
+ size_t output_frame_offset,
+ size_t num_frames_to_copy, AudioBuffer* output);
+
+// Copies interleaved audio data from a raw int16 pointer into a planar
+// |AudioBuffer|. The |channel_map| argument allows to reorder the channel
+// mapping between the interleaved input and output buffer. The i'th channel in
+// the output buffer corresponds to the |channel_map[i]|'th input channel. Note
+// that the number of output channels and frames is derived from the target
+// |AudioBuffer| instance.
+//
+// @param interleaved_buffer Raw int16 pointer to interleaved audio data.
+// @param num_input_frames Size of interleaved_buffer in frames.
+// @param num_input_channels Number of input channels.
+// @param channel_map Mapping that maps output channels to input channels
+// @param output Target output buffer.
+void FillAudioBufferWithChannelRemapping(const int16* interleaved_buffer,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ const std::vector<size_t>& channel_map,
+ AudioBuffer* output);
+
+// Copies interleaved audio data from a raw float pointer into a planar
+// |AudioBuffer|. The |channel_map| argument allows to reorder the channel
+// mapping between the interleaved input and output buffer. The i'th channel in
+// the output buffer corresponds to the |channel_map[i]| input channel. Note
+// that the number of output channels and frames is derived from the target
+// |AudioBuffer| instance.
+//
+// @param interleaved_buffer Raw float pointer to interleaved audio data.
+// @param num_input_frames Size of interleaved_buffer in frames.
+// @param num_input_channels Number of input channels.
+// @param channel_map Mapping that maps output channels to input channels
+// @param output Target output buffer.
+void FillAudioBufferWithChannelRemapping(const float* interleaved_buffer,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ const std::vector<size_t>& channel_map,
+ AudioBuffer* output);
+
+// Copies raw planar float audio data into a planar |AudioBuffer|. The
+// |channel_map| argument allows to reorder the channel mapping between the
+// planar input and output buffer. The i'th channel in the output buffer
+// corresponds to the |channel_map[i]| input channel. Note that the number of
+// output channels and frames is derived from the target |AudioBuffer| instance.
+//
+// @param planar_ptrs Pointer to an array of pointers to raw float channel data.
+// @param num_input_frames Size of interleaved_buffer in frames.
+// @param num_input_channels Number of input channels.
+// @param channel_map Mapping that maps output channels to input channels
+// @param output Target output buffer.
+void FillAudioBufferWithChannelRemapping(const float* const* planar_ptr,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ const std::vector<size_t>& channel_map,
+ AudioBuffer* output);
+
+// Copies raw planar int16 audio data into a planar |AudioBuffer|. The
+// |channel_map| argument allows to reorder the channel mapping between the
+// planar input and output buffer. The i'th channel in the output buffer
+// corresponds to the |channel_map[i]| input channel. Note that the number of
+// output channels and frames is derived from the target |AudioBuffer| instance.
+//
+// @param planar_ptrs Pointer to an array of pointers to raw int16 channel data.
+// @param num_input_frames Size of interleaved_buffer in frames.
+// @param num_input_channels Number of input channels.
+// @param channel_map Mapping that maps output channels to input channels
+// @param output Target output buffer.
+void FillAudioBufferWithChannelRemapping(const int16* const* planar_ptr,
+ size_t num_input_frames,
+ size_t num_input_channels,
+ const std::vector<size_t>& channel_map,
+ AudioBuffer* output);
+
+// Copies planar audio data from an |AudioBuffer| to an external interleaved
+// float vector. Note this method resizes the target vector to match number of
+// input samples.
+//
+// @param input Input audio buffer.
+// @param output interleaved output vector.
+void FillExternalBuffer(const AudioBuffer& input, std::vector<float>* output);
+
+// Copies and converts planar audio data from an |AudioBuffer| to an external
+// interleaved int16 vector. Note this method resizes the target vector to match
+// number of input samples.
+//
+// @param input Input audio buffer.
+// @param output interleaved output vector.
+void FillExternalBuffer(const AudioBuffer& input, std::vector<int16>* output);
+
+// Copies planar audio data from an |AudioBuffer| to an external planar raw
+// float buffer. Note that the input and output buffer must match in terms of
+// number of channels and frames.
+//
+// @param input Input audio buffer.
+// @param planar_output_ptrs Planar output vector.
+// @param num_output_frames Number of frames in output buffer.
+// @param num_output_channels Number of channels in output buffer.
+void FillExternalBuffer(const AudioBuffer& input,
+ float* const* planar_output_ptrs,
+ size_t num_output_frames, size_t num_output_channels);
+
+// Copies and converts audio data from an |AudioBuffer| to an external planar
+// int16 buffer. Note that the input and output buffer must match in terms of
+// number of channels and frames.
+//
+// @param input Input audio buffer.
+// @param planar_output_ptrs Planar output vector.
+// @param num_output_frames Number of frames in output buffer.
+// @param num_output_channels Number of channels in output buffer.
+void FillExternalBuffer(const AudioBuffer& input,
+ int16* const* planar_output_ptrs,
+ size_t num_output_frames, size_t num_output_channels);
+
+// Copies and converts planar audio data from an |AudioBuffer| to an external
+// interleaved raw int16 buffer. Note that the input and output buffer must
+// match in terms of number of channels and frames.
+//
+// @param input Input audio buffer.
+// @param interleaved_output_buffer Interleaved output vector.
+// @param num_output_frames Number of frames in output buffer.
+// @param num_output_channels Number of channels in output buffer.
+void FillExternalBuffer(const AudioBuffer& input,
+ int16* interleaved_output_buffer,
+ size_t num_output_frames, size_t num_output_channels);
+
+// Copies planar audio data from an |AudioBuffer| to an external interleaved
+// raw float buffer. Note that the input and output buffer must match in terms
+// of number of channels and frames.
+//
+// @param input Input audio buffer.
+// @param interleaved_output_buffer Interleaved output vector.
+// @param num_output_frames Number of frames in output buffer.
+// @param num_output_channels Number of channels in output buffer.
+void FillExternalBuffer(const AudioBuffer& input,
+ float* interleaved_output_buffer,
+ size_t num_output_frames, size_t num_output_channels);
+
+// Copies and converts audio data from an |AudioBuffer| to an external
+// planar raw int16 buffer with the ability to specify an offset into the
+// input and output buffer.
+//
+// @param input Input audio buffer.
+// @param input_offset_frames Offset into input buffer in frames.
+// @param planar_output_ptrs Planar output vector.
+// @param num_output_frames Number of frames in output buffer.
+// @param num_output_channels Number of channels in output buffer.
+// @param output_offset_frames Offset into the output buffer in frames.
+// @param num_frames_convert_and_copy Number of frames to be processed.
+void FillExternalBufferWithOffset(const AudioBuffer& input,
+ size_t input_offset_frames,
+ int16* const* planar_output_ptrs,
+ size_t num_output_frames,
+ size_t num_output_channels,
+ size_t output_offset_frames,
+ size_t num_frames_convert_and_copy);
+
+// Copies audio data from an |AudioBuffer| to an external planar raw float
+// buffer with the ability to specify an offset into the input and output
+// buffer.
+//
+// @param input Input audio buffer.
+// @param input_offset_frames Offset into input buffer in frames.
+// @param planar_output_ptrs Planar output vector.
+// @param num_output_frames Number of frames in output buffer.
+// @param num_output_channels Number of channels in output buffer.
+// @param output_offset_frames Offset into the output buffer in frames.
+// @param num_frames_convert_and_copy Number of frames to be processed.
+void FillExternalBufferWithOffset(const AudioBuffer& input,
+ size_t input_offset_frames,
+ float* const* planar_output_ptrs,
+ size_t num_output_frames,
+ size_t num_output_channels,
+ size_t output_offset_frames,
+ size_t num_frames_convert_and_copy);
+
+// Copies and converts audio data from an |AudioBuffer| to an external
+// interleaved raw int16 buffer with the ability to specify an offset into the
+// input and output buffer.
+//
+// @param input Input audio buffer.
+// @param input_offset_frames Offset into input buffer in frames.
+// @param interleaved_output_buffer Interleaved output vector.
+// @param num_output_frames Number of frames in output buffer.
+// @param num_output_channels Number of channels in output buffer.
+// @param output_offset_frames Offset into the output buffer in frames.
+// @param num_frames_convert_and_copy Number of frames to be processed.
+void FillExternalBufferWithOffset(const AudioBuffer& input,
+ size_t input_offset_frames,
+ int16* interleaved_output_buffer,
+ size_t num_output_frames,
+ size_t num_output_channels,
+ size_t output_offset_frames,
+ size_t num_frames_convert_and_copy);
+
+// Copies and audio data from an |AudioBuffer| to an external interleaved raw
+// float buffer with the ability to specify an offset into the input and output
+// buffer.
+//
+// @param input Input audio buffer.
+// @param input_offset_frames Offset into input buffer in frames.
+// @param interleaved_output_buffer Interleaved output vector.
+// @param num_output_frames Number of frames in output buffer.
+// @param num_output_channels Number of channels in output buffer.
+// @param output_offset_frames Offset into the output buffer in frames.
+// @param num_frames_convert_and_copy Number of frames to be processed.
+void FillExternalBufferWithOffset(const AudioBuffer& input,
+ size_t input_offset_frames,
+ float* interleaved_output_buffer,
+ size_t num_output_frames,
+ size_t num_output_channels,
+ size_t output_offset_frames,
+ size_t num_frames_convert_and_copy);
+
+// Generates a vector of mutable float pointers to the beginning of each channel
+// buffer in an |AudioBuffer|. The size of the |channel_ptr_vector| output
+// vector must match the number of channels in |audio_buffer|.
+//
+// @param audio_buffer Audio buffer.
+// @param channel_ptr_vector Output std::vector<float*> vector.
+void GetRawChannelDataPointersFromAudioBuffer(
+ AudioBuffer* audio_buffer, std::vector<float*>* channel_ptr_vector);
+
+// Generates a vector of immutable float pointers to the beginning of each
+// channel buffer in an |AudioBuffer|. The size of the |channel_ptr_vector|
+// output vector must match the number of channels in |audio_buffer|.
+//
+// @param audio_buffer Audio buffer.
+// @param channel_ptr_vector Output std::vector<const float*> vector.
+void GetRawChannelDataPointersFromAudioBuffer(
+ const AudioBuffer& audio_buffer,
+ std::vector<const float*>* channel_ptr_vector);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_PLANAR_INTERLEAVED_CONVERSION_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/planar_interleaved_conversion_test.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/planar_interleaved_conversion_test.cc
new file mode 100644
index 000000000..abfd31b82
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/planar_interleaved_conversion_test.cc
@@ -0,0 +1,875 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/planar_interleaved_conversion.h"
+
+#include <algorithm>
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+namespace {
+
+// Epsilon for conversion from int16_t back to float.
+const float kFloatEpsilon = 1e-4f;
+const int16_t kIntEpsilon = 1;
+
+const int kMemoryAlignmentBytesInt = static_cast<int>(kMemoryAlignmentBytes);
+
+const size_t kMaxNumFrames = 16;
+const size_t kMaxNumChannels = 8;
+
+int16_t SingleFloatToInt16(float value) {
+ const float kInt16Min = static_cast<float>(-0x7FFF);
+ const float kInt16Max = static_cast<float>(0x7FFF);
+ const float scaled_value = value * kInt16Max;
+ const float clamped_value =
+ std::min(kInt16Max, std::max(kInt16Min, scaled_value));
+ return static_cast<int16_t>(clamped_value);
+}
+
+// Creates a trivial channel map, i.e. no mapping.
+std::vector<size_t> GetTrivialChannelMap(size_t size) {
+ std::vector<size_t> channel_map(size);
+ for (size_t i = 0; i < size; ++i) {
+ channel_map[i] = i;
+ }
+ return channel_map;
+}
+
+// Fills an interleaved buffer with the channel number / 10, in each frame
+// (converted to integer format).
+void FillInterleaved(size_t num_channels, size_t num_frames, int16_t* buffer) {
+ for (size_t f = 0; f < num_frames; ++f) {
+ for (size_t c = 0; c < num_channels; ++c) {
+ buffer[f * num_channels + c] =
+ SingleFloatToInt16(static_cast<float>(c) * 0.1f);
+ }
+ }
+}
+
+// Fills an interleaved buffer with the channel number / 10, in each frame.
+void FillInterleaved(size_t num_channels, size_t num_frames, float* buffer) {
+ for (size_t f = 0; f < num_frames; ++f) {
+ for (size_t c = 0; c < num_channels; ++c) {
+ buffer[f * num_channels + c] = static_cast<float>(c) * 0.1f;
+ }
+ }
+}
+
+// Fills a planar buffer with the channel number / 10, in each frame.
+void FillPlanar(AudioBuffer* buffer) {
+ for (size_t c = 0; c < buffer->num_channels(); ++c) {
+ std::fill_n(&(*buffer)[c][0], buffer->num_frames(),
+ static_cast<float>(c) * 0.1f);
+ }
+}
+
+// Fills a planar buffer with the channel number / 10, in each frame.
+void FillPlanar(size_t num_frames, std::vector<float*>* buffer) {
+ for (size_t c = 0; c < buffer->size(); ++c) {
+ std::fill_n((*buffer)[c], num_frames, static_cast<float>(c) * 0.1f);
+ }
+}
+
+// Fills a planar buffer with the channel number / 10, in each frame (converted
+// to integer format).
+void FillPlanar(size_t num_frames, int16_t** buffer, size_t num_channels) {
+ for (size_t c = 0; c < num_channels; ++c) {
+ std::fill_n(buffer[c], num_frames,
+ SingleFloatToInt16(static_cast<float>(c) * 0.1f));
+ }
+}
+
+// Fills a planar buffer with the channel number / 10, in each frame (converted
+// to integer format).
+void FillPlanar(size_t num_frames, std::vector<int16_t*>* buffer) {
+ FillPlanar(num_frames, buffer->data(), buffer->size());
+}
+
+// Verifies that the output and expected output match.
+void VerifyOutput(size_t num_channels, size_t num_frames,
+ const std::vector<int16_t*>& expected_output,
+ const std::vector<int16_t*>& output) {
+ for (size_t c = 0; c < num_channels; ++c) {
+ for (size_t f = 0; f < num_frames; ++f) {
+ EXPECT_NEAR(expected_output[c][f], output[c][f], kIntEpsilon);
+ }
+ }
+}
+
+// Verifies that the output and expected output match.
+template <typename InputType, typename OutputType>
+void VerifyOutputFloatTemplated(size_t num_channels, size_t num_frames,
+ const InputType& expected_output,
+ const OutputType& output,
+ const std::vector<size_t>& channel_map,
+ size_t output_offset) {
+ for (size_t c = 0; c < num_channels; ++c) {
+ for (size_t f = output_offset; f < output_offset + num_frames; ++f) {
+ EXPECT_NEAR(expected_output[channel_map[c]][f], output[c][f],
+ kFloatEpsilon);
+ }
+ }
+}
+
+// Verifies that the output and expected output match.
+void VerifyOutput(size_t num_channels, size_t num_frames,
+ const AudioBuffer& expected_output,
+ const AudioBuffer& output) {
+ VerifyOutputFloatTemplated<AudioBuffer, AudioBuffer>(
+ num_channels, num_frames, expected_output, output,
+ GetTrivialChannelMap(num_channels), 0);
+}
+
+// Verifies that the output and expected output match.
+void VerifyOutput(size_t num_channels, size_t num_frames,
+ const std::vector<float*>& expected_output,
+ const AudioBuffer& output) {
+ VerifyOutputFloatTemplated<std::vector<float*>, AudioBuffer>(
+ num_channels, num_frames, expected_output, output,
+ GetTrivialChannelMap(num_channels), 0);
+}
+
+// Verifies that the output and expected output match.
+void VerifyOutput(size_t num_channels, size_t num_frames,
+ const AudioBuffer& expected_output,
+ const std::vector<float*>& output) {
+ VerifyOutputFloatTemplated<AudioBuffer, std::vector<float*>>(
+ num_channels, num_frames, expected_output, output,
+ GetTrivialChannelMap(num_channels), 0);
+}
+
+// Verifies that the output and expected output match.
+void VerifyOutput(size_t num_channels, size_t num_frames,
+ const std::vector<float*>& expected_output,
+ const std::vector<float*>& output) {
+ VerifyOutputFloatTemplated<std::vector<float*>, std::vector<float*>>(
+ num_channels, num_frames, expected_output, output,
+ GetTrivialChannelMap(num_channels), 0);
+}
+
+// Verifies that the output and expected output match.
+void VerifyOutput(size_t num_channels, size_t num_frames,
+ const std::vector<const float*>& expected_output,
+ const std::vector<const float*>& output) {
+ VerifyOutputFloatTemplated<std::vector<const float*>,
+ std::vector<const float*>>(
+ num_channels, num_frames, expected_output, output,
+ GetTrivialChannelMap(num_channels), 0);
+}
+
+// Verifies that the output and expected output match.
+void VerifyOutput(size_t num_channels, size_t num_frames,
+ const AudioBuffer& expected_output, const AudioBuffer& output,
+ const std::vector<size_t>& channel_map) {
+ VerifyOutputFloatTemplated<AudioBuffer, AudioBuffer>(
+ num_channels, num_frames, expected_output, output, channel_map, 0);
+}
+
+// Verifies that the output and expected output match.
+void VerifyOutput(size_t num_channels, size_t num_frames, size_t output_offset,
+ const AudioBuffer& expected_output,
+ const AudioBuffer& output) {
+ VerifyOutputFloatTemplated<AudioBuffer, AudioBuffer>(
+ num_channels, num_frames, expected_output, output,
+ GetTrivialChannelMap(num_channels), output_offset);
+}
+
+// Verifies that the output and expected output match.
+void VerifyOutput(size_t length, const std::vector<float>& expected_output,
+ int16_t* output) {
+ for (size_t f = 0; f < length; ++f) {
+ EXPECT_NEAR(SingleFloatToInt16(expected_output[f]), output[f], kIntEpsilon);
+ }
+}
+
+// Verifies that the output and expected output match.
+void VerifyOutput(size_t length, const std::vector<float>& expected_output,
+ const std::vector<int16_t>& output) {
+ for (size_t f = 0; f < length; ++f) {
+ EXPECT_NEAR(SingleFloatToInt16(expected_output[f]), output[f], kIntEpsilon);
+ }
+}
+
+// Verifies that the output and expected output match.
+void VerifyOutput(size_t length, const std::vector<float>& expected_output,
+ float* output) {
+ for (size_t f = 0; f < length; ++f) {
+ EXPECT_NEAR(expected_output[f], output[f], kIntEpsilon);
+ }
+}
+
+// Verifies that the output and expected output match.
+void VerifyOutput(size_t length, const std::vector<float>& expected_output,
+ const std::vector<float>& output) {
+ for (size_t f = 0; f < length; ++f) {
+ EXPECT_NEAR(expected_output[f], output[f], kIntEpsilon);
+ }
+}
+
+typedef std::tuple<size_t, size_t> TestParams;
+class PlanarInterleavedConverterTest
+ : public ::testing::Test,
+ public ::testing::WithParamInterface<TestParams> {
+ protected:
+ PlanarInterleavedConverterTest() {}
+
+ // Virtual methods from ::testing::Test
+ ~PlanarInterleavedConverterTest() override {}
+
+ void SetUp() override {}
+
+ void TearDown() override {}
+};
+
+// Tests that interleaved (float/int16_t) data can be correctly written into
+// vectors of float pointers.
+TEST_P(PlanarInterleavedConverterTest, TestInterleavedIntoVectorFloatPtr) {
+ const size_t num_channels = ::testing::get<0>(GetParam());
+ const size_t num_frames = ::testing::get<1>(GetParam());
+ const size_t num_interleaved_samples = num_frames * num_channels;
+
+ AudioBuffer expected_output(kMaxNumChannels, num_frames);
+ FillPlanar(&expected_output);
+
+ // Create output buffers memory.
+ AudioBuffer aligned_output_buffer(kMaxNumChannels, num_frames);
+ std::vector<float> unaligned_output_memory(num_interleaved_samples);
+
+ // Create output planar buffers.
+ std::vector<float*> aligned_output(num_channels);
+ std::vector<float*> unaligned_output(num_channels);
+ for (size_t c = 0; c < num_channels; ++c) {
+ aligned_output[c] = &aligned_output_buffer[c][0];
+ unaligned_output[c] = &unaligned_output_memory[num_frames * c];
+ }
+
+ // Integer.
+ AudioBuffer::AlignedInt16Vector interleaved_aligned_int(
+ num_interleaved_samples);
+ std::vector<int16_t> interleaved_unaligned_int(num_interleaved_samples);
+ FillInterleaved(num_channels, num_frames, interleaved_aligned_int.data());
+ FillInterleaved(num_channels, num_frames, &interleaved_unaligned_int[0]);
+
+ // Aligned Input, Aligned Output.
+ aligned_output_buffer.Clear();
+ LOG(INFO) << "aligned_output.size()" << aligned_output.size();
+ LOG(INFO) << "num_frames" << num_frames;
+ PlanarFromInterleaved(interleaved_aligned_int.data(), num_frames,
+ num_channels, aligned_output, num_frames);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ aligned_output);
+
+ // Unaligned Input, Aligned Output.
+ aligned_output_buffer.Clear();
+ PlanarFromInterleaved(interleaved_unaligned_int.data(), num_frames,
+ num_channels, aligned_output, num_frames);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ aligned_output);
+
+ // Aligned Input. Unaligned Output.
+ std::fill(unaligned_output_memory.begin(), unaligned_output_memory.end(),
+ 0.0f);
+ PlanarFromInterleaved(interleaved_aligned_int.data(), num_frames,
+ num_channels, unaligned_output, num_frames);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ unaligned_output);
+ // Unligned Input. Unaligned Output.
+ std::fill(unaligned_output_memory.begin(), unaligned_output_memory.end(),
+ 0.0f);
+ PlanarFromInterleaved(&interleaved_unaligned_int[0], num_frames, num_channels,
+ unaligned_output, num_frames);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ unaligned_output);
+
+ // Floating point.
+ AudioBuffer::AlignedFloatVector interleaved_aligned_float(
+ num_interleaved_samples);
+ std::vector<float> interleaved_plane_float(num_interleaved_samples);
+ FillInterleaved(num_channels, num_frames, interleaved_aligned_float.data());
+ FillInterleaved(num_channels, num_frames, &interleaved_plane_float[0]);
+ // Aligned Input, Aligned Output.
+ aligned_output_buffer.Clear();
+ PlanarFromInterleaved(interleaved_aligned_float.data(), num_frames,
+ num_channels, aligned_output, num_frames);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ aligned_output);
+ // Unaligned Input, Aligned Output.
+ aligned_output_buffer.Clear();
+ PlanarFromInterleaved(&interleaved_plane_float[0], num_frames, num_channels,
+ aligned_output, num_frames);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ aligned_output);
+ // Aligned Input. Unaligned Output.
+ std::fill(unaligned_output_memory.begin(), unaligned_output_memory.end(),
+ 0.0f);
+ PlanarFromInterleaved(interleaved_aligned_float.data(), num_frames,
+ num_channels, unaligned_output, num_frames);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ unaligned_output);
+ // Unligned Input. Unaligned Output.
+ std::fill(unaligned_output_memory.begin(), unaligned_output_memory.end(),
+ 0.0f);
+ PlanarFromInterleaved(&interleaved_plane_float[0], num_frames, num_channels,
+ unaligned_output, num_frames);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ unaligned_output);
+}
+
+// Tests that interleaved (float/int16_t) data can be correctly written into
+// |AudioBuffer|s.
+TEST_P(PlanarInterleavedConverterTest, TestInterleavedIntoAudioBuffer) {
+ const size_t num_channels = ::testing::get<0>(GetParam());
+ const size_t num_frames = ::testing::get<1>(GetParam());
+ const size_t num_interleaved_samples = num_frames * num_channels;
+
+ AudioBuffer expected_output(num_channels, num_frames);
+ FillPlanar(&expected_output);
+
+ // Create output buffers memory.
+ AudioBuffer output_buffer(num_channels, num_frames);
+
+ // Integer.
+ AudioBuffer::AlignedInt16Vector interleaved_aligned_int(
+ num_interleaved_samples);
+ std::vector<int16_t> interleaved_unaligned_int(num_interleaved_samples);
+ FillInterleaved(num_channels, num_frames, interleaved_aligned_int.data());
+ FillInterleaved(num_channels, num_frames, &interleaved_unaligned_int[0]);
+ // Aligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBuffer(interleaved_aligned_int.data(), num_frames, num_channels,
+ &output_buffer);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ output_buffer);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBuffer(&interleaved_unaligned_int[0], num_frames, num_channels,
+ &output_buffer);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ output_buffer);
+
+ // Floating point.
+ AudioBuffer::AlignedFloatVector interleaved_aligned_float(
+ num_interleaved_samples);
+ std::vector<float> interleaved_plane_float(num_interleaved_samples);
+ FillInterleaved(num_channels, num_frames, interleaved_aligned_float.data());
+ FillInterleaved(num_channels, num_frames, &interleaved_plane_float[0]);
+ // Aligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBuffer(interleaved_aligned_float.data(), num_frames, num_channels,
+ &output_buffer);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ output_buffer);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBuffer(&interleaved_plane_float[0], num_frames, num_channels,
+ &output_buffer);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ output_buffer);
+}
+
+// Tests that interleaved (float/int16_t) vectors can be correctly written into
+// |AudioBuffer|s.
+TEST_P(PlanarInterleavedConverterTest, TestInterleavedVectorIntoAudioBuffer) {
+ const size_t num_channels = ::testing::get<0>(GetParam());
+ const size_t num_frames = ::testing::get<1>(GetParam());
+ const size_t num_interleaved_samples = num_frames * num_channels;
+
+ AudioBuffer expected_output(num_channels, num_frames);
+ FillPlanar(&expected_output);
+
+ // Create output buffers memory.
+ AudioBuffer output_buffer(num_channels, num_frames);
+
+ // Integer.
+ std::vector<int16_t> interleaved_unaligned_int(num_interleaved_samples);
+ FillInterleaved(num_channels, num_frames, &interleaved_unaligned_int[0]);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBuffer(&interleaved_unaligned_int[0], num_frames, num_channels,
+ &output_buffer);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ output_buffer);
+
+ // Floating point.
+ std::vector<float> interleaved_plane_float(num_interleaved_samples);
+ FillInterleaved(num_channels, num_frames, &interleaved_plane_float[0]);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBuffer(&interleaved_plane_float[0], num_frames, num_channels,
+ &output_buffer);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ output_buffer);
+}
+
+// Tests that vectors of planar (float/int16_t) pointers can be correctly
+// written into |AudioBuffer|s.
+TEST_P(PlanarInterleavedConverterTest, TestPlanarPtrsIntoAudioBuffer) {
+ const size_t num_channels = ::testing::get<0>(GetParam());
+ const size_t num_frames = ::testing::get<1>(GetParam());
+
+ AudioBuffer expected_output(kMaxNumChannels, num_frames);
+ FillPlanar(&expected_output);
+
+ // Create output buffers memory.
+ AudioBuffer output_buffer(num_channels, num_frames);
+
+ // Integer.
+ std::vector<AudioBuffer::AlignedInt16Vector> planar_aligned_channels_int_buf(
+ num_channels, AudioBuffer::AlignedInt16Vector(num_frames));
+ std::vector<std::vector<int16_t>> planar_unaligned_channels_int_buffers(
+ num_channels, std::vector<int16_t>(num_frames));
+ std::vector<int16_t*> planar_aligned_channels_int(num_channels);
+ std::vector<int16_t*> planar_unaligned_channels_int(num_channels);
+ for (size_t c = 0; c < num_channels; ++c) {
+ planar_aligned_channels_int[c] = planar_aligned_channels_int_buf[c].data();
+ planar_unaligned_channels_int[c] =
+ planar_unaligned_channels_int_buffers[c].data();
+ }
+ FillPlanar(num_frames, &planar_aligned_channels_int);
+ FillPlanar(num_frames, &planar_unaligned_channels_int);
+ // Aligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBuffer(&planar_aligned_channels_int[0], num_frames, num_channels,
+ &output_buffer);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ output_buffer);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBuffer(&planar_unaligned_channels_int[0], num_frames, num_channels,
+ &output_buffer);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ output_buffer);
+
+ // Floating point.
+ std::vector<AudioBuffer::AlignedFloatVector>
+ planar_aligned_channels_float_buf(
+ num_channels, AudioBuffer::AlignedFloatVector(num_frames));
+ std::vector<std::vector<float>> planar_unaligned_channels_float_buffers(
+ num_channels, std::vector<float>(num_frames));
+ std::vector<float*> planar_aligned_channels_float(num_channels);
+ std::vector<float*> planar_unaligned_channels_float(num_channels);
+ for (size_t c = 0; c < num_channels; ++c) {
+ planar_aligned_channels_float[c] =
+ planar_aligned_channels_float_buf[c].data();
+ planar_unaligned_channels_float[c] =
+ planar_unaligned_channels_float_buffers[c].data();
+ }
+ FillPlanar(num_frames, &planar_aligned_channels_float);
+ FillPlanar(num_frames, &planar_unaligned_channels_float);
+ // Aligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBuffer(&planar_aligned_channels_float[0], num_frames, num_channels,
+ &output_buffer);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ output_buffer);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBuffer(&planar_unaligned_channels_float[0], num_frames, num_channels,
+ &output_buffer);
+ VerifyOutput(num_channels, std::min(num_frames, num_frames), expected_output,
+ output_buffer);
+}
+
+// Tests that interleaved (float/int16_t) data can be correctly written into
+// |AudioBuffer|s, with offsets into both the input and output data.
+TEST_P(PlanarInterleavedConverterTest,
+ TestInterleavedIntoAudioBufferWithOffset) {
+ const size_t num_channels = ::testing::get<0>(GetParam());
+ const size_t num_frames = ::testing::get<1>(GetParam());
+
+ AudioBuffer expected_output(kMaxNumChannels, num_frames);
+ FillPlanar(&expected_output);
+
+ const size_t num_interleaved_samples = num_frames * num_channels;
+ // Create output buffers memory.
+ AudioBuffer output_buffer(num_channels, num_frames);
+
+ AudioBuffer::AlignedInt16Vector interleaved_aligned_int(
+ num_interleaved_samples);
+ std::vector<int16_t> interleaved_unaligned_int(num_interleaved_samples);
+ FillInterleaved(num_channels, num_frames, interleaved_aligned_int.data());
+ FillInterleaved(num_channels, num_frames, &interleaved_unaligned_int[0]);
+
+ AudioBuffer::AlignedFloatVector interleaved_aligned_float(
+ num_interleaved_samples);
+ std::vector<float> interleaved_plane_float(num_interleaved_samples);
+ FillInterleaved(num_channels, num_frames, interleaved_aligned_float.data());
+ FillInterleaved(num_channels, num_frames, &interleaved_plane_float[0]);
+
+ for (size_t output_offset = 1; output_offset <= 4; ++output_offset) {
+ for (size_t input_offset = 1; input_offset <= 4; ++input_offset) {
+ const size_t num_frames_to_copy =
+ std::min(num_frames - input_offset, num_frames - output_offset);
+ // Integer.
+ // Aligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithOffset(interleaved_aligned_int.data(), num_frames,
+ num_channels, input_offset, output_offset,
+ num_frames_to_copy, &output_buffer);
+ VerifyOutput(num_channels, num_frames_to_copy, output_offset,
+ expected_output, output_buffer);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithOffset(&interleaved_unaligned_int[0], num_frames,
+ num_channels, input_offset, output_offset,
+ num_frames_to_copy, &output_buffer);
+ VerifyOutput(num_channels, num_frames_to_copy, output_offset,
+ expected_output, output_buffer);
+
+ // Floating point.
+ // Aligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithOffset(interleaved_aligned_float.data(), num_frames,
+ num_channels, input_offset, output_offset,
+ num_frames_to_copy, &output_buffer);
+ VerifyOutput(num_channels, num_frames_to_copy, output_offset,
+ expected_output, output_buffer);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithOffset(&interleaved_plane_float[0], num_frames,
+ num_channels, input_offset, output_offset,
+ num_frames_to_copy, &output_buffer);
+ VerifyOutput(num_channels, num_frames_to_copy, output_offset,
+ expected_output, output_buffer);
+ }
+ }
+}
+
+// Tests that planar (float/int16_t) data can be correctly written into
+// |AudioBuffer|s, with offsets into both the input and output data.
+TEST_P(PlanarInterleavedConverterTest,
+ TestPlanarPtrsIntoAudioBufferWithOffset) {
+ const size_t num_channels = ::testing::get<0>(GetParam());
+ const size_t num_frames = ::testing::get<1>(GetParam());
+
+ AudioBuffer expected_output(num_channels, num_frames);
+ FillPlanar(&expected_output);
+
+ // Create output buffers memory.
+ AudioBuffer output_buffer(num_channels, num_frames);
+
+ std::vector<AudioBuffer::AlignedInt16Vector>
+ planar_aligned_channels_int_buffers(
+ num_channels, AudioBuffer::AlignedInt16Vector(num_frames));
+ std::vector<std::vector<int16_t>> planar_unaligned_channels_int_buffers(
+ num_channels, std::vector<int16_t>(num_frames));
+
+ std::vector<int16_t*> planar_aligned_channels_int(num_channels);
+ std::vector<int16_t*> planar_unaligned_channels_int(num_channels);
+ for (size_t c = 0; c < num_channels; ++c) {
+ planar_aligned_channels_int[c] =
+ planar_aligned_channels_int_buffers[c].data();
+ planar_unaligned_channels_int[c] =
+ planar_unaligned_channels_int_buffers[c].data();
+ }
+ FillPlanar(num_frames, &planar_aligned_channels_int);
+ FillPlanar(num_frames, &planar_unaligned_channels_int);
+
+ std::vector<AudioBuffer::AlignedFloatVector>
+ planar_aligned_channels_float_buffer(
+ num_channels, AudioBuffer::AlignedFloatVector(num_frames));
+ std::vector<std::vector<float>> planar_unaligned_channels_float_buffers(
+ num_channels, std::vector<float>(num_frames));
+
+ std::vector<float*> planar_aligned_channels_float(num_channels);
+ std::vector<float*> planar_unaligned_channels_float(num_channels);
+
+ for (size_t c = 0; c < num_channels; ++c) {
+ planar_aligned_channels_float[c] =
+ planar_aligned_channels_float_buffer[c].data();
+ planar_unaligned_channels_float[c] =
+ planar_unaligned_channels_float_buffers[c].data();
+ }
+ FillPlanar(num_frames, &planar_aligned_channels_float);
+ FillPlanar(num_frames, &planar_unaligned_channels_float);
+
+ for (size_t output_offset = 1; output_offset <= 4; ++output_offset) {
+ for (size_t input_offset = 1; input_offset <= 4; ++input_offset) {
+ const size_t num_frames_to_copy =
+ std::min(num_frames - input_offset, num_frames - output_offset);
+ // Integer.
+ // Aligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithOffset(&planar_aligned_channels_int[0], num_frames,
+ num_channels, input_offset, output_offset,
+ num_frames_to_copy, &output_buffer);
+ VerifyOutput(num_channels, num_frames_to_copy, output_offset,
+ expected_output, output_buffer);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithOffset(&planar_unaligned_channels_int[0], num_frames,
+ num_channels, input_offset, output_offset,
+ num_frames_to_copy, &output_buffer);
+ VerifyOutput(num_channels, num_frames_to_copy, output_offset,
+ expected_output, output_buffer);
+
+ // Floating point.
+ // Aligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithOffset(&planar_aligned_channels_float[0], num_frames,
+ num_channels, input_offset, output_offset,
+ num_frames_to_copy, &output_buffer);
+ VerifyOutput(num_channels, num_frames_to_copy, output_offset,
+ expected_output, output_buffer);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithOffset(&planar_unaligned_channels_float[0], num_frames,
+ num_channels, input_offset, output_offset,
+ num_frames_to_copy, &output_buffer);
+ VerifyOutput(num_channels, num_frames_to_copy, output_offset,
+ expected_output, output_buffer);
+ }
+ }
+}
+
+// Tests that interleaved (float/int16_t) data can be correctly written into
+// |AudioBuffer|s, with remapping of channels.
+TEST_P(PlanarInterleavedConverterTest,
+ TestInterleavedIntoAudioBufferRemapping) {
+ const size_t num_channels = ::testing::get<0>(GetParam());
+ const size_t num_frames = ::testing::get<1>(GetParam());
+
+ AudioBuffer expected_output(num_channels, num_frames);
+ FillPlanar(&expected_output);
+
+ std::vector<size_t> channel_map(num_channels);
+ for (size_t c = 0; c < num_channels; ++c) {
+ channel_map[c] = c;
+ }
+
+ // Permute the channel map.
+ std::next_permutation(channel_map.begin(), channel_map.end());
+ const size_t num_interleaved_samples = num_frames * num_channels;
+
+ // Create output buffers memory.
+ AudioBuffer output_buffer(num_channels, num_frames);
+
+ // Integer.
+ AudioBuffer::AlignedInt16Vector interleaved_aligned_int(
+ num_interleaved_samples);
+ std::vector<int16_t> interleaved_unaligned_int(num_interleaved_samples);
+ FillInterleaved(num_channels, num_frames, interleaved_aligned_int.data());
+ FillInterleaved(num_channels, num_frames, &interleaved_unaligned_int[0]);
+ // Aligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithChannelRemapping(interleaved_aligned_int.data(),
+ num_frames, num_channels, channel_map,
+ &output_buffer);
+ VerifyOutput(num_channels, num_frames, expected_output, output_buffer,
+ channel_map);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithChannelRemapping(&interleaved_unaligned_int[0], num_frames,
+ num_channels, channel_map,
+ &output_buffer);
+ VerifyOutput(num_channels, num_frames, expected_output, output_buffer,
+ channel_map);
+
+ // Floating point.
+ AudioBuffer::AlignedFloatVector interleaved_aligned_float(
+ num_interleaved_samples);
+ std::vector<float> interleaved_plane_float(num_interleaved_samples);
+ FillInterleaved(num_channels, num_frames, interleaved_aligned_float.data());
+ FillInterleaved(num_channels, num_frames, &interleaved_plane_float[0]);
+ // Aligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithChannelRemapping(interleaved_aligned_float.data(),
+ num_frames, num_channels, channel_map,
+ &output_buffer);
+ VerifyOutput(num_channels, num_frames, expected_output, output_buffer,
+ channel_map);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithChannelRemapping(&interleaved_plane_float[0], num_frames,
+ num_channels, channel_map,
+ &output_buffer);
+ VerifyOutput(num_channels, num_frames, expected_output, output_buffer,
+ channel_map);
+}
+
+// Tests that planar (float/int16_t) data can be correctly written into
+// |AudioBuffer|s, with remapping of channels.
+TEST_P(PlanarInterleavedConverterTest, TestPlanarPtrsIntoAudioBufferRemapping) {
+ const size_t num_channels = ::testing::get<0>(GetParam());
+ const size_t num_frames = ::testing::get<1>(GetParam());
+
+ AudioBuffer expected_output(num_channels, num_frames);
+ FillPlanar(&expected_output);
+
+ std::vector<size_t> channel_map(num_channels);
+ for (size_t c = 0; c < num_channels; ++c) {
+ channel_map[c] = c;
+ }
+ // Permute the channel map.
+ std::next_permutation(channel_map.begin(), channel_map.end());
+ // Create output buffers memory.
+ AudioBuffer output_buffer(num_channels, num_frames);
+
+ // Integer.
+ std::vector<AudioBuffer::AlignedInt16Vector>
+ planar_aligned_channels_int_bufferss(
+ num_channels, AudioBuffer::AlignedInt16Vector(num_frames));
+ std::vector<std::vector<int16_t>> planar_unaligned_channels_int_buffers(
+ num_channels, std::vector<int16_t>(num_frames));
+
+ std::vector<int16_t*> planar_aligned_channels_int(num_channels);
+ std::vector<int16_t*> planar_unaligned_channels_int(num_channels);
+ for (size_t c = 0; c < num_channels; ++c) {
+ planar_aligned_channels_int[c] =
+ planar_aligned_channels_int_bufferss[c].data();
+ planar_unaligned_channels_int[c] =
+ planar_unaligned_channels_int_buffers[c].data();
+ }
+
+ FillPlanar(num_frames, &planar_aligned_channels_int);
+ FillPlanar(num_frames, &planar_unaligned_channels_int);
+ // Aligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithChannelRemapping(&planar_aligned_channels_int[0],
+ num_frames, num_channels, channel_map,
+ &output_buffer);
+ VerifyOutput(num_channels, num_frames, expected_output, output_buffer,
+ channel_map);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithChannelRemapping(&planar_unaligned_channels_int[0],
+ num_frames, num_channels, channel_map,
+ &output_buffer);
+ VerifyOutput(num_channels, num_frames, expected_output, output_buffer,
+ channel_map);
+
+ // Floating point.
+ std::vector<AudioBuffer::AlignedFloatVector>
+ planar_aligned_channels_float_buffers(
+ num_channels, AudioBuffer::AlignedFloatVector(num_frames));
+ std::vector<std::vector<float>> planar_unaligned_channels_float_buffers(
+ num_channels, std::vector<float>(num_frames));
+
+ std::vector<float*> planar_aligned_channels_float(num_channels);
+ std::vector<float*> planar_unaligned_channels_float(num_channels);
+ for (size_t c = 0; c < num_channels; ++c) {
+ planar_aligned_channels_float[c] =
+ planar_aligned_channels_float_buffers[c].data();
+ planar_unaligned_channels_float[c] =
+ planar_unaligned_channels_float_buffers[c].data();
+ }
+ FillPlanar(num_frames, &planar_aligned_channels_float);
+ FillPlanar(num_frames, &planar_unaligned_channels_float);
+ // Aligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithChannelRemapping(&planar_aligned_channels_float[0],
+ num_frames, num_channels, channel_map,
+ &output_buffer);
+ VerifyOutput(num_channels, num_frames, expected_output, output_buffer,
+ channel_map);
+ // Unaligned Input, Aligned Output.
+ output_buffer.Clear();
+ FillAudioBufferWithChannelRemapping(&planar_unaligned_channels_float[0],
+ num_frames, num_channels, channel_map,
+ &output_buffer);
+ VerifyOutput(num_channels, num_frames, expected_output, output_buffer,
+ channel_map);
+}
+
+// Tests that an |AudioBuffer| can be correctly written into an interleaved
+// vector of (float/int16_t) data.
+TEST_P(PlanarInterleavedConverterTest, TestAudioBufferIntoInterleavedVector) {
+ const size_t num_channels = ::testing::get<0>(GetParam());
+ const size_t num_frames = ::testing::get<1>(GetParam());
+ const size_t num_interleaved_samples = num_frames * num_channels;
+ std::vector<float> expected_output(num_interleaved_samples);
+ FillInterleaved(num_channels, num_frames, &expected_output[0]);
+
+ // Floating point.
+ AudioBuffer planar_input(num_channels, num_frames);
+ FillPlanar(&planar_input);
+ // Aligned Input, Unaligned Output.
+ std::vector<float> interleaved_output;
+ FillExternalBuffer(planar_input, &interleaved_output);
+ VerifyOutput(num_frames * num_channels, expected_output, interleaved_output);
+}
+
+// Tests that an |AudioBuffer| can be correctly written into an interleaved
+// (float/int16_t) array.
+TEST_P(PlanarInterleavedConverterTest, TestAudioBufferIntoInterleavedPtr) {
+ const size_t num_channels = ::testing::get<0>(GetParam());
+ const size_t num_frames = ::testing::get<1>(GetParam());
+ const size_t num_interleaved_samples = num_frames * num_channels;
+
+ std::vector<float> expected_output(num_interleaved_samples);
+ FillInterleaved(num_channels, num_frames, &expected_output[0]);
+
+ AudioBuffer planar_input(num_channels, num_frames);
+ FillPlanar(&planar_input);
+
+ // Integer.
+ AudioBuffer::AlignedInt16Vector interleaved_aligned_int(
+ num_interleaved_samples);
+ std::vector<int16_t> interleaved_unaligned_int(num_interleaved_samples);
+
+ // Aligned Input, Aligned Output.
+ DCHECK_EQ(interleaved_aligned_int.size(),
+ planar_input.num_frames() * planar_input.num_channels());
+ FillExternalBuffer(planar_input, interleaved_aligned_int.data(),
+ planar_input.num_frames(), planar_input.num_channels());
+ VerifyOutput(num_frames * num_channels, expected_output,
+ interleaved_aligned_int.data());
+ // Aligned Input, Unaligned Output.
+ DCHECK_EQ(interleaved_unaligned_int.size(),
+ planar_input.num_frames() * planar_input.num_channels());
+ FillExternalBuffer(planar_input, interleaved_unaligned_int.data(),
+ planar_input.num_frames(), planar_input.num_channels());
+ VerifyOutput(num_frames * num_channels, expected_output,
+ interleaved_unaligned_int);
+
+ // Floating point.
+ AudioBuffer::AlignedFloatVector interleaved_aligned_float(
+ num_interleaved_samples);
+ std::vector<float> interleaved_plane_float(num_interleaved_samples);
+
+ // Aligned Input, Aligned Output.
+ DCHECK_EQ(interleaved_aligned_float.size(),
+ planar_input.num_frames() * planar_input.num_channels());
+ FillExternalBuffer(planar_input, interleaved_aligned_float.data(),
+ planar_input.num_frames(), planar_input.num_channels());
+ VerifyOutput(num_frames * num_channels, expected_output,
+ interleaved_aligned_float.data());
+ // Aligned Input, Unaligned Output.
+ FillExternalBuffer(planar_input, &interleaved_plane_float[0],
+ planar_input.num_frames(), planar_input.num_channels());
+ VerifyOutput(num_frames * num_channels, expected_output,
+ interleaved_plane_float);
+}
+
+// Test Params define: channels, frames
+INSTANTIATE_TEST_CASE_P(
+ TestParameters, PlanarInterleavedConverterTest,
+ testing::Values(
+ TestParams(2, 8), TestParams(2, 13), TestParams(2, kMaxNumFrames),
+ TestParams(4, 8), TestParams(4, 13), TestParams(4, kMaxNumFrames),
+ TestParams(5, 8), TestParams(5, 13), TestParams(5, kMaxNumFrames),
+ TestParams(kMaxNumChannels, 8), TestParams(kMaxNumChannels, 13),
+ TestParams(kMaxNumChannels, kMaxNumFrames)));
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/pseudoinverse.h b/src/3rdparty/resonance-audio/resonance_audio/utils/pseudoinverse.h
new file mode 100644
index 000000000..17b4b2934
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/pseudoinverse.h
@@ -0,0 +1,45 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_PSEUDOINVERSE_H_
+#define RESONANCE_AUDIO_UTILS_PSEUDOINVERSE_H_
+
+#include "Eigen/Dense"
+
+namespace vraudio {
+
+// Computes the Moore-Penrose pseudoinverse of |matrix|.
+//
+// @tparam MatrixType The type of the input matrix (an Eigen::Matrix).
+// @param matrix The input matrix to compute the pseudoinverse of.
+// @return The Moore-Penrose pseudoinverse of |matrix|.
+template <typename MatrixType>
+Eigen::Matrix<typename MatrixType::Scalar, MatrixType::ColsAtCompileTime,
+ MatrixType::RowsAtCompileTime>
+Pseudoinverse(const MatrixType& matrix) {
+ Eigen::JacobiSVD<Eigen::Matrix<typename MatrixType::Scalar, Eigen::Dynamic,
+ Eigen::Dynamic>> svd(matrix,
+ Eigen::ComputeThinU |
+ Eigen::ComputeThinV);
+ return svd.solve(
+ Eigen::Matrix<typename MatrixType::Scalar, MatrixType::RowsAtCompileTime,
+ MatrixType::RowsAtCompileTime>::Identity(matrix.rows(),
+ matrix.rows()));
+}
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_PSEUDOINVERSE_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/pseudoinverse_test.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/pseudoinverse_test.cc
new file mode 100644
index 000000000..c71e97595
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/pseudoinverse_test.cc
@@ -0,0 +1,87 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/pseudoinverse.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/constants_and_types.h"
+
+namespace vraudio {
+
+namespace {
+
+// Tests that the pseudoinverse of an invertible square matrix is equal to the
+// inverse of that matrix.
+TEST(PseudoinverseTest, SquareInverse) {
+ Eigen::Matrix<double, 5, 5> invertible_matrix;
+ invertible_matrix << 0.8478, 0.1676, 0.1961, 0.2654, 0.7662, 0.0279, 0.5309,
+ 0.2043, 0.4947, 0.0918, 0.1367, 0.4714, 0.7113, 0.246, 0.8048, 0.9617,
+ 0.4378, 0.0259, 0.536, 0.9565, 0.1541, 0.6275, 0.8471, 0.1133, 0.8074;
+ ASSERT_NE(0.0, invertible_matrix.determinant());
+
+ auto pseudoinverse = Pseudoinverse(invertible_matrix);
+ auto inverse = invertible_matrix.inverse();
+
+ EXPECT_TRUE(pseudoinverse.isApprox(inverse, kEpsilonDouble))
+ << "Pseudoinverse: \n"
+ << pseudoinverse << " should be within " << kEpsilonDouble
+ << " of inverse: \n"
+ << inverse;
+}
+
+// Tests that the pseudoinverse of a full-rank matrix with more rows than
+// columns works successfully.
+TEST(PseudoinverseTest, PseudoinverseMoreRows) {
+ Eigen::Matrix<double, 5, 4> invertible_matrix;
+ invertible_matrix << 0.8478, 0.1676, 0.1961, 0.2654, 0.0279, 0.5309, 0.2043,
+ 0.4947, 0.1367, 0.4714, 0.7113, 0.246, 0.9617, 0.4378, 0.0259, 0.536,
+ 0.1541, 0.6275, 0.8471, 0.1133;
+
+ auto pseudoinverse = Pseudoinverse(invertible_matrix);
+ auto should_be_identity = pseudoinverse * invertible_matrix;
+
+ EXPECT_TRUE(should_be_identity.isApprox(
+ decltype(should_be_identity)::Identity(should_be_identity.rows(),
+ should_be_identity.cols()),
+ kEpsilonDouble))
+ << "Matrix should be within " << kEpsilonDouble
+ << " of an identity matrix: \n"
+ << should_be_identity;
+}
+
+// Tests that the pseudoinverse of a full-rank matrix with more columns than
+// rows works successfully.
+TEST(PseudoinverseTest, PseudoinverseMoreColumns) {
+ Eigen::Matrix<double, 4, 5> invertible_matrix;
+ invertible_matrix << 0.8478, 0.1676, 0.1961, 0.2654, 0.7662, 0.0279, 0.5309,
+ 0.2043, 0.4947, 0.0918, 0.1367, 0.4714, 0.7113, 0.246, 0.8048, 0.9617,
+ 0.4378, 0.0259, 0.536, 0.9565;
+
+ auto pseudoinverse = Pseudoinverse(invertible_matrix);
+ auto should_be_identity = invertible_matrix * pseudoinverse;
+
+ EXPECT_TRUE(should_be_identity.isApprox(
+ decltype(should_be_identity)::Identity(should_be_identity.rows(),
+ should_be_identity.cols()),
+ kEpsilonDouble))
+ << "Matrix should be within " << kEpsilonDouble
+ << " of an identity matrix: \n"
+ << should_be_identity;
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/sample_type_conversion.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/sample_type_conversion.cc
new file mode 100644
index 000000000..fdd010cb9
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/sample_type_conversion.cc
@@ -0,0 +1,40 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "utils/sample_type_conversion.h"
+
+#include "base/simd_utils.h"
+
+namespace vraudio {
+
+void ConvertPlanarSamples(size_t length, const int16* input, float* output) {
+ FloatFromInt16(length, input, output);
+}
+
+void ConvertPlanarSamples(size_t length, const float* input, float* output) {
+ std::copy_n(input, length, output);
+}
+
+void ConvertPlanarSamples(size_t length, const float* input, int16* output) {
+ Int16FromFloat(length, input, output);
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/sample_type_conversion.h b/src/3rdparty/resonance-audio/resonance_audio/utils/sample_type_conversion.h
new file mode 100644
index 000000000..5fcb1a33c
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/sample_type_conversion.h
@@ -0,0 +1,79 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_SAMPLE_TYPE_CONVERSION_H_
+#define RESONANCE_AUDIO_UTILS_SAMPLE_TYPE_CONVERSION_H_
+
+#include <algorithm>
+
+#include "base/integral_types.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+// Convert the given int16 to a float in the range [-1.0f, 1.0f].
+inline void ConvertSampleToFloatFormat(int16 input, float* output) {
+ DCHECK(output);
+ static const float kInt16Max = static_cast<float>(0x7FFF);
+ static const float kInt16ToFloat = 1.0f / kInt16Max;
+ *output = input * kInt16ToFloat;
+}
+
+// Overloaded input argument to support sample type templated methods.
+inline void ConvertSampleToFloatFormat(float input, float* output) {
+ DCHECK(output);
+ *output = input;
+}
+
+// Saturating if the float is not in [-1.0f, 1.0f].
+inline void ConvertSampleFromFloatFormat(float input, int16* output) {
+ DCHECK(output);
+ // Convert the given float to an int16 in the range
+ // [-32767 (0x7FFF), 32767 (0x7FFF)],
+ static const float kInt16Min = static_cast<float>(-0x7FFF);
+ static const float kInt16Max = static_cast<float>(0x7FFF);
+ static const float kFloatToInt16 = kInt16Max;
+ const float scaled_value = input * kFloatToInt16;
+ const float clamped_value =
+ std::min(kInt16Max, std::max(kInt16Min, scaled_value));
+ *output = static_cast<int16>(clamped_value);
+}
+
+// Overloaded output argument to support sample type templated methods.
+inline void ConvertSampleFromFloatFormat(float input, float* output) {
+ DCHECK(output);
+ *output = input;
+}
+
+// Convert a vector of int16 samples to float format in the range [-1.0f, 1.0f].
+void ConvertPlanarSamples(size_t length, const int16* input,
+ float* output);
+
+// Overloaded input argument to support sample type templated methods.
+void ConvertPlanarSamples(size_t length, const float* input,
+ float* output);
+
+// Overloaded method to support methods templated against the input sample type.
+void ConvertPlanarSamples(size_t length, const float* input,
+ int16* output);
+
+// Overloaded output argument to support sample type templated methods.
+void ConvertPlanarSamples(size_t length, const float* input,
+ float* output);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_SAMPLE_TYPE_CONVERSION_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/sample_type_conversion_test.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/sample_type_conversion_test.cc
new file mode 100644
index 000000000..8d4e84243
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/sample_type_conversion_test.cc
@@ -0,0 +1,80 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/sample_type_conversion.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+
+namespace vraudio {
+
+namespace {
+
+TEST(MiscMath, Int16ToFloatTest) {
+ static const int16 kMinInt16 = -0x7FFF;
+ static const int16 kMaxInt16 = 0x7FFF;
+ static const float kMinFloat = -1.0f;
+ static const float kMaxFloat = 1.0f;
+
+ static const float kFloatRange = kMaxFloat - kMinFloat;
+ static const uint16 kInt16Range = kMaxInt16 - kMinInt16;
+
+ for (int16 i = kMinInt16; i < kMaxInt16; i = static_cast<int16>(i + 0xFF)) {
+ const float mapped_float =
+ static_cast<float>(i) / static_cast<float>(kInt16Range) * kFloatRange;
+ float float_result = 0.0f;
+ ConvertSampleToFloatFormat(i, &float_result);
+ EXPECT_FLOAT_EQ(mapped_float, float_result);
+ }
+}
+
+TEST(MiscMath, FloatToInt16Test) {
+ static const int16 kMinInt16 = -0x7FFF;
+ static const int16 kMaxInt16 = 0x7FFF;
+ static const float kMinFloat = -1.0f;
+ static const float kMaxFloat = 1.0f;
+ // NOTE: Int16 maximum is 0x7FFF, NOT 0x8000; see scheme 2) in
+ // http://goo.gl/NTRQ1a for background.
+ static const float kFloatRange = kMaxFloat - kMinFloat;
+ static const uint16 kInt16Range = kMaxInt16 - kMinInt16;
+
+ for (float i = kMinFloat; i < kMaxFloat; i += 0.005f) {
+ const int16 mapped_int = static_cast<int16>(i * kInt16Range / kFloatRange);
+ int16 int16_result = 0;
+ ConvertSampleFromFloatFormat(i, &int16_result);
+ EXPECT_EQ(mapped_int, int16_result);
+ }
+}
+
+TEST(MiscMath, FloatToInt16TestPositiveSaturate) {
+ // Maximum positive value is 2^15 - 1
+ static const int16 kMaxInt16 = 0x7FFF;
+ static const float kMaxFloat = 1.0f;
+ int16 int16_result = 0;
+ ConvertSampleFromFloatFormat(2 * kMaxFloat, &int16_result);
+ EXPECT_EQ(kMaxInt16, int16_result);
+}
+
+TEST(MiscMath, FloatToInt16TestNegativeSaturate) {
+ static const int16 kMinInt16 = -0x7FFF;
+ static const float kMinFloat = -1.0f;
+ int16 int16_result = 0;
+ ConvertSampleFromFloatFormat(2 * kMinFloat, &int16_result);
+ EXPECT_EQ(kMinInt16, int16_result);
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/semi_lockless_fifo.h b/src/3rdparty/resonance-audio/resonance_audio/utils/semi_lockless_fifo.h
new file mode 100644
index 000000000..81950036b
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/semi_lockless_fifo.h
@@ -0,0 +1,232 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_SEMI_LOCKLESS_FIFO_H_
+#define RESONANCE_AUDIO_UTILS_SEMI_LOCKLESS_FIFO_H_
+
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <vector>
+
+#include "base/logging.h"
+
+namespace vraudio {
+
+// Thread-safe multiple producer - single consumer FIFO queue to share data
+// between threads. The FIFO takes over ownership of the queue elements. Note
+// that |PushBack| calls are synchronized with a mutex and may block. Calls to
+// |PopFront| are lockless and never block.
+//
+// @tparam DataType Object type that the FIFO handles.
+template <typename DataType>
+class SemiLocklessFifo {
+ public:
+ typedef std::chrono::steady_clock::duration ClockDuration;
+
+ SemiLocklessFifo();
+
+ ~SemiLocklessFifo();
+
+ // Takes over ownership of |input| and pushes it to the FIFO queue back.
+ //
+ // @param input Input element to be added to the FIFO queue.
+ void PushBack(DataType&& input);
+
+ // Pops element from FIFO queue front.
+ //
+ // @return Element from FIFO queue front. Must not be called if the queue is
+ // empty.
+ DataType PopFront();
+
+ // Returns true if FIFO queue is empty, false otherwise. This method is *not*
+ // thread-safe and should only be called from the consumer thread.
+ bool Empty() const;
+
+ // Clears the FIFO queue and deletes all its elements. This method is *not*
+ // thread-safe and should only be called from the consumer thread.
+ void Clear();
+
+ // Sleeps until the number of elements in the FIFO queue drop below a target
+ // threshold. This method can be used to synchronize the producer and the
+ // consumer. Sleeping is enabled by default and can be disabled via
+ // |EnableBlockingSleepUntilMethods|.
+ //
+ // @param target_size Target size of FIFO queue.
+ // @param max_wait Maximum waiting period.
+ // @return True if number of FIFO elements is below target size.
+ bool SleepUntilBelowSizeTarget(size_t target_size,
+ const ClockDuration& max_wait);
+
+ // Sleeps until the number of elements in the FIFO queue is greater or equal a
+ // target threshold. This method can be used to synchronize the producer and
+ // the consumer. Sleeping is enabled by default and can be disabled via
+ // |EnableBlockingSleepUntilMethods|.
+ //
+ // @param target_size Target size of FIFO queue.
+ // @param max_wait Maximum waiting period.
+ // @return True if number of FIFO elements is greater or equal the target
+ // size.
+ bool SleepUntilNumElementsInQueue(size_t target_size,
+ const ClockDuration& max_wait);
+
+ // Allows for unblocking |SleepUntil[BelowSizeTarget|NumElementsInQueue]|
+ // method.
+ void EnableBlockingSleepUntilMethods(bool enable);
+
+ private:
+ // Node in single-linked list.
+ struct Node {
+ Node() : next(nullptr) {}
+ std::atomic<Node*> next;
+ DataType data;
+ };
+
+ // Head of linked list.
+ Node* head_;
+
+ // Tail of linked list.
+ Node* tail_;
+
+ // Number of elements.
+ std::atomic<size_t> fifo_size_;
+
+ // Mutex to synchronize |PushBack| calls from multiple threads.
+ std::mutex push_mutex_;
+
+ // Conditional to signal consumption.
+ std::condition_variable pop_conditional_;
+
+ // Mutex to block on until signal consumption occurs.
+ std::mutex pop_conditional_mutex_;
+
+ // Conditional to signal new elements on the FIFO.
+ std::condition_variable push_conditional_;
+
+ // Mutex to block on until new elements have been added to the FIFO.
+ std::mutex push_conditional_mutex_;
+
+ // Flag to enable and disable blocking sleeping calls.
+ std::atomic<bool> enable_sleeping_;
+};
+
+template <typename DataType>
+SemiLocklessFifo<DataType>::SemiLocklessFifo()
+ : fifo_size_(0), enable_sleeping_(true) {
+ head_ = tail_ = new Node();
+}
+
+template <typename DataType>
+SemiLocklessFifo<DataType>::~SemiLocklessFifo() {
+ Clear();
+ DCHECK_EQ(head_, tail_);
+ DCHECK(head_->next.load() == nullptr);
+ delete head_;
+}
+
+template <typename DataType>
+void SemiLocklessFifo<DataType>::PushBack(DataType&& input) {
+ std::lock_guard<std::mutex> lock(push_mutex_);
+ tail_->data = std::move(input);
+ Node* const new_node = new Node();
+ DCHECK(tail_->next.load() == nullptr);
+ tail_->next = new_node;
+ tail_ = new_node;
+ ++fifo_size_;
+
+ {
+ // Taking the lock and dropping it immediately assure that the notify
+ // cannot happen between the check of the predicate and wait of the
+ // |push_conditional_|.
+ std::lock_guard<std::mutex> lock(push_conditional_mutex_);
+ }
+ push_conditional_.notify_all();
+}
+
+template <typename DataType>
+DataType SemiLocklessFifo<DataType>::PopFront() {
+ DCHECK(!Empty());
+
+ Node* const front_node = head_;
+ head_ = front_node->next;
+
+ DataType output = std::move(front_node->data);
+ delete front_node;
+
+ DCHECK_GT(fifo_size_.load(), 0u);
+ --fifo_size_;
+
+ {
+ // Taking the lock and dropping it immediately assure that the notify
+ // cannot happen between the check of the predicate and wait of the
+ // |pop_conditional_|.
+ std::lock_guard<std::mutex> lock(pop_conditional_mutex_);
+ }
+ pop_conditional_.notify_one();
+ return output;
+}
+
+template <typename DataType>
+bool SemiLocklessFifo<DataType>::Empty() const {
+ return fifo_size_.load() == 0;
+}
+
+template <typename DataType>
+void SemiLocklessFifo<DataType>::Clear() {
+ while (!Empty()) {
+ PopFront();
+ }
+ DCHECK_EQ(fifo_size_, 0u);
+}
+
+template <typename DataType>
+bool SemiLocklessFifo<DataType>::SleepUntilBelowSizeTarget(
+ size_t target_size, const ClockDuration& max_wait) {
+ DCHECK_GT(target_size, 0);
+ std::unique_lock<std::mutex> lock(pop_conditional_mutex_);
+ pop_conditional_.wait_for(lock, max_wait, [this, target_size]() {
+ return fifo_size_ < target_size || !enable_sleeping_.load();
+ });
+ return fifo_size_ < target_size;
+}
+
+template <typename DataType>
+bool SemiLocklessFifo<DataType>::SleepUntilNumElementsInQueue(
+ size_t target_size, const ClockDuration& max_wait) {
+ DCHECK_GT(target_size, 0u);
+ std::unique_lock<std::mutex> lock(push_conditional_mutex_);
+ push_conditional_.wait_for(lock, max_wait, [this, target_size]() {
+ return fifo_size_ >= target_size || !enable_sleeping_.load();
+ });
+ return fifo_size_ >= target_size;
+}
+
+template <typename DataType>
+void SemiLocklessFifo<DataType>::EnableBlockingSleepUntilMethods(bool enable) {
+ enable_sleeping_ = enable;
+ // Taking the lock and dropping it immediately assure that the notify
+ // cannot happen between the check of the predicate and wait of the
+ // |pop_conditional_| and |push_conditional_|.
+ { std::lock_guard<std::mutex> lock(pop_conditional_mutex_); }
+ { std::lock_guard<std::mutex> lock(push_conditional_mutex_); }
+ pop_conditional_.notify_one();
+ push_conditional_.notify_one();
+}
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_SEMI_LOCKLESS_FIFO_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/sum_and_difference_processor.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/sum_and_difference_processor.cc
new file mode 100644
index 000000000..e6d9fe862
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/sum_and_difference_processor.cc
@@ -0,0 +1,40 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/sum_and_difference_processor.h"
+
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+
+
+namespace vraudio {
+
+SumAndDifferenceProcessor::SumAndDifferenceProcessor(size_t num_frames)
+ : temp_buffer_(kNumMonoChannels, num_frames) {}
+
+void SumAndDifferenceProcessor::Process(AudioBuffer* stereo_buffer) {
+
+ DCHECK_EQ(stereo_buffer->num_channels(), kNumStereoChannels);
+ AudioBuffer::Channel* temp_channel = &temp_buffer_[0];
+ // channel_1' = channel_1 + channel_2;
+ // channel_2' = channel_1 - channel_2;
+ *temp_channel = (*stereo_buffer)[0];
+ *temp_channel -= (*stereo_buffer)[1];
+ (*stereo_buffer)[0] += (*stereo_buffer)[1];
+ (*stereo_buffer)[1] = *temp_channel;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/sum_and_difference_processor.h b/src/3rdparty/resonance-audio/resonance_audio/utils/sum_and_difference_processor.h
new file mode 100644
index 000000000..76804a9c7
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/sum_and_difference_processor.h
@@ -0,0 +1,44 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_SUM_AND_DIFFERENCE_PROCESSOR_H_
+#define RESONANCE_AUDIO_UTILS_SUM_AND_DIFFERENCE_PROCESSOR_H_
+
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Class which converts a 2-channel input audio buffer into its sum and
+// difference signals and stores them in the left and right channel
+// respectively.
+class SumAndDifferenceProcessor {
+ public:
+ // Constructs a stereo sum and difference processor.
+ //
+ // @param num_frames Number of frames in the stereo input audio buffer.
+ explicit SumAndDifferenceProcessor(size_t num_frames);
+
+ // Converts a 2-channel buffer signals into their sum and difference.
+ void Process(AudioBuffer* stereo_buffer);
+
+ private:
+ // Temporary audio buffer to store left channel input data during conversion.
+ AudioBuffer temp_buffer_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_SUM_AND_DIFFERENCE_PROCESSOR_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/sum_and_difference_processor_test.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/sum_and_difference_processor_test.cc
new file mode 100644
index 000000000..e80acbdb5
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/sum_and_difference_processor_test.cc
@@ -0,0 +1,42 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/sum_and_difference_processor.h"
+
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+
+namespace vraudio {
+
+// Tests Process method.
+TEST(SumAndDifferenceProcessor, TestProcessMethod) {
+ static const std::vector<std::vector<float>> kTestVector = {
+ {0.0f, 1.0f, 2.0f}, {3.0f, 4.0f, 5.0f}};
+
+ AudioBuffer audio_buffer(kTestVector.size(), kTestVector[0].size());
+ audio_buffer = kTestVector;
+
+ SumAndDifferenceProcessor processor(audio_buffer.num_frames());
+ processor.Process(&audio_buffer);
+
+ for (size_t frame = 0; frame < kTestVector[0].size(); ++frame) {
+ EXPECT_EQ(kTestVector[0][frame] + kTestVector[1][frame],
+ audio_buffer[0][frame]);
+ EXPECT_EQ(kTestVector[0][frame] - kTestVector[1][frame],
+ audio_buffer[1][frame]);
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/task_thread_pool.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/task_thread_pool.cc
new file mode 100644
index 000000000..485e834df
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/task_thread_pool.cc
@@ -0,0 +1,249 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/task_thread_pool.h"
+
+#include <thread>
+
+#include "base/integral_types.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+// A simple worker thread wrapper, to be used by TaskThreadPool.
+class TaskThreadPool::WorkerThread {
+ public:
+ // Constructor.
+ //
+ // @param parent_pool The |TaskThreadPool| which owns and manages this
+ // |WorkerThread| instance.
+ WorkerThread()
+ : parent_pool_(), task_closure_(), task_loop_triggered_(false) {}
+
+ // Copy constructor. Necessary for allowing this class to be storied in
+ // std::vector<WorkerThread> container class.
+ //
+ // @param other |WorkerThread| instance this instance will be copied from.
+ WorkerThread(const TaskThreadPool::WorkerThread& other)
+ : parent_pool_(), task_closure_(), task_loop_triggered_(false) {}
+
+ // Destructor.
+ ~WorkerThread();
+
+ // Sets the parent pool for this |WorkerThread|.
+ //
+ // @param parent_pool The |TaskThreadPool| which owns this |WorkerThread|.
+ // @return True indicates that the thread could be started.
+ bool SetParentAndStart(TaskThreadPool* parent_pool);
+
+ // Indicates that the worker thread should run the |TaskClosure| task.
+ //
+ // @param task_closure The task function which |TaskThreadPool| assigns to
+ // this |WorkerThread|.
+ void Run(TaskClosure task_closure);
+
+ // Waits for the |WorkerThread| task loop to exit, for shutdown.
+ void Join();
+
+ // Checks whether this |WorkerThread| is available for a task assignment.
+ bool IsAvailable() const;
+
+ private:
+ // The task loop for this |WorkerThread|. This function will wait for an
+ // assigned task, execute the task, and then reset itself to wait for the next
+ // task.
+ void TaskLoop();
+
+ // This |WorkerThread|'s parent |TaskThreadPool|.
+ TaskThreadPool* parent_pool_;
+
+ // Condition allowing the |TaskThreadPool| to trigger this worker thread to
+ // continue once it has been given a work assignment.
+ std::condition_variable execute_task_condition_;
+
+ // Mutex for receiving |execute_task_condition_| notification.
+ std::mutex execute_task_mutex_;
+
+ // The current task binding which the Worker Thread has been asked to
+ // execute.
+ TaskClosure task_closure_;
+
+ // An atomic boolean to indicate that a task loop trigger has occurred. This
+ // may happen even when a task has not been assigned during shutdown.
+ std::atomic<bool> task_loop_triggered_;
+
+ // The worker thread.
+ std::thread task_thread_;
+};
+
+TaskThreadPool::TaskThreadPool()
+ : num_worker_threads_available_(0),
+ is_pool_running_(false) {}
+
+TaskThreadPool::~TaskThreadPool() { StopThreadPool(); }
+
+bool TaskThreadPool::StartThreadPool(size_t num_worker_threads) {
+ if (is_pool_running_) {
+ return true;
+ }
+ is_pool_running_ = true;
+ worker_threads_.resize(num_worker_threads);
+
+ // Start all worker threads.
+ for (auto& worker_thread : worker_threads_) {
+ bool thread_started = worker_thread.SetParentAndStart(this);
+ if (!thread_started) {
+ StopThreadPool();
+ return false;
+ }
+ }
+
+ // Wait for all worker threads to be launched.
+ std::unique_lock<std::mutex> worker_lock(worker_available_mutex_);
+ worker_available_condition_.wait(worker_lock, [this, num_worker_threads]() {
+ return !is_pool_running_.load() || num_worker_threads_available_.load() ==
+ static_cast<int>(num_worker_threads);
+ });
+
+ return true;
+}
+
+void TaskThreadPool::StopThreadPool() {
+ if (!is_pool_running_) {
+ return;
+ }
+ // Shut down all active worker threads.
+ {
+ std::lock_guard<std::mutex> worker_lock(worker_available_mutex_);
+ is_pool_running_ = false;
+ }
+ worker_available_condition_.notify_one();
+
+ // Join and destruct workers.
+ worker_threads_.resize(0);
+}
+
+bool TaskThreadPool::WaitUntilWorkerBecomesAvailable() {
+ if (!is_pool_running_.load()) {
+ return false;
+ }
+ if (num_worker_threads_available_.load() > 0) {
+ return true;
+ }
+ std::unique_lock<std::mutex> worker_lock(worker_available_mutex_);
+ worker_available_condition_.wait(worker_lock, [this]() {
+ return (num_worker_threads_available_.load() > 0) ||
+ !is_pool_running_.load();
+ });
+ return num_worker_threads_available_.load() > 0 && is_pool_running_.load();
+}
+
+bool TaskThreadPool::RunOnWorkerThread(TaskThreadPool::TaskClosure closure) {
+ if (!is_pool_running_.load() || num_worker_threads_available_.load() == 0) {
+ return false;
+ }
+ // Find the first available worker thread.
+ WorkerThread* available_worker_thread = nullptr;
+ for (auto& worker_thread : worker_threads_) {
+ if (worker_thread.IsAvailable()) {
+ available_worker_thread = &worker_thread;
+ break;
+ }
+ }
+ DCHECK(available_worker_thread);
+ {
+ std::lock_guard<std::mutex> lock(worker_available_mutex_);
+ --num_worker_threads_available_;
+ }
+ available_worker_thread->Run(std::move(closure));
+ return true;
+}
+
+size_t TaskThreadPool::GetAvailableTaskThreadCount() const {
+ return num_worker_threads_available_.load();
+}
+
+bool TaskThreadPool::IsPoolRunning() { return is_pool_running_.load(); }
+
+void TaskThreadPool::SignalWorkerAvailable() {
+ {
+ std::lock_guard<std::mutex> lock(worker_available_mutex_);
+ ++num_worker_threads_available_;
+ }
+ worker_available_condition_.notify_one();
+}
+
+TaskThreadPool::WorkerThread::~WorkerThread() { Join(); }
+
+bool TaskThreadPool::WorkerThread::SetParentAndStart(
+ TaskThreadPool* parent_pool) {
+ parent_pool_ = parent_pool;
+
+ // Start the worker thread.
+ task_thread_ = std::thread(std::bind(&WorkerThread::TaskLoop, this));
+ return true;
+}
+
+void TaskThreadPool::WorkerThread::Run(TaskThreadPool::TaskClosure closure) {
+ if (closure) {
+ task_closure_ = std::move(closure);
+ {
+ std::lock_guard<std::mutex> lock(execute_task_mutex_);
+ task_loop_triggered_ = true;
+ }
+ execute_task_condition_.notify_one();
+ }
+}
+
+void TaskThreadPool::WorkerThread::Join() {
+ DCHECK(!parent_pool_->IsPoolRunning());
+ // Aquire and release lock to assure that the notify cannot happen between the
+ // check of the predicate and wait of the |push_conditional_|.
+ { std::lock_guard<std::mutex> lock(execute_task_mutex_); }
+ execute_task_condition_.notify_one();
+ if (task_thread_.joinable()) {
+ task_thread_.join();
+ }
+}
+
+bool TaskThreadPool::WorkerThread::IsAvailable() const {
+ return !task_loop_triggered_.load();
+}
+
+void TaskThreadPool::WorkerThread::TaskLoop() {
+ // Signal back to the parent thread pool that this thread has started and is
+ // ready for use.
+ task_loop_triggered_ = false;
+
+ while (parent_pool_->IsPoolRunning() || task_closure_ != nullptr) {
+ parent_pool_->SignalWorkerAvailable();
+ std::unique_lock<std::mutex> task_lock(execute_task_mutex_);
+ execute_task_condition_.wait(task_lock, [this]() {
+ return task_loop_triggered_.load() || !parent_pool_->IsPoolRunning();
+ });
+
+ // Execute the assigned task.
+ if (task_closure_ != nullptr) {
+ task_closure_();
+
+ // Clear the assigned task and return to ready state.
+ task_closure_ = nullptr;
+ }
+ task_loop_triggered_ = false;
+ }
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/task_thread_pool.h b/src/3rdparty/resonance-audio/resonance_audio/utils/task_thread_pool.h
new file mode 100644
index 000000000..e9db083f9
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/task_thread_pool.h
@@ -0,0 +1,121 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_TASK_THREAD_POOL_H_
+#define RESONANCE_AUDIO_UTILS_TASK_THREAD_POOL_H_
+
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+namespace vraudio {
+
+class TaskThreadPool;
+
+// A very basic thread pool for launching discrete encapsulated task functions
+// on a configurable number of task threads. Note that this pool expects tasks
+// to complete their work with no management by the pool itself. Any means of
+// managing or terminating tasks must be designed into the task contexts and
+// managed externally in a thread-safe way.
+class TaskThreadPool {
+ friend class WorkerThread;
+
+ public:
+ // Type definition for task function which may be assigned to a |WorkerThread|
+ // in this pool. Note that tasks should be self-contained and not require
+ // communication with other tasks.
+ typedef std::function<void()> TaskClosure;
+
+ // Constructor.
+ //
+ TaskThreadPool();
+
+ ~TaskThreadPool();
+
+ // Creates and initializes thread pool. This method blocks until all threads
+ // are loaded and initialized.
+ //
+ // @param num_worker_threads The number of worker threads to make available in
+ // the pool.
+ // @return true on success or if thread pool has been already started.
+ bool StartThreadPool(size_t num_worker_threads);
+
+ // Signals all |WorkerThread|s to stop and waits for completion.
+ void StopThreadPool();
+
+ // Waits until a |WorkerThread| becomes available. It is assumed that only a
+ // single thread will dispatch threads using this function, and therefore this
+ // function should not itself be considered thread safe.
+ //
+ // @return True if a |WorkerThread| is available.
+ bool WaitUntilWorkerBecomesAvailable();
+
+ // Executes a |TaskClosure| on a |WorkerThread|. It is assumed that only a
+ // signal thread will dispatch threads using this function, and therefore this
+ // function should not itself be considered thread safe.
+ //
+ // @param closure The client task which will begin execution if and when this
+ // function returns True.
+ // @return True if a |WorkerThread| is allocated to execute the closure
+ // function, false if no |WorkerThread| is available.
+ bool RunOnWorkerThread(TaskClosure closure);
+
+ // Query the number of |WorkerThread|s current available to do work.
+ size_t GetAvailableTaskThreadCount() const;
+
+ private:
+ // Forward declaration of |WorkerThread| class. See implementation file for
+ // class details.
+ class WorkerThread;
+
+ // Query whether the |TaskThreadPool| is active.
+ //
+ // @return True if the pool is still running.
+ bool IsPoolRunning();
+
+ // Signals to thread pool that a worker thread has become available for
+ // task assignment.
+ void SignalWorkerAvailable();
+
+ // Closure reusable task loop to be executed by each |WorkerThread|.
+ void WorkerLoopFunction();
+
+ // Task Loop executed by each worker thread.
+ void WorkerThreadLoop();
+
+ // Number of worker threads currently available to execute tasks.
+ std::atomic<int> num_worker_threads_available_;
+
+ // Control of all worker thread loops.
+ std::atomic<bool> is_pool_running_;
+
+ // Container of available worker threads, waiting to be used.
+
+ std::vector<WorkerThread> worker_threads_;
+
+ // Condition to indicate that a worker thread has become available.
+ std::condition_variable worker_available_condition_;
+
+ // Mutex for the worker thread available condition notification receiver.
+ std::mutex worker_available_mutex_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_TASK_THREAD_POOL_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/task_thread_pool_test.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/task_thread_pool_test.cc
new file mode 100644
index 000000000..61e83f2d9
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/task_thread_pool_test.cc
@@ -0,0 +1,185 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include <atomic>
+#include <chrono>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "base/integral_types.h"
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/logging.h"
+#include "utils/task_thread_pool.h"
+
+namespace vraudio {
+
+namespace {
+
+// The number of simultaneous worker threads to run for these tests.
+const size_t kNumberOfThreads = 5;
+
+// An extremely large number of worker threads to use when attempting to shut
+// down the |TaskThreadPool| while it is still being initialized.
+const size_t kHugeThreadCount = 64;
+
+// An arbitrary numeric value to set when testing worker threads.
+const int kModifiedValue = 113;
+
+// A limited number of iterations to perform when testing worker threads which
+// do continuous work over time.
+const size_t kNumberOfIncrementOperations = 50;
+
+// The number of times to repeat test loops in functions to insure reuse of
+// worker threads.
+const size_t kNumTestLoops = 3;
+
+class TaskThreadPoolTest : public ::testing::Test {
+ protected:
+ TaskThreadPoolTest() {}
+ ~TaskThreadPoolTest() override {}
+
+ void SetUp() override { modified_values_.resize(kNumberOfThreads, 0); }
+
+ public:
+ // Helper worker task to asynchronously set values in worker threads.
+ void ModifyValue(int* value_to_set) {
+ std::lock_guard<std::mutex> worker_lock(modification_mutex_);
+ EXPECT_NE(value_to_set, nullptr);
+ *value_to_set = kModifiedValue;
+ }
+
+ // Helper worker task to asynchronously increment modified values over time
+ // with enough interior delay to measure results.
+ void IncrementValue(int* value_to_increment) {
+ EXPECT_NE(value_to_increment, nullptr);
+ for (size_t i = 0; i < kNumberOfIncrementOperations; ++i) {
+ // Sleep briefly so this doesn't finish too quickly.
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ std::lock_guard<std::mutex> worker_lock(modification_mutex_);
+ *value_to_increment += 1;
+ }
+ }
+
+ protected:
+ // Vector of numbers used for testing asynchronous threading results.
+ std::vector<int> modified_values_;
+
+ // A mutex for protecting modified_values_ from tsan (Thread Sanitizer)
+ // failures.
+ std::mutex modification_mutex_;
+};
+
+// This test verifies that TaskThreadPool actually executes tasks.
+TEST_F(TaskThreadPoolTest, SetValuesInWorkerThreads) {
+ TaskThreadPool thread_pool;
+ EXPECT_TRUE(thread_pool.StartThreadPool(kNumberOfThreads));
+
+ // Run this several times to insure that worker threads can be reused.
+ for (size_t loop = 0; loop < kNumTestLoops; ++loop) {
+ // Verify that all worker threads are available again.
+ EXPECT_EQ(thread_pool.GetAvailableTaskThreadCount(), kNumberOfThreads);
+
+ for (size_t i = 0; i < kNumberOfThreads; ++i) {
+ modified_values_[i] = 0;
+ const bool task_available = thread_pool.WaitUntilWorkerBecomesAvailable();
+ EXPECT_TRUE(task_available);
+ const bool task_submitted = thread_pool.RunOnWorkerThread(
+ std::bind(&vraudio::TaskThreadPoolTest::ModifyValue, this,
+ &modified_values_[i]));
+ EXPECT_TRUE(task_submitted);
+ }
+
+ // Wait until all threads become available again (trying to time their
+ // completion seems to cause flakey tests).
+ while (thread_pool.GetAvailableTaskThreadCount() < kNumberOfThreads) {
+ // Wait briefly to allow tasks to execute.
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
+ }
+
+ // Check results.
+ for (size_t i = 0; i < kNumberOfThreads; ++i) {
+ std::lock_guard<std::mutex> worker_lock(modification_mutex_);
+ EXPECT_EQ(modified_values_[i], kModifiedValue);
+ }
+ }
+}
+
+// This test verifies that the |TaskThreadPool| cannot be shut down until all of
+// its worker threads are brought to a ready state.
+TEST_F(TaskThreadPoolTest, VerifyRapidShutdownWithLargeThreadCount) {
+ TaskThreadPool thread_pool;
+ EXPECT_TRUE(thread_pool.StartThreadPool(kHugeThreadCount));
+}
+
+// This test verifies the timeout features of assigning worker threads, as well
+// as the continuous asynchronous operation of threads..
+TEST_F(TaskThreadPoolTest, VerifyTimeoutsAndContinuousOperation) {
+ // Preset |modified_values_| to known state.
+ for (size_t i = 0; i < kNumberOfThreads; ++i) {
+ modified_values_[i] = 0;
+ }
+
+ {
+ TaskThreadPool thread_pool;
+ EXPECT_TRUE(thread_pool.StartThreadPool(kNumberOfThreads));
+
+ // Verify that all worker threads are available again.
+ EXPECT_EQ(thread_pool.GetAvailableTaskThreadCount(), kNumberOfThreads);
+
+ for (size_t i = 0; i < kNumberOfThreads; ++i) {
+ modified_values_[i] = 0;
+ const bool task_available = thread_pool.WaitUntilWorkerBecomesAvailable();
+ EXPECT_TRUE(task_available);
+ const bool task_submitted = thread_pool.RunOnWorkerThread(
+ std::bind(&vraudio::TaskThreadPoolTest::IncrementValue, this,
+ &modified_values_[i]));
+ EXPECT_TRUE(task_submitted);
+ }
+
+ // Verify that all worker threads are available again.
+ EXPECT_EQ(thread_pool.GetAvailableTaskThreadCount(), 0U);
+
+ // Trying to add one more task should fail.
+ int extra_modified;
+ EXPECT_FALSE(thread_pool.RunOnWorkerThread(std::bind(
+ &vraudio::TaskThreadPoolTest::IncrementValue, this, &extra_modified)));
+
+ // Wait briefly to allow tasks to execute.
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ // Verify that all of the tasks are still doing some work.
+ for (size_t i = 0; i < kNumberOfThreads; ++i) {
+ std::lock_guard<std::mutex> worker_lock(modification_mutex_);
+ EXPECT_GT(modified_values_[i], 0);
+ }
+ }
+ // To verify that all threads are shut down correctly, record the
+ // |modified_values_|, wait briefly, and then make sure theyΩ have not
+ // changed.
+ std::vector<int> copy_of_modified_values = modified_values_;
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ for (size_t i = 0; i < kNumberOfThreads; ++i) {
+ // NOTE: modification_mutex_ is intentionally not used for this block so
+ // that any race conditions might be caught if for some reason the worker
+ // threads have not yet shut down.
+ EXPECT_EQ(copy_of_modified_values[i], modified_values_[i]);
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/test_util.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/test_util.cc
new file mode 100644
index 000000000..4912ab8cb
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/test_util.cc
@@ -0,0 +1,190 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/test_util.h"
+
+#include <algorithm>
+#include <cmath>
+#include <string>
+
+#include "third_party/googletest/googlemock/include/gmock/gmock.h"
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/logging.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+namespace {
+
+using ::testing::NotNull;
+
+} // namespace
+
+void GenerateSilence(AudioBuffer::Channel* output) {
+ ASSERT_THAT(output, ::testing::NotNull());
+ output->Clear();
+}
+
+void GenerateSineWave(float frequency_hz, int sample_rate,
+ AudioBuffer::Channel* output) {
+ ASSERT_GE(frequency_hz, 0.0f);
+ ASSERT_GT(sample_rate, 0);
+ ASSERT_THAT(output, ::testing::NotNull());
+
+ for (size_t i = 0; i < output->size(); ++i) {
+ const float phase = static_cast<float>(i) * kTwoPi /
+ static_cast<float>(sample_rate) * frequency_hz;
+ (*output)[i] = std::sin(phase);
+ }
+}
+
+void GenerateSawToothSignal(size_t tooth_length_samples,
+ AudioBuffer::Channel* output) {
+ ASSERT_GT(tooth_length_samples, 0U);
+ ASSERT_THAT(output, ::testing::NotNull());
+ for (size_t i = 0; i < output->size(); ++i) {
+ (*output)[i] = static_cast<float>(i % tooth_length_samples) /
+ static_cast<float>(tooth_length_samples) * 2.0f -
+ 1.0f;
+ }
+}
+
+void GenerateDiracImpulseFilter(size_t delay_samples,
+ AudioBuffer::Channel* output) {
+ ASSERT_THAT(output, ::testing::NotNull());
+ ASSERT_LT(delay_samples, output->size());
+ ASSERT_THAT(output, ::testing::NotNull());
+ output->Clear();
+ (*output)[delay_samples] = 1.0f;
+}
+
+void GenerateIncreasingSignal(AudioBuffer::Channel* output) {
+ ASSERT_THAT(output, ::testing::NotNull());
+ for (size_t i = 0; i < output->size(); ++i) {
+ (*output)[i] =
+ static_cast<float>(i) / static_cast<float>(output->size()) * 2.0f -
+ 1.0f;
+ }
+}
+
+size_t ZeroCompare(const AudioBuffer::Channel& signal, float epsilon) {
+ for (size_t i = 0; i < signal.size(); ++i) {
+ if (std::abs(signal[i]) > epsilon) {
+ return i;
+ }
+ }
+ return signal.size();
+}
+
+bool CompareAudioBuffers(const AudioBuffer::Channel& buffer_a,
+ const AudioBuffer::Channel& buffer_b, float epsilon) {
+ if (buffer_a.size() != buffer_b.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < buffer_a.size(); ++i) {
+ if (std::abs(buffer_a[i] - buffer_b[i]) > epsilon) {
+ return false;
+ }
+ }
+ return true;
+}
+
+size_t DelayCompare(const AudioBuffer::Channel& original_signal,
+ const AudioBuffer::Channel& delayed_signal, size_t delay,
+ float epsilon) {
+ if (delay > delayed_signal.size() ||
+ (delayed_signal.size() > original_signal.size() + delay)) {
+ return 0;
+ }
+ for (size_t i = delay; i < delayed_signal.size(); ++i) {
+ const size_t original_index = i - delay;
+ const float difference =
+ std::abs(delayed_signal[i] - original_signal[original_index]);
+ if (difference > epsilon) {
+ return i;
+ }
+ }
+ return delayed_signal.size();
+}
+
+bool TestZeroPaddedDelay(const AudioBuffer::Channel& original_signal,
+ const AudioBuffer::Channel& delayed_signal,
+ size_t delay_samples, float epsilon) {
+ size_t temp = ZeroCompare(delayed_signal, epsilon);
+ if (delay_samples != temp) {
+ return false;
+ }
+ temp = DelayCompare(original_signal, delayed_signal, delay_samples, epsilon);
+ if (original_signal.size() != temp) {
+ return false;
+ }
+ return true;
+}
+
+double CalculateSignalPeak(const AudioBuffer::Channel& channel) {
+ double peak = 0.0;
+ for (const float& sample : channel) {
+ if (std::abs(sample) > peak) peak = std::abs(sample);
+ }
+
+ DCHECK_GT(channel.size(), 0);
+ return peak;
+}
+
+double CalculateSignalEnergy(const AudioBuffer::Channel& channel) {
+ double energy = 0.0;
+ for (const float& sample : channel) {
+ energy += sample * sample;
+ }
+ return energy;
+}
+
+double CalculateSignalRms(const AudioBuffer::Channel& channel) {
+ const double energy = CalculateSignalEnergy(channel);
+ DCHECK_GT(channel.size(), 0);
+ return std::sqrt(energy / static_cast<double>(channel.size()));
+}
+
+double DbFromMagnitude(double magnitude) {
+ DCHECK_GT(magnitude, 0.0);
+ const double decibel = 20.0 * std::log10(magnitude);
+ return decibel;
+}
+
+double DbFromPower(double power) {
+ DCHECK_GT(power, 0.0);
+ const double decibel = 10.0 * std::log10(power);
+ return decibel;
+}
+
+float MaxCrossCorrelation(const AudioBuffer::Channel& signal_a,
+ const AudioBuffer::Channel& signal_b) {
+ CHECK_EQ(signal_a.size(), signal_b.size());
+ float output = 0.0f;
+ const size_t length = signal_a.size();
+ for (size_t i = 0; i < length; ++i) {
+ float current = 0.0f;
+ for (size_t j = 0; j < length - i - 1; ++j) {
+ current += signal_a[j + i] * signal_b[j];
+ }
+ output = std::max(output, current);
+ }
+ return output;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/test_util.h b/src/3rdparty/resonance-audio/resonance_audio/utils/test_util.h
new file mode 100644
index 000000000..3ce9db5d2
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/test_util.h
@@ -0,0 +1,91 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_TEST_UTIL_H_
+#define RESONANCE_AUDIO_UTILS_TEST_UTIL_H_
+
+#include <cstddef>
+#include <vector>
+
+#include "base/integral_types.h"
+#include "base/audio_buffer.h"
+
+namespace vraudio {
+
+// Silences an audio channel.
+void GenerateSilence(AudioBuffer::Channel* output);
+
+// Generates a sine wave at the specified frequency in hertz at the given
+// sampling rate in hertz.
+void GenerateSineWave(float frequency_hz, int sample_rate,
+ AudioBuffer::Channel* output);
+
+// Generates a saw tooth signal between -1 and 1 for the given wave form length.
+void GenerateSawToothSignal(size_t tooth_length_samples,
+ AudioBuffer::Channel* output);
+
+// Generates a Dirac impulse filter kernel, which delays filtered signals by
+// the given delay.
+void GenerateDiracImpulseFilter(size_t delay_samples,
+ AudioBuffer::Channel* output);
+
+// Generates a linear ramp signal between -1 and 1.
+void GenerateIncreasingSignal(AudioBuffer::Channel* output);
+
+// Returns the index of the first non-zero element.
+size_t ZeroCompare(const AudioBuffer::Channel& signal, float epsilon);
+
+// Compares the content of two audio channels. Returns true if the absolute
+// difference between all samples is below epsion.
+bool CompareAudioBuffers(const AudioBuffer::Channel& buffer_a,
+ const AudioBuffer::Channel& buffer_b, float epsilon);
+
+// Returns delayed_signal.size() in output if delayed_signal is approximately
+// equal to original_signal delayed by the given amount; otherwise, returns the
+// index of the first unequal element in delayed_signal.
+size_t DelayCompare(const AudioBuffer::Channel& original_signal,
+ const AudioBuffer::Channel& delayed_signal, size_t delay,
+ float epsilon);
+
+// Test if two signals are shifted by a fixed number of samples with zero
+// padding.
+bool TestZeroPaddedDelay(const AudioBuffer::Channel& original_signal,
+ const AudioBuffer::Channel& delayed_signal,
+ size_t delay_samples, float epsilon);
+
+// Returns absolute peak amplitude of a signal.
+double CalculateSignalPeak(const AudioBuffer::Channel& channel);
+
+// Returns energy of a signal.
+double CalculateSignalEnergy(const AudioBuffer::Channel& channel);
+
+// Returns Root Mean Square (RMS) of a signal.
+double CalculateSignalRms(const AudioBuffer::Channel& channel);
+
+// Expresses a magnitude measurement in dB.
+double DbFromMagnitude(double magnitude);
+
+// Expresses a power measurement in dB.
+double DbFromPower(double power);
+
+// Returns the maximum cross correlation value between two signals.
+// To be used only with signals of the same length.
+float MaxCrossCorrelation(const AudioBuffer::Channel& signal_a,
+ const AudioBuffer::Channel& signal_b);
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_TEST_UTIL_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/test_util_test.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/test_util_test.cc
new file mode 100644
index 000000000..e92f55632
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/test_util_test.cc
@@ -0,0 +1,249 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/test_util.h"
+
+#include <cmath>
+#include <string>
+
+#include "third_party/googletest/googlemock/include/gmock/gmock.h"
+#include "third_party/googletest/googletest/include/gtest/gtest.h"
+#include "base/audio_buffer.h"
+#include "base/constants_and_types.h"
+#include "base/misc_math.h"
+
+namespace vraudio {
+
+namespace {
+
+TEST(TestUtilTest, GenerateSineWave_SuccessfulGeneration) {
+ const size_t kLengthStart = 1;
+ const size_t kLengthStop = 200;
+ const size_t kLengthStep = 10;
+ for (size_t length = kLengthStart; length <= kLengthStop;
+ length += kLengthStep) {
+ const float kFrequencyStart = 0.0f;
+ const float kFrequencyStop = 2000.0f;
+ const float kFrequencyStep = 100.0f;
+ for (float frequency = kFrequencyStart; frequency <= kFrequencyStop;
+ frequency += kFrequencyStep) {
+ const int kSampleRate = 2000;
+ AudioBuffer expected_signal(1, length);
+ AudioBuffer::Channel& expected_signal_view = expected_signal[0];
+ for (size_t i = 0; i < length; i++) {
+ const float phase = static_cast<float>(i) * 2.0f *
+ static_cast<float>(M_PI) / kSampleRate * frequency;
+ const float expected_value_float = std::sin(phase);
+ expected_signal_view[i] = expected_value_float;
+ }
+
+ AudioBuffer sine_wave(1U, length);
+ GenerateSineWave(frequency, kSampleRate, &sine_wave[0]);
+ EXPECT_TRUE(CompareAudioBuffers(sine_wave[0], expected_signal_view,
+ kEpsilonFloat));
+ }
+ }
+}
+
+TEST(TestUtilTest, GenerateSawToothSignal_SuccessfulGeneration) {
+ const size_t kLengthStart = 1;
+ const size_t kLengthStop = 200;
+ const size_t kLengthStep = 10;
+ for (size_t length = kLengthStart; length <= kLengthStop;
+ length += kLengthStep) {
+ const size_t kToothLengthStart = 1;
+ const size_t kToothLengthStop = 20;
+ const size_t kToothLengthStep = 3;
+ for (size_t tooth_length = kToothLengthStart;
+ tooth_length <= kToothLengthStop; tooth_length += kToothLengthStep) {
+ AudioBuffer expected_signal(1, length);
+ AudioBuffer::Channel& expected_signal_view = expected_signal[0];
+ for (size_t i = 0; i < length; i++) {
+ const float expected_value = static_cast<float>(i % tooth_length) /
+ static_cast<float>(tooth_length) *
+ 2.0f -
+ 1.0f;
+ expected_signal_view[i] = expected_value;
+ }
+
+ AudioBuffer signal(1, length);
+ GenerateSawToothSignal(tooth_length, &signal[0]);
+ EXPECT_TRUE(
+ CompareAudioBuffers(signal[0], expected_signal_view, kEpsilonFloat));
+ }
+ }
+}
+
+TEST(TestUtilTest, GenerateDiracImpulseFilterFloat_SuccessfulGeneration) {
+ const size_t kLengthStart = 1;
+ const size_t kLengthStop = 100;
+ const size_t kLengthStep = 10;
+ for (size_t length = kLengthStart; length <= kLengthStop;
+ length += kLengthStep) {
+ const size_t kDelayStart = 1;
+ const size_t kDelayStop = length - 1;
+ const size_t kDelayStep = 3;
+ for (size_t delay = kDelayStart; delay <= kDelayStop; delay += kDelayStep) {
+ AudioBuffer expected_signal(1, length);
+ GenerateSilence(&expected_signal[0]);
+ expected_signal[0][delay] = 1.0f;
+
+ AudioBuffer dirac_buffer(1, length);
+ GenerateDiracImpulseFilter(delay, &dirac_buffer[0]);
+ EXPECT_TRUE(CompareAudioBuffers(dirac_buffer[0], expected_signal[0],
+ kEpsilonFloat));
+ }
+ }
+}
+
+TEST(TestUtilTest, GenerateIncreasingSignal_SuccessfulGeneration) {
+ const size_t kLengthStart = 1;
+ const size_t kLengthStop = 200;
+ const size_t kLengthStep = 10;
+ for (size_t length = kLengthStart; length <= kLengthStop;
+ length += kLengthStep) {
+ AudioBuffer expected_signal(1, length);
+ AudioBuffer::Channel& expected_signal_view = expected_signal[0];
+ for (size_t i = 0; i < length; i++) {
+ const float expected_value =
+ static_cast<float>(i) / static_cast<float>(length) * 2.0f - 1.0f;
+ expected_signal_view[i] = expected_value;
+ }
+
+ AudioBuffer signal(1, length);
+ GenerateIncreasingSignal(&signal[0]);
+ EXPECT_TRUE(
+ CompareAudioBuffers(signal[0], expected_signal[0], kEpsilonFloat));
+ }
+}
+
+TEST(TestUtilTest, ZeroCompare_SuccessfulZeroSignal) {
+ const size_t kLengthStart = 1;
+ const size_t kLengthStop = 100;
+ const size_t kLengthStep = 10;
+ for (size_t length = kLengthStart; length <= kLengthStop;
+ length += kLengthStep) {
+ const size_t kZeroLengthStart = 0;
+ const size_t kZeroLengthStop = length - 1;
+ const size_t kZeroLengthStep = 3;
+ for (size_t zero_length = kZeroLengthStart; zero_length <= kZeroLengthStop;
+ zero_length += kZeroLengthStep) {
+ AudioBuffer signal(1, length);
+ AudioBuffer::Channel& signal_view = signal[0];
+ GenerateSilence(&signal_view);
+ for (size_t i = zero_length; i < length; i++) {
+ signal_view[i] = 123.0f * static_cast<float>(i) + 1.0f;
+ }
+ const size_t result = ZeroCompare(signal_view, kEpsilonFloat);
+ EXPECT_EQ(zero_length, result);
+ }
+ }
+}
+
+TEST(TestUtilTest, ZeroCompare_SuccessfulNonzeroSignal) {
+ const size_t kLengthStart = 1;
+ const size_t kLengthStop = 100;
+ const size_t kLengthStep = 10;
+ for (size_t length = kLengthStart; length <= kLengthStop;
+ length += kLengthStep) {
+ const size_t kZeroLengthStart = 1;
+ const size_t kZeroLengthStop = length - 1;
+ const size_t kZeroLengthStep = 3;
+ for (size_t zero_length = kZeroLengthStart; zero_length <= kZeroLengthStop;
+ zero_length += kZeroLengthStep) {
+ AudioBuffer signal(1, length);
+ AudioBuffer::Channel& signal_view = signal[0];
+ std::fill(signal_view.begin(), signal_view.end(), 100.0f);
+ for (size_t i = 0; i < zero_length - 1; i++) {
+ signal_view[i] = 0.0f;
+ const size_t result = ZeroCompare(signal_view, kEpsilonFloat);
+ EXPECT_NE(zero_length, result);
+ }
+ }
+ }
+}
+
+TEST(TestUtilTest, DelayCompare_SuccessfulEqualDelay) {
+ const size_t kLengthStart = 1;
+ const size_t kLengthStop = 100;
+ const size_t kLengthStep = 10;
+ for (size_t length = kLengthStart; length <= kLengthStop;
+ length += kLengthStep) {
+ AudioBuffer original_signal(1, length);
+ AudioBuffer::Channel& original_signal_view = original_signal[0];
+
+ GenerateIncreasingSignal(&original_signal_view);
+
+ const size_t kDelayStart = 0;
+ const size_t kDelayStop = length - 1;
+ const size_t kDelayStep = 3;
+ for (size_t delay = kDelayStart; delay <= kDelayStop; delay += kDelayStep) {
+ AudioBuffer delayed_signal(1, length + delay);
+ AudioBuffer::Channel& delayed_signal_view = delayed_signal[0];
+ GenerateSilence(&delayed_signal_view);
+ std::copy(original_signal_view.begin(), original_signal_view.end(),
+ delayed_signal_view.begin() + delay);
+ const size_t result = DelayCompare(
+ original_signal_view, delayed_signal_view, delay, kEpsilonFloat);
+ EXPECT_EQ(delayed_signal_view.size(), result);
+ }
+ }
+}
+
+TEST(TestUtilTest, DelayCompare_SuccessfulNotEqualDelay) {
+ const size_t kLengthStart = 1;
+ const size_t kLengthStop = 20;
+ const size_t kLengthStep = 10;
+ for (size_t length = kLengthStart; length <= kLengthStop;
+ length += kLengthStep) {
+ AudioBuffer original_signal(1, length);
+ AudioBuffer::Channel& original_signal_view = original_signal[0];
+
+ GenerateIncreasingSignal(&original_signal_view);
+
+ const size_t kDelayStart = 1;
+ const size_t kDelayStop = length - 1;
+ const size_t kDelayStep = 3;
+ for (size_t delay = kDelayStart; delay <= kDelayStop; delay += kDelayStep) {
+ // Test altering first delayed element.
+ {
+ AudioBuffer delayed_signal(1, length + delay);
+ AudioBuffer::Channel& delayed_signal_view = delayed_signal[0];
+ std::copy(original_signal_view.begin(), original_signal_view.end(),
+ delayed_signal_view.begin() + delay);
+ delayed_signal_view[delay] = -100.0f;
+ const size_t result = DelayCompare(
+ original_signal_view, delayed_signal_view, delay, kEpsilonFloat);
+ EXPECT_NE(delayed_signal_view.size(), result);
+ }
+ // Test altering last delayed element.
+ {
+ AudioBuffer delayed_signal(1, length + delay);
+ AudioBuffer::Channel& delayed_signal_view = delayed_signal[0];
+ std::copy(original_signal_view.begin(), original_signal_view.end(),
+ delayed_signal_view.begin() + delay);
+ delayed_signal_view[delayed_signal_view.size() - 1] = -100.0f;
+ const size_t result = DelayCompare(
+ original_signal_view, delayed_signal_view, delay, kEpsilonFloat);
+ EXPECT_NE(delayed_signal_view.size(), result);
+ }
+ }
+ }
+}
+
+} // namespace
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/threadsafe_fifo.h b/src/3rdparty/resonance-audio/resonance_audio/utils/threadsafe_fifo.h
new file mode 100644
index 000000000..a8c863714
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/threadsafe_fifo.h
@@ -0,0 +1,258 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_THREADSAFE_FIFO_H_
+#define RESONANCE_AUDIO_UTILS_THREADSAFE_FIFO_H_
+
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "base/logging.h"
+
+namespace vraudio {
+
+// Container to share preallocated data between threads. It is thread-safe for
+// single producer - single consumer FIFO usage.
+//
+// @tparam T Object type that the FIFO handles.
+template <typename T>
+class ThreadsafeFifo {
+ public:
+ // Constructor preallocates the maximum number of objects in the FIFO queue
+ // and defines the maximum waiting period before triggering a buffer underflow
+ // or overflow event. Sleeping is enabled by default and can be disabled via
+ // |EnableBlockingSleepUntilMethods|.
+ //
+ // @param max_objects Maximum number of objects in FIFO queue.
+ explicit ThreadsafeFifo(size_t max_objects);
+
+ // Constructor preallocates the maximum number of objects in the FIFO queue.
+ // Sleeping is enabled by default and can be disabled via
+ // |EnableBlockingSleepUntilMethods|.
+ //
+ // @param max_objects Maximum number of objects in FIFO queue.
+ // @param init Initializer to be assigned to allocated objects.
+ ThreadsafeFifo(size_t max_objects, const T& init);
+
+ // Returns a pointer to an available input object T. If the queue is full, a
+ // nullptr is returned.
+ //
+ // @return Pointer to an available input object. Nullptr if no input object is
+ // available.
+ T* AcquireInputObject();
+
+ // Releases a previously acquired input object to be pushed onto the FIFO
+ // front.
+ void ReleaseInputObject(const T* object);
+
+ // Returns a pointer to an output object T. If the queue is empty, a nullptr
+ // is returned.
+ //
+ // @return Pointer to the output object. Nullptr on empty queue.
+ T* AcquireOutputObject();
+
+ // Releases a previously acquired output object back to the FIFO.
+ void ReleaseOutputObject(const T* object);
+
+ // Blocks until the FIFO queue has an input object available or
+ // |EnableBlockingSleepUntilMethods(false)| is called.
+ //
+ // Returns true if free slot is available.
+ bool SleepUntilInputObjectIsAvailable() const;
+
+ // Blocks until the FIFO queue has an output object available or
+ // |EnableBlockingSleepUntilMethods(false)| is called.
+ //
+ // Returns true if an object is available.
+ bool SleepUntilOutputObjectIsAvailable() const;
+
+ // Allows for unblocking |SleepUntil[Input|Output]ObjectIsAvailable|
+ // method.
+ void EnableBlockingSleepUntilMethods(bool enable);
+
+ // Returns the number of objects in the FIFO queue.
+ size_t Size() const;
+
+ // Returns true if FIFO queue is empty, false otherwise.
+ bool Empty() const;
+
+ // Returns true if FIFO queue is full, false otherwise.
+ bool Full() const;
+
+ // Clears the FIFO queue. This call is only thread-safe if called by the
+ // consumer.
+ void Clear();
+
+ private:
+ // Conditional to signal empty/full queue events.
+ mutable std::mutex fifo_empty_mutex_;
+ mutable std::condition_variable fifo_empty_conditional_;
+
+ mutable std::mutex fifo_full_mutex_;
+ mutable std::condition_variable fifo_full_conditional_;
+
+ // Vector that stores all objects.
+ std::vector<T> fifo_;
+ size_t read_pos_;
+ size_t write_pos_;
+
+ // Atomic counter that reflects the size of |fifo_|.
+ std::atomic<size_t> fifo_size_;
+
+ std::atomic<bool> enable_sleeping_;
+};
+
+template <typename T>
+ThreadsafeFifo<T>::ThreadsafeFifo(size_t max_objects)
+ : fifo_(max_objects),
+ read_pos_(0),
+ write_pos_(0),
+ fifo_size_(0),
+ enable_sleeping_(true) {
+ CHECK_GT(max_objects, 0) << "FIFO size must be greater than zero";
+}
+
+template <typename T>
+ThreadsafeFifo<T>::ThreadsafeFifo(size_t max_objects, const T& init)
+ : ThreadsafeFifo(max_objects) {
+ for (auto& object : fifo_) {
+ object = init;
+ }
+}
+
+template <typename T>
+T* ThreadsafeFifo<T>::AcquireInputObject() {
+ if (Full()) {
+ return nullptr;
+ }
+ CHECK_LT(fifo_size_, fifo_.size());
+
+ // Add object to FIFO queue.
+ return &fifo_[write_pos_];
+}
+
+template <typename T>
+void ThreadsafeFifo<T>::ReleaseInputObject(const T* object) {
+ DCHECK_EQ(object, &fifo_[write_pos_]);
+
+ ++write_pos_;
+ write_pos_ = write_pos_ % fifo_.size();
+ if (fifo_size_.fetch_add(1) == 0) {
+ {
+ // Taking the lock and dropping it immediately assure that the notify
+ // cannot happen between the check of the predicate and wait of the
+ // |fifo_empty_conditional_|.
+ std::lock_guard<std::mutex> lock(fifo_empty_mutex_);
+ }
+ // In case of an empty queue, notify reader.
+ fifo_empty_conditional_.notify_one();
+ }
+}
+
+template <typename T>
+T* ThreadsafeFifo<T>::AcquireOutputObject() {
+ if (Empty()) {
+ return nullptr;
+ }
+ CHECK_GT(fifo_size_, 0);
+ return &fifo_[read_pos_];
+}
+
+template <typename T>
+void ThreadsafeFifo<T>::ReleaseOutputObject(const T* object) {
+ DCHECK_EQ(object, &fifo_[read_pos_]);
+
+ ++read_pos_;
+ read_pos_ = read_pos_ % fifo_.size();
+
+ if (fifo_size_.fetch_sub(1) == fifo_.size()) {
+ {
+ // Taking the lock and dropping it immediately assure that the notify
+ // cannot happen between the check of the predicate and wait of the
+ // |fifo_full_conditional_|.
+ std::lock_guard<std::mutex> lock(fifo_full_mutex_);
+ }
+ // In case of a previously full queue, notify writer.
+ fifo_full_conditional_.notify_one();
+ }
+}
+
+template <typename T>
+bool ThreadsafeFifo<T>::SleepUntilInputObjectIsAvailable() const {
+ // In case of a full queue, wait to allow objects to be popped from the
+ // FIFO queue.
+ std::unique_lock<std::mutex> lock(fifo_full_mutex_);
+ fifo_full_conditional_.wait(lock, [this]() {
+ return fifo_size_.load() < fifo_.size() || !enable_sleeping_.load();
+ });
+ return fifo_size_.load() < fifo_.size();
+}
+
+template <typename T>
+bool ThreadsafeFifo<T>::SleepUntilOutputObjectIsAvailable() const {
+ // In case of an empty queue, wait for new objects to be added.
+ std::unique_lock<std::mutex> lock(fifo_empty_mutex_);
+ fifo_empty_conditional_.wait(lock, [this]() {
+ return fifo_size_.load() > 0 || !enable_sleeping_.load();
+ });
+ return fifo_size_.load() > 0;
+}
+
+template <typename T>
+void ThreadsafeFifo<T>::EnableBlockingSleepUntilMethods(bool enable) {
+ enable_sleeping_ = enable;
+ // Taking the lock and dropping it immediately assure that the notify
+ // cannot happen between the check of the predicate and wait of the
+ // |fifo_empty_conditional_| and |fifo_full_conditional_|.
+ { std::lock_guard<std::mutex> lock(fifo_empty_mutex_); }
+ { std::lock_guard<std::mutex> lock(fifo_full_mutex_); }
+ fifo_empty_conditional_.notify_one();
+ fifo_full_conditional_.notify_one();
+}
+
+template <typename T>
+size_t ThreadsafeFifo<T>::Size() const {
+ return fifo_size_.load();
+}
+
+template <typename T>
+bool ThreadsafeFifo<T>::Empty() const {
+ return fifo_size_.load() == 0;
+}
+
+template <typename T>
+bool ThreadsafeFifo<T>::Full() const {
+ return fifo_size_.load() == fifo_.size();
+}
+
+template <typename T>
+void ThreadsafeFifo<T>::Clear() {
+ while (!Empty()) {
+ T* output = AcquireOutputObject();
+ if (output != nullptr) {
+ ReleaseOutputObject(output);
+ }
+ }
+}
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_THREADSAFE_FIFO_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/vorbis_stream_encoder.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/vorbis_stream_encoder.cc
new file mode 100644
index 000000000..f8b2f2287
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/vorbis_stream_encoder.cc
@@ -0,0 +1,174 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Prevent Visual Studio from complaining about std::copy_n.
+#if defined(_WIN32)
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include "utils/vorbis_stream_encoder.h"
+
+#include "vorbis/vorbisenc.h"
+#include "base/audio_buffer.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+VorbisStreamEncoder::VorbisStreamEncoder() : init_(false) {}
+
+bool VorbisStreamEncoder::InitializeForFile(const std::string& filename,
+ size_t num_channels,
+ int sample_rate,
+ EncodingMode encoding_mode,
+ int bitrate, float quality) {
+ output_file_stream_.open(filename,
+ std::ios::out | std::ios::trunc | std::ios::binary);
+ if (!output_file_stream_.good()) {
+ LOG(ERROR) << "Could not open output file: " << filename;
+ return false;
+ }
+
+ int return_value = 1;
+ vorbis_info_init(&vorbis_info_);
+
+ switch (encoding_mode) {
+ case EncodingMode::kVariableBitRate:
+ return_value = vorbis_encode_init_vbr(
+ &vorbis_info_, static_cast<long>(num_channels),
+ sample_rate, quality);
+ break;
+ case EncodingMode::kAverageBitRate:
+ return_value = vorbis_encode_init(
+ &vorbis_info_, static_cast<long>(num_channels),
+ sample_rate, -1 /* max_bitrate */, bitrate, -1 /* quality */);
+ break;
+ case EncodingMode::kUndefined:
+ default:
+ break;
+ }
+
+ if (return_value != 0) {
+ return false;
+ }
+
+ vorbis_comment_init(&vorbis_comment_);
+ vorbis_comment_add_tag(&vorbis_comment_, "ENCODER", "VrAudio");
+ vorbis_analysis_init(&vorbis_state_, &vorbis_info_);
+ vorbis_block_init(&vorbis_state_, &vorbis_block_);
+ ogg_stream_init(&stream_state_, 1 /* serial_number */);
+
+ // Generate Ogg header
+ ogg_packet header;
+ ogg_packet header_comments;
+ ogg_packet header_code;
+
+ vorbis_analysis_headerout(&vorbis_state_, &vorbis_comment_, &header,
+ &header_comments, &header_code);
+ ogg_stream_packetin(&stream_state_, &header);
+ ogg_stream_packetin(&stream_state_, &header_comments);
+ ogg_stream_packetin(&stream_state_, &header_code);
+
+ while (true) {
+ return_value = ogg_stream_flush(&stream_state_, &ogg_page_);
+ if (return_value == 0) {
+ break;
+ }
+ if (!WriteOggPage()) {
+ return false;
+ }
+ }
+ init_ = true;
+ return true;
+}
+
+bool VorbisStreamEncoder::AddPlanarBuffer(const float* const* input_ptrs,
+ size_t num_channels,
+ size_t num_frames) {
+ CHECK(init_);
+ PrepareVorbisBuffer(input_ptrs, num_channels, num_frames);
+ return PerformEncoding();
+}
+
+bool VorbisStreamEncoder::FlushAndClose() {
+ // Signal end of stream.
+ vorbis_analysis_wrote(&vorbis_state_, 0);
+ if (!PerformEncoding()) {
+ return false;
+ }
+
+ output_file_stream_.close();
+
+ vorbis_comment_clear(&vorbis_comment_);
+ vorbis_dsp_clear(&vorbis_state_);
+ vorbis_block_clear(&vorbis_block_);
+ ogg_stream_clear(&stream_state_);
+ vorbis_info_clear(&vorbis_info_);
+
+ init_ = false;
+ return true;
+}
+
+void VorbisStreamEncoder::PrepareVorbisBuffer(const float* const* input_ptrs,
+ size_t num_channels,
+ size_t num_frames) {
+ float** buffer = vorbis_analysis_buffer(
+ &vorbis_state_, static_cast<int>(num_channels * num_frames));
+ for (size_t channel = 0; channel < num_channels; ++channel) {
+ std::copy_n(input_ptrs[channel], num_frames, buffer[channel]);
+ }
+
+ vorbis_analysis_wrote(&vorbis_state_, static_cast<int>(num_frames));
+}
+
+bool VorbisStreamEncoder::PerformEncoding() {
+ CHECK(init_);
+ while (vorbis_analysis_blockout(&vorbis_state_, &vorbis_block_) == 1) {
+ vorbis_analysis(&vorbis_block_, nullptr);
+ vorbis_bitrate_addblock(&vorbis_block_);
+
+ while (vorbis_bitrate_flushpacket(&vorbis_state_, &ogg_packet_)) {
+ ogg_stream_packetin(&stream_state_, &ogg_packet_);
+
+ bool end_of_stream = false;
+ while (!end_of_stream) {
+ int result = ogg_stream_pageout(&stream_state_, &ogg_page_);
+ if (result == 0) {
+ break;
+ }
+ if (!WriteOggPage()) {
+ return false;
+ }
+ if (ogg_page_eos(&ogg_page_)) {
+ end_of_stream = true;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool VorbisStreamEncoder::WriteOggPage() {
+ output_file_stream_.write(reinterpret_cast<char*>(ogg_page_.header),
+ ogg_page_.header_len);
+ output_file_stream_.write(reinterpret_cast<char*>(ogg_page_.body),
+ ogg_page_.body_len);
+ if (!output_file_stream_.good()) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/vorbis_stream_encoder.h b/src/3rdparty/resonance-audio/resonance_audio/utils/vorbis_stream_encoder.h
new file mode 100644
index 000000000..cf66d2ad2
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/vorbis_stream_encoder.h
@@ -0,0 +1,114 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_VORBIS_STREAM_ENCODER_H_
+#define RESONANCE_AUDIO_UTILS_VORBIS_STREAM_ENCODER_H_
+
+#include <fstream>
+#include <iostream>
+
+#include "base/integral_types.h"
+
+#include "ogg/ogg.h"
+#include "vorbis/codec.h"
+
+namespace vraudio {
+
+class VorbisStreamEncoder {
+ public:
+ // Supported encoding modes.
+ enum class EncodingMode {
+ kUndefined,
+ // Variable bit rate mode (VBR).
+ kVariableBitRate,
+ // Average bit rate mode (ABR).
+ kAverageBitRate,
+ };
+
+ VorbisStreamEncoder();
+
+ // Initializes Vorbis encoding.
+ //
+ // @param filename Ogg vorbis output file. If it doesn't exist, it will be
+ // created. Existing files will be overwritten.
+ // @param num_channels Number of input channels.
+ // @param sample_rate Sample rate of input audio buffers.
+ // @param encoding_mode Selects variable (VBR) or average encoding (ABR).
+ // @param bitrate Target bitrate (only used when selecting ABR encoding).
+ // @param quality Target quality (only used when selecting VBR encoding). The
+ // usable range is -.1 (lowest quality, smallest file) to 1. (highest
+ // quality, largest file).
+ // @return False in case of file I/O errors or libvorbis initialization
+ // failures like non-supported channel/sample rate configuration.
+ bool InitializeForFile(const std::string& filename, size_t num_channels,
+ int sample_rate, EncodingMode encoding_mode,
+ int bitrate, float quality);
+
+ // Feeds input audio data into libvorbis encoder and triggers encoding.
+ //
+ // @param Array of pointers to planar channel data.
+ // @param num_channels Number of input channels.
+ // @param num_frames Number of input frames.
+ // @return False in case of file I/O errors or missing encoder initialization.
+ bool AddPlanarBuffer(const float* const* input_ptrs, size_t num_channels,
+ size_t num_frames);
+
+ // Flushes the remaining audio buffers and closes the output file.
+ //
+ // @return False in case of file I/O errors or missing encoder initialization.
+ bool FlushAndClose();
+
+ private:
+ // Copies input audio data into libvorbis encoder buffer.
+ //
+ // @param Array of pointers to planar channel data.
+ // @param num_channels Number of input channels.
+ // @param num_frames Number of input frames.
+ void PrepareVorbisBuffer(const float* const* input_ptrs, size_t num_channels,
+ size_t num_frames);
+
+ // Performs encoding of audio data prepared via |PrepareVorbisBuffer| or when
+ // the end of stream has been signaled.
+ //
+ // @return False in case of file I/O errors or missing encoder initialization.
+ bool PerformEncoding();
+
+ // Dumps data from |ogg_page_| struct to |output_file_stream_|.
+ //
+ // @return False in case of file I/O errors or missing encoder initialization.
+ bool WriteOggPage();
+
+ // Flag indicating if encoder has been successfully initialized.
+ bool init_;
+
+ // Output file stream.
+ std::ofstream output_file_stream_;
+
+ // libogg structs.
+ ogg_stream_state stream_state_;
+ ogg_page ogg_page_;
+ ogg_packet ogg_packet_;
+
+ // libvorbis structs.
+ vorbis_info vorbis_info_;
+ vorbis_comment vorbis_comment_;
+ vorbis_dsp_state vorbis_state_;
+ vorbis_block vorbis_block_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_VORBIS_STREAM_ENCODER_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/wav.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/wav.cc
new file mode 100644
index 000000000..5edbbc7bd
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/wav.cc
@@ -0,0 +1,53 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/wav.h"
+
+#include <cerrno>
+#include <fstream>
+#include <string>
+
+#include "base/integral_types.h"
+#include "base/logging.h"
+#include "utils/wav_reader.h"
+
+namespace vraudio {
+
+Wav::Wav(size_t num_channels, int sample_rate,
+ std::vector<int16_t>&& interleaved_samples)
+ : num_channels_(num_channels),
+ sample_rate_(sample_rate),
+ interleaved_samples_(interleaved_samples) {}
+
+Wav::~Wav() {}
+
+std::unique_ptr<const Wav> Wav::CreateOrNull(std::istream* binary_stream) {
+ WavReader wav_reader(binary_stream);
+ const size_t num_total_samples = wav_reader.GetNumTotalSamples();
+ if (!wav_reader.IsHeaderValid() || num_total_samples == 0) {
+ return nullptr;
+ }
+ std::vector<int16> interleaved_samples(num_total_samples);
+ if (wav_reader.ReadSamples(num_total_samples, &interleaved_samples[0]) !=
+ num_total_samples) {
+ return nullptr;
+ }
+ return std::unique_ptr<Wav>(new Wav(wav_reader.GetNumChannels(),
+ wav_reader.GetSampleRateHz(),
+ std::move(interleaved_samples)));
+}
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/wav.h b/src/3rdparty/resonance-audio/resonance_audio/utils/wav.h
new file mode 100644
index 000000000..c76bd8f3a
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/wav.h
@@ -0,0 +1,66 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_WAV_H_
+#define RESONANCE_AUDIO_UTILS_WAV_H_
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+namespace vraudio {
+
+// Wraps WavReader class to decode a wav file into memory.
+class Wav {
+ public:
+ ~Wav();
+
+ // Reads a RIFF WAVE from an opened binary stream.
+ static std::unique_ptr<const Wav> CreateOrNull(std::istream* binary_stream);
+
+ // Returns reference to interleaved samples.
+ const std::vector<int16_t>& interleaved_samples() const {
+ return interleaved_samples_;
+ }
+
+ // Returns number of channels.
+ size_t GetNumChannels() const { return num_channels_; }
+
+ // Returns sample rate.
+ int GetSampleRateHz() const { return sample_rate_; }
+
+ private:
+ // Private constructor used by static factory methods.
+ //
+ // @param num_channels Number of audio channels.
+ // @param sample_rate Sample rate.
+ // @param interleaved_samples Decoded interleaved samples.
+ Wav(size_t num_channels, int sample_rate,
+ std::vector<int16_t>&& interleaved_samples);
+
+ // Number of channels.
+ size_t num_channels_;
+
+ // Sample rate.
+ int sample_rate_;
+
+ // Interleaved samples.
+ std::vector<int16_t> interleaved_samples_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_WAV_H_
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/wav_reader.cc b/src/3rdparty/resonance-audio/resonance_audio/utils/wav_reader.cc
new file mode 100644
index 000000000..ca1078467
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/wav_reader.cc
@@ -0,0 +1,214 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "utils/wav_reader.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/integral_types.h"
+#include "base/logging.h"
+
+namespace vraudio {
+
+namespace {
+
+struct ChunkHeader {
+ char id[4];
+ uint32 size;
+};
+
+struct WavFormat {
+ ChunkHeader header;
+ uint16 format_tag; // Integer identifier of the format
+ uint16 num_channels; // Number of audio channels
+ uint32 samples_rate; // Audio sample rate
+ uint32 average_bytes_per_second; // Bytes per second (possibly approximate)
+ uint16 block_align; // Size in bytes of a sample block (all channels)
+ uint16 bits_per_sample; // Size in bits of a single per-channel sample
+};
+static_assert(sizeof(WavFormat) == 24, "Padding in WavFormat struct detected");
+
+struct WavHeader {
+ struct {
+ ChunkHeader header;
+ char format[4];
+ } riff;
+ WavFormat format;
+ struct {
+ ChunkHeader header;
+ } data;
+};
+
+// Size of WAV header.
+const size_t kWavHeaderSize = 44;
+
+static_assert(sizeof(WavHeader) == kWavHeaderSize,
+ "Padding in WavHeader struct detected");
+
+// Supported WAV encoding formats.
+static const uint16 kExtensibleWavFormat = 0xfffe;
+static const uint16 kPcmFormat = 0x1;
+} // namespace
+
+WavReader::WavReader(std::istream* binary_stream)
+ : binary_stream_(CHECK_NOTNULL(binary_stream)),
+ num_channels_(0),
+ sample_rate_hz_(-1),
+ num_total_samples_(0),
+ num_remaining_samples_(0),
+ pcm_offset_bytes_(0) {
+ init_ = ParseHeader();
+}
+
+size_t WavReader::ReadBinaryDataFromStream(void* target_ptr, size_t size) {
+ if (!binary_stream_->good()) {
+ return 0;
+ }
+ binary_stream_->read(static_cast<char*>(target_ptr), size);
+ return static_cast<size_t>(binary_stream_->gcount());
+}
+
+bool WavReader::ParseHeader() {
+ WavHeader header;
+ // Exclude data field to be able to optionally parse the two-byte extension
+ // field.
+ if (ReadBinaryDataFromStream(&header, kWavHeaderSize - sizeof(header.data)) !=
+ kWavHeaderSize - sizeof(header.data))
+ return false;
+ const uint32 format_size = header.format.header.size;
+ // Size of |WavFormat| without |ChunkHeader|.
+ static const uint32 kFormatSubChunkHeader =
+ sizeof(WavFormat) - sizeof(ChunkHeader);
+ if (format_size < kFormatSubChunkHeader) {
+ return false;
+ }
+ if (format_size != kFormatSubChunkHeader) {
+ // Parse optional extension fields.
+ int16 extension_size;
+ if (ReadBinaryDataFromStream(&extension_size, sizeof(extension_size)) !=
+ sizeof(extension_size))
+ return false;
+ int8 extension_data;
+ for (size_t i = 0; i < static_cast<size_t>(extension_size); ++i) {
+ if (ReadBinaryDataFromStream(&extension_data, sizeof(extension_data)) !=
+ sizeof(extension_data))
+ return false;
+ }
+ }
+ if (header.format.format_tag == kExtensibleWavFormat) {
+ // Skip extensible wav format "fact" header.
+ ChunkHeader fact_header;
+ if (ReadBinaryDataFromStream(&fact_header, sizeof(fact_header)) !=
+ sizeof(fact_header)) {
+ return false;
+ }
+ if (std::string(fact_header.id, 4) != "fact") {
+ return false;
+ }
+ int8 fact_data;
+ for (size_t i = 0; i < static_cast<size_t>(fact_header.size); ++i) {
+ if (ReadBinaryDataFromStream(&fact_data, sizeof(fact_data)) !=
+ sizeof(fact_data))
+ return false;
+ }
+ }
+
+ // Read the "data" header.
+ if (ReadBinaryDataFromStream(&header.data, sizeof(header.data)) !=
+ sizeof(header.data)) {
+ return false;
+ }
+
+ num_channels_ = header.format.num_channels;
+ sample_rate_hz_ = header.format.samples_rate;
+
+ bytes_per_sample_ = header.format.bits_per_sample / 8;
+ if (bytes_per_sample_ == 0 || bytes_per_sample_ != sizeof(int16)) {
+ return false;
+ }
+ const size_t bytes_in_payload = header.data.header.size;
+ num_total_samples_ = bytes_in_payload / bytes_per_sample_;
+ num_remaining_samples_ = num_total_samples_;
+
+ if (header.format.num_channels == 0 || num_total_samples_ == 0 ||
+ bytes_in_payload % bytes_per_sample_ != 0 ||
+ (header.format.format_tag != kPcmFormat &&
+ header.format.format_tag != kExtensibleWavFormat) ||
+ (std::string(header.riff.header.id, 4) != "RIFF") ||
+ (std::string(header.riff.format, 4) != "WAVE") ||
+ (std::string(header.format.header.id, 4) != "fmt ") ||
+ (std::string(header.data.header.id, 4) != "data")) {
+ return false;
+ }
+
+ int64 current_position = binary_stream_->tellg();
+ if (current_position < 0) {
+ return false;
+ }
+ pcm_offset_bytes_ = static_cast<uint64>(current_position);
+ return true;
+}
+
+size_t WavReader::ReadSamples(size_t num_samples, int16* target_buffer) {
+ const size_t num_samples_to_read =
+ std::min(num_remaining_samples_, num_samples);
+ if (num_samples_to_read == 0) {
+ return 0;
+ }
+ const size_t num_bytes_read =
+ ReadBinaryDataFromStream(target_buffer, num_samples * sizeof(int16));
+ const size_t num_samples_read = num_bytes_read / bytes_per_sample_;
+
+ num_remaining_samples_ -= num_samples_read;
+ return num_samples_read;
+}
+
+int64 WavReader::SeekToFrame(const uint64 frame_position) {
+ DCHECK_GT(num_channels_, 0U);
+ if (frame_position <= (num_total_samples_ / num_channels_)) {
+ const uint64 seek_position_byte =
+ pcm_offset_bytes_ + frame_position * num_channels_ * bytes_per_sample_;
+ binary_stream_->seekg(seek_position_byte, binary_stream_->beg);
+ }
+
+ int64 binary_stream_position_byte =
+ static_cast<int64>(binary_stream_->tellg());
+ if (binary_stream_position_byte < 0) {
+ // Return an error status if the actual bitstream position could not be
+ // queried.
+ return binary_stream_position_byte;
+ }
+
+ if (static_cast<uint64>(binary_stream_position_byte) > pcm_offset_bytes_) {
+ DCHECK_GT(bytes_per_sample_, 0U);
+ return (static_cast<uint64>(binary_stream_position_byte) -
+ pcm_offset_bytes_) /
+ (bytes_per_sample_ * num_channels_);
+ }
+
+ return 0;
+}
+
+size_t WavReader::GetNumTotalSamples() const { return num_total_samples_; }
+
+size_t WavReader::GetNumChannels() const { return num_channels_; }
+
+int WavReader::GetSampleRateHz() const { return sample_rate_hz_; }
+
+bool WavReader::IsHeaderValid() const { return init_; }
+
+} // namespace vraudio
diff --git a/src/3rdparty/resonance-audio/resonance_audio/utils/wav_reader.h b/src/3rdparty/resonance-audio/resonance_audio/utils/wav_reader.h
new file mode 100644
index 000000000..513853c30
--- /dev/null
+++ b/src/3rdparty/resonance-audio/resonance_audio/utils/wav_reader.h
@@ -0,0 +1,103 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef RESONANCE_AUDIO_UTILS_WAV_READER_H_
+#define RESONANCE_AUDIO_UTILS_WAV_READER_H_
+
+#include <cstdint>
+#include <istream>
+
+#include "base/integral_types.h"
+
+namespace vraudio {
+
+// Basic RIFF WAVE decoder that supports multichannel 16-bit PCM.
+class WavReader {
+ public:
+ // Constructor decodes WAV header.
+ //
+ // @param binary_stream Binary input stream to read from.
+ explicit WavReader(std::istream* binary_stream);
+
+ // True if WAV header was successfully parsed.
+ bool IsHeaderValid() const;
+
+ // Returns the total number of samples defined in the WAV header. Note that
+ // the actual number of samples in the file can differ.
+ size_t GetNumTotalSamples() const;
+
+ // Returns number of channels.
+ size_t GetNumChannels() const;
+
+ // Returns sample rate in Hertz.
+ int GetSampleRateHz() const;
+
+ // Seek to a specific frame position within the wave file. If frame_position
+ // is not a valid address, then the internal read position remains unchanged.
+ //
+ // @param frame_position Destination frame position for play cursor.
+ // @return Actual frame position of cursor after this seek operation. A
+ // negative return value indicates a stream failure.
+ int64 SeekToFrame(const uint64 frame_position);
+
+ // Reads samples from WAV file into target buffer.
+ //
+ // @param num_samples Number of samples to read.
+ // @param target_buffer Target buffer to write to.
+ // @return Number of decoded samples.
+ size_t ReadSamples(size_t num_samples, int16_t* target_buffer);
+
+ private:
+ // Parses WAV header.
+ //
+ // @return True on success.
+ bool ParseHeader();
+
+ // Helper method to read binary data from input stream.
+ //
+ // @param size Number of bytes to read.
+ // @param target_ptr Target buffer to write to.
+ // @return Number of bytes read.
+ size_t ReadBinaryDataFromStream(void* target_ptr, size_t size);
+
+ // Binary input stream.
+ std::istream* binary_stream_;
+
+ // Flag indicating if the WAV header was parsed successfully.
+ bool init_;
+
+ // Number of audio channels.
+ size_t num_channels_;
+
+ // Sample rate in Hertz.
+ int sample_rate_hz_;
+
+ // Total number of samples.
+ size_t num_total_samples_;
+
+ // Number of remaining samples in WAV file.
+ size_t num_remaining_samples_;
+
+ // Bytes per sample as defined in the WAV header.
+ size_t bytes_per_sample_;
+
+ // Offset into data stream where PCM data begins.
+ uint64 pcm_offset_bytes_;
+};
+
+} // namespace vraudio
+
+#endif // RESONANCE_AUDIO_UTILS_WAV_READER_H_
diff --git a/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/LICENSE b/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/LICENSE
new file mode 100644
index 000000000..a59c8797f
--- /dev/null
+++ b/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/LICENSE
@@ -0,0 +1,157 @@
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this
+License, each Contributor hereby grants to You a perpetual, worldwide,
+non-exclusive, no-charge, royalty-free, irrevocable copyright license to
+reproduce, prepare Derivative Works of, publicly display, publicly perform,
+sublicense, and distribute the Work and such Derivative Works in Source or
+Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License,
+each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section) patent
+license to make, have made, use, offer to sell, sell, import, and otherwise
+transfer the Work, where such license applies only to those patent claims
+licensable by such Contributor that are necessarily infringed by their
+Contribution(s) alone or by combination of their Contribution(s) with the Work
+to which such Contribution(s) was submitted. If You institute patent litigation
+against any entity (including a cross-claim or counterclaim in a lawsuit)
+alleging that the Work or a Contribution incorporated within the Work
+constitutes direct or contributory patent infringement, then any patent licenses
+granted to You under this License for that Work shall terminate as of the date
+such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or
+Derivative Works thereof in any medium, with or without modifications, and in
+Source or Object form, provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+5. Submission of Contributions. Unless You explicitly state otherwise, any
+Contribution intentionally submitted for inclusion in the Work by You to the
+Licensor shall be under the terms and conditions of this License, without any
+additional terms or conditions. Notwithstanding the above, nothing herein shall
+supersede or modify the terms of any separate license agreement you may have
+executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names,
+trademarks, service marks, or product names of the Licensor, except as required
+for reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
+writing, Licensor provides the Work (and each Contributor provides its
+Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied, including, without limitation, any warranties
+or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+PARTICULAR PURPOSE. You are solely responsible for determining the
+appropriateness of using or redistributing the Work and assume any risks
+associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in
+tort (including negligence), contract, or otherwise, unless required by
+applicable law (such as deliberate and grossly negligent acts) or agreed to in
+writing, shall any Contributor be liable to You for damages, including any
+direct, indirect, special, incidental, or consequential damages of any character
+arising as a result of this License or out of the use or inability to use the
+Work (including but not limited to damages for loss of goodwill, work stoppage,
+computer failure or malfunction, or any and all other commercial damages or
+losses), even if such Contributor has been advised of the possibility of such
+damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or
+Derivative Works thereof, You may choose to offer, and charge a fee for,
+acceptance of support, warranty, indemnity, or other liability obligations
+and/or rights consistent with this License. However, in accepting such
+obligations, You may act only on Your own behalf and on Your sole
+responsibility, not on behalf of any other Contributor, and only if You agree to
+indemnify, defend, and hold each Contributor harmless for any liability incurred
+by, or claims asserted against, such Contributor by reason of your accepting any
+such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
diff --git a/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generate_hrtf_assets.py b/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generate_hrtf_assets.py
new file mode 100755
index 000000000..5272d3855
--- /dev/null
+++ b/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generate_hrtf_assets.py
@@ -0,0 +1,287 @@
+"""
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+#!/usr/bin/python
+"""Generates a C++ class that statically stores raw asset data.
+
+Usage: generate_hrtf_assets.py <asset file> <output_path>
+
+Parses an asset file and generates a compilable C++ class that statically
+defines the content of all files declared in the asset file.
+
+An asset file is an XML file that lists group of files. See below for an
+example:
+
+<?xml version="1.0"?>
+<IAD name="Assets">
+ <assets>
+ <file>file1.txt</file>
+ <file>file2.txt</file>
+ <file name="dir/file1.txt">file1.txt</file>
+ <file name="dir/file2.txt">file2.txt</file>
+ </assets>
+ <assets prefix="path">
+ <file name="file1.txt">file1.txt</file>
+ <file name="file2.txt">file2.txt</file>
+ </assets>
+</IAD>
+
+Below that are any number of <assets> blocks which define groups of files with
+similar paths. The filename to be added is the text of each <file> block, and
+path to the file relative to the definition file. If an <asset> has a "prefix"
+tag then that prefix is prepended to the filename.
+"""
+
+import array
+import os
+import re
+import sys
+import textwrap
+from xml.etree import ElementTree
+
+
+class Error(Exception):
+ """Base class for errors."""
+
+
+class InvalidAssetSyntaxError(Error):
+ """Error representing an asset file with invalid syntax."""
+
+
+#------------------------------------------------------------------------------
+def BuildManifest(asset_file):
+ """Builds a list of (filename, asset) pairs from an asset file.
+
+ Args:
+ asset_file: string - a file containing asset definitions.
+
+ Returns:
+ A tuple containing:
+ - an asset class name
+ - an list of (filename, asset) pairs, where filename is the absolute path
+ to the file on local disk, and asset is the name the stored asset should
+ be given.
+
+ Raises:
+ InvalidAssetSyntaxError: if asset_file has syntax errors.
+ IOError: if asset_file or any files it references are not found.
+ """
+ if not os.path.exists(asset_file):
+ raise IOError('Could not find asset file "%s"' % asset_file)
+
+ # Load the xml.
+ root = ElementTree.parse(asset_file).getroot()
+ if root.tag != 'IAD':
+ raise InvalidAssetSyntaxError('Root tag should be "IAD" not "%s"' %
+ root.tag)
+ if 'name' not in root.attrib:
+ raise InvalidAssetSyntaxError('Root tag requires a "name" attribute')
+
+ path_to_asset_file = os.path.abspath(os.path.dirname(asset_file))
+
+ manifest = []
+ for group in root.findall('assets'):
+ # Get the path prefix if there is one.
+ prefix = group.attrib.get('prefix', '')
+ for asset in group.findall('file'):
+ # See if the file exists.
+ asset_filename = os.path.join(path_to_asset_file,
+ os.path.normcase(asset.text))
+ if not os.path.exists(asset_filename):
+ raise IOError('Asset "%s" does not exist, searched in %s' %
+ (asset.text, path_to_asset_file))
+
+ # The filename is either the asset text appended to the prefix, or a
+ # requested name appended to the prefix.
+ filename = os.path.join(prefix, asset.attrib.get('name', asset.text))
+ # Add the full path to the file to the manifest.
+ manifest += [(os.path.abspath(asset_filename), filename)]
+
+ return root.attrib['name'], manifest
+
+
+#------------------------------------------------------------------------------
+def CamelCaseToUnderscore(name):
+ """Converts a camel-case formatted string to an underscore format.
+
+ Example: TestAssetClass -> test_asset_class
+ HTTPServer -> http_server
+
+ Args:
+ name: string - the camel-case formatted string.
+
+ Returns:
+ The underscore formatted input string.
+ """
+ s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
+ return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
+
+#------------------------------------------------------------------------------
+
+
+def GenerateAssetHeaderString(asset_class_name):
+ """Generates C++ header of asset class.
+
+ Args:
+ asset_class_name: string - the asset class name.
+
+ Returns:
+ String of generated asset class header.
+ """
+ header_guard = asset_class_name.upper().replace(
+ '/', '_').replace('.', '_') + '_'
+ output_string = []
+ output_string.append('#ifndef %s\n' % header_guard)
+ output_string.append('#define %s\n\n' % header_guard)
+ output_string.append('#include <memory>\n')
+ output_string.append('#include <string>\n')
+ output_string.append('#include <unordered_map>\n')
+ output_string.append('#include <vector>\n\n')
+ output_string.append('namespace sadie {\n\n')
+ output_string.append(
+ '// Note this class is automatically generated. Do not modify.\n')
+ output_string.append('class %s {\n' % asset_class_name)
+ output_string.append(' public:\n')
+ output_string.append(' // Lookups and retrieves a file from an asset class.\n')
+ output_string.append(' //\n')
+ output_string.append(' // @param filename: Filename to be retrieved.\n')
+ output_string.append(' // @return std::string with raw file data. In case of an error, a nullptr is\n')
+ output_string.append(' // returned. Caller must take over the ownership of the returned data.\n')
+ output_string.append(
+ ' std::unique_ptr<std::string> GetFile(const std::string& filename) '
+ 'const;\n\n')
+ output_string.append(' private:\n')
+ output_string.append(
+ ' typedef std::unordered_map<std::string, std::vector<unsigned char>>\n')
+ output_string.append(' AssetDataMap;\n')
+ output_string.append(' static const AssetDataMap kAssetMap;\n')
+ output_string.append('};\n\n')
+ output_string.append('} // namespace sadie\n\n')
+ output_string.append('#endif // %s\n' % header_guard)
+ return ''.join(output_string)
+
+
+#------------------------------------------------------------------------------
+def GenerateMapEntryDataString(manifest_entry):
+ """Generates formatted string of hexadecimal integers from a manifest entry.
+
+ Generates a formatted string of comma-separated hexadecimal integers that
+ define a C++ std::pair<std::string, std::vector<char>> map entry.
+
+ Args:
+ manifest_entry: a (filename, asset_name) pair, where filename is
+ the absolute path to the file on local disk, and asset_name is the name
+ the stored asset should be given.
+
+ Returns:
+ Formatted string containing asset name and binary file data:
+ {"asset_name", {0x.., 0x..}}
+ """
+ with open(manifest_entry[0], 'rb') as f:
+ file_data = f.read()
+ ints = array.array('B', file_data)
+ data_string = '{' + ', '.join('0x%x' % value for value in ints) + '}}'
+
+ wrapper = textwrap.TextWrapper(initial_indent=' ' * 5,
+ width=80,
+ subsequent_indent=' ' * 6)
+ wrapped_data_string = wrapper.fill(data_string)
+ return ' {\"%s\",\n%s' % (manifest_entry[1], wrapped_data_string)
+
+
+#------------------------------------------------------------------------------
+def GenerateAssetImplementationString(manifest, asset_class_name,
+ header_filename):
+ """Generates C++ implementation of asset class.
+
+ Args:
+ manifest: list of lists of (filename, asset_name) pairs, where filename is
+ the absolute path to the file on local disk, and asset_name is the name
+ the stored asset should be given.
+ asset_class_name: string - the asset class name.
+ header_filename: string - header file name.
+
+ Returns:
+ String of generated asset class implementation.
+ """
+ output_string = []
+ output_string.append('#include \"%s\"\n\n' %
+ header_filename.lower())
+ output_string.append('namespace sadie {\n\n')
+ output_string.append(
+ 'std::unique_ptr<std::string> %s::GetFile(\n' % asset_class_name)
+ output_string.append(' const std::string& filename) const {\n')
+ output_string.append(' AssetDataMap::const_iterator map_entry_itr = '
+ 'kAssetMap.find(filename);\n')
+ output_string.append(' if (map_entry_itr == kAssetMap.end()) {\n')
+ output_string.append(' return nullptr;\n')
+ output_string.append(' }\n')
+ output_string.append(' const char* data =\n')
+ output_string.append(' reinterpret_cast<const char*>('
+ 'map_entry_itr->second.data());\n')
+ output_string.append(
+ ' const size_t data_size = map_entry_itr->second.size();\n')
+ output_string.append(' return std::unique_ptr<std::string>('
+ 'new std::string(data, data_size));\n')
+ output_string.append('}\n\n')
+ # Generate map definition.
+ output_string.append('const %s::AssetDataMap %s::kAssetMap = {\n' %
+ (asset_class_name, asset_class_name))
+ output_string.append(
+ ',\n'.join(GenerateMapEntryDataString(f) for f in manifest))
+ output_string.append('};\n\n')
+ output_string.append('} // namespace sadie\n')
+ return ''.join(output_string)
+
+
+#------------------------------------------------------------------------------
+def main():
+ if len(sys.argv) != 3:
+ print 'Usage: %s <asset definition file> <output_path>' % sys.argv[0]
+ return
+
+ cur_dir = os.getcwd()
+ asset_file_path = os.path.normcase(os.path.join(cur_dir, sys.argv[1]))
+ output_dir_path = os.path.normcase(os.path.join(cur_dir, sys.argv[2]))
+
+ # Parse manifest.
+ classname, manifest = BuildManifest(asset_file_path)
+ if not manifest:
+ raise InvalidAssetSyntaxError(
+ '"%s" does not contain any asset definitions' % asset_file_path)
+
+ # Define class name, file names, output paths, etc.
+ classname_underscore = CamelCaseToUnderscore(classname)
+ header_filename = '%s.h' % classname_underscore
+ implementation_filename = '%s.cc' % classname_underscore
+ header_output_file_path = os.path.join(output_dir_path, header_filename)
+ implementation_output_file_path = os.path.join(output_dir_path,
+ implementation_filename)
+
+ # Render asset class and write output to .cc/.h files.
+ header_content = GenerateAssetHeaderString(
+ classname)
+ cc_content = GenerateAssetImplementationString(
+ manifest, classname, header_filename)
+
+ with open(header_output_file_path, 'w') as text_file:
+ text_file.write(header_content)
+ with open(implementation_output_file_path, 'w') as text_file:
+ text_file.write(cc_content)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generated/README b/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generated/README
new file mode 100644
index 000000000..41101e044
--- /dev/null
+++ b/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generated/README
@@ -0,0 +1,5 @@
+These files have been automatically generated using:
+
+$./generate_hrtf_assets.py hrtf_assets.iad generated
+
+Do not modify manually these automatically generated files. \ No newline at end of file
diff --git a/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generated/hrtf_assets.cc b/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generated/hrtf_assets.cc
new file mode 100644
index 000000000..50c0a9921
--- /dev/null
+++ b/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generated/hrtf_assets.cc
@@ -0,0 +1,1200 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "hrtf_assets.h"
+
+namespace sadie {
+
+std::unique_ptr<std::string> HrtfAssets::GetFile(
+ const std::string& filename) const {
+ AssetDataMap::const_iterator map_entry_itr = kAssetMap.find(filename);
+ if (map_entry_itr == kAssetMap.end()) {
+ return nullptr;
+ }
+ const char* data =
+ reinterpret_cast<const char*>(map_entry_itr->second.data());
+ const size_t data_size = map_entry_itr->second.size();
+ return std::unique_ptr<std::string>(new std::string(data, data_size));
+}
+
+const HrtfAssets::AssetDataMap HrtfAssets::kAssetMap = {
+ {"WAV/Subject_002/SH/sh_hrir_order_1.wav",
+ {0x52, 0x49, 0x46, 0x46, 0x24, 0x8, 0x0, 0x0, 0x57, 0x41, 0x56, 0x45, 0x66,
+ 0x6d, 0x74, 0x20, 0x10, 0x0, 0x0, 0x0, 0x1, 0x0, 0x4, 0x0, 0x80, 0xbb,
+ 0x0, 0x0, 0x0, 0xdc, 0x5, 0x0, 0x8, 0x0, 0x10, 0x0, 0x64, 0x61, 0x74,
+ 0x61, 0x0, 0x8, 0x0, 0x0, 0xfe, 0xff, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf6,
+ 0xff, 0x10, 0x0, 0xfd, 0xff, 0x2, 0x0, 0xfb, 0xff, 0xb, 0x0, 0xff, 0xff,
+ 0x1, 0x0, 0xfe, 0xff, 0x7, 0x0, 0xff, 0xff, 0x1, 0x0, 0xfe, 0xff, 0xc,
+ 0x0, 0xff, 0xff, 0x1, 0x0, 0xff, 0xff, 0x1, 0x0, 0xfd, 0xff, 0x0, 0x0,
+ 0xfb, 0xff, 0x1, 0x0, 0x2, 0x0, 0xfe, 0xff, 0x8, 0x0, 0x4, 0x0, 0xf9,
+ 0xff, 0x0, 0x0, 0xf8, 0xff, 0xfa, 0xff, 0x2, 0x0, 0xfc, 0xff, 0x18, 0x0,
+ 0x10, 0x0, 0x5, 0x0, 0x4, 0x0, 0xee, 0xff, 0xe7, 0xff, 0xf3, 0xff, 0x0,
+ 0x0, 0xfb, 0xff, 0xf4, 0xff, 0x1a, 0x0, 0xf9, 0xff, 0xcc, 0xff, 0xc0,
+ 0xff, 0xd5, 0xff, 0x4, 0x0, 0xfd, 0xfd, 0xf8, 0xfd, 0x1d, 0x0, 0xce, 0xff,
+ 0x1d, 0xfc, 0x12, 0xfc, 0x1a, 0x0, 0x41, 0x0, 0xf1, 0x1, 0xf1, 0x1, 0x4e,
+ 0xfe, 0x87, 0x0, 0xb4, 0xd, 0xa5, 0xd, 0xd0, 0xfc, 0x75, 0xfe, 0x37, 0x1,
+ 0x25, 0x1, 0xe7, 0x7, 0x5b, 0xfe, 0x49, 0xf5, 0x3e, 0xf5, 0x14, 0xa, 0x6f,
+ 0x7, 0x14, 0xfd, 0x3, 0xfd, 0x2c, 0xee, 0xa4, 0x2, 0xc0, 0xf5, 0xb5, 0xf5,
+ 0x3b, 0xfc, 0xfd, 0xf0, 0x68, 0xfd, 0x6b, 0xfd, 0x91, 0xd, 0xd0, 0x0,
+ 0xb8, 0xf4, 0xce, 0xf4, 0x61, 0xf3, 0xe2, 0x4, 0xf3, 0xe6, 0x3a, 0xe7,
+ 0x88, 0x5, 0x5e, 0xf7, 0xdc, 0xfd, 0x4f, 0xfe, 0x94, 0x8, 0xcd, 0x6, 0x6a,
+ 0x2, 0xf7, 0x2, 0x6, 0xf8, 0x7a, 0xfe, 0x2b, 0x1c, 0xfd, 0x1c, 0x7c, 0xfb,
+ 0xad, 0xfd, 0x6e, 0x18, 0xa1, 0x19, 0x9, 0xfd, 0x7, 0x10, 0x6, 0xfd, 0xe3,
+ 0xfe, 0x49, 0x15, 0x5d, 0x6, 0x26, 0xc, 0xcd, 0xd, 0xa, 0x1, 0x4a, 0x1,
+ 0xa0, 0x3, 0x3b, 0x4, 0x51, 0xeb, 0x6a, 0xfd, 0x41, 0x5, 0x0, 0x6, 0x34,
+ 0xff, 0x26, 0xf8, 0x24, 0x5, 0xac, 0x4, 0xba, 0x4, 0x2, 0xfa, 0xb3, 0x3,
+ 0x97, 0x1, 0xe2, 0x5, 0x50, 0x0, 0xd9, 0x9, 0xb2, 0xb, 0x88, 0x3, 0xe6,
+ 0x0, 0x2d, 0x0, 0xb6, 0x4, 0x11, 0x6, 0x31, 0xfd, 0x78, 0x1, 0xe6, 0x6,
+ 0xa5, 0xf9, 0x2c, 0x1, 0xb5, 0xf8, 0x0, 0x3, 0x8e, 0xf6, 0xba, 0xfc, 0x68,
+ 0xf9, 0x39, 0x1, 0x6e, 0x0, 0xff, 0xff, 0xcf, 0xfa, 0xc7, 0x0, 0xf, 0xfc,
+ 0x26, 0x3, 0xac, 0xfb, 0xc, 0x4, 0x32, 0x5, 0xe6, 0xfd, 0x98, 0x4, 0x82,
+ 0x2, 0x1d, 0x2, 0x28, 0x1, 0x4, 0x6, 0xdc, 0xfa, 0x3a, 0xfd, 0xf7, 0x1,
+ 0xf9, 0x5, 0x6a, 0xfe, 0x7, 0x0, 0x2d, 0x0, 0x98, 0xff, 0x68, 0xf8, 0xd0,
+ 0xfe, 0x91, 0x0, 0x3c, 0x4, 0xf3, 0xf7, 0x34, 0x3, 0x95, 0x3, 0x81, 0x1,
+ 0x22, 0xfa, 0x87, 0x1, 0x6c, 0xff, 0x31, 0x0, 0xa1, 0xfd, 0xc2, 0x1, 0xf1,
+ 0xfc, 0x1f, 0x3, 0x5b, 0xfd, 0x9d, 0x0, 0x43, 0xff, 0xfb, 0xfe, 0xd2,
+ 0xfb, 0x3, 0xff, 0x2f, 0xfd, 0xc2, 0x1, 0x8e, 0xfc, 0xee, 0xff, 0x98,
+ 0xff, 0x1d, 0xff, 0x10, 0xf8, 0x23, 0xff, 0x66, 0x1, 0x20, 0x0, 0xcb,
+ 0xfc, 0x23, 0x1, 0x37, 0x2, 0xf7, 0xfd, 0xce, 0xfb, 0xfd, 0x0, 0x62, 0xff,
+ 0x3e, 0xfe, 0x5d, 0xfe, 0xae, 0x0, 0xe0, 0xfc, 0xc2, 0x0, 0x7b, 0xff,
+ 0x96, 0x0, 0x20, 0xff, 0xe2, 0xfe, 0xed, 0xfc, 0xc, 0x0, 0x21, 0xff, 0xc5,
+ 0x0, 0x31, 0xfd, 0xcb, 0xfe, 0x7d, 0x0, 0x91, 0xfe, 0x80, 0xfa, 0x7b,
+ 0xff, 0xce, 0x0, 0x7d, 0xfe, 0xc, 0xfe, 0x93, 0x1, 0xdc, 0xff, 0xbe, 0xfd,
+ 0x33, 0xfd, 0xd7, 0x0, 0x5f, 0xff, 0xc4, 0xfe, 0xbc, 0xfe, 0x5f, 0x0,
+ 0x8c, 0xff, 0x99, 0xfe, 0xc3, 0xfd, 0x4f, 0x0, 0x7, 0xff, 0xba, 0xfe,
+ 0x2d, 0xfd, 0xd, 0x0, 0x47, 0xff, 0x62, 0x0, 0x34, 0xfe, 0xad, 0xfe, 0x23,
+ 0x1, 0xee, 0xfd, 0xd0, 0xfb, 0x3d, 0xff, 0x96, 0x0, 0x7d, 0xfe, 0x40,
+ 0xfe, 0x9, 0x0, 0xfe, 0x0, 0xee, 0xfd, 0x6c, 0xfd, 0x1e, 0x0, 0xe9, 0x0,
+ 0x66, 0xfe, 0xf1, 0xfe, 0xef, 0x0, 0x51, 0xff, 0x2, 0xff, 0x53, 0xfe,
+ 0x4b, 0xff, 0x4e, 0xff, 0xa5, 0xff, 0xf1, 0xfd, 0x41, 0xff, 0xa, 0x0,
+ 0xc4, 0xfe, 0xeb, 0xfd, 0xe0, 0xff, 0xc7, 0xff, 0xe9, 0xfd, 0x7c, 0xfd,
+ 0x83, 0xff, 0x7b, 0x0, 0x7d, 0xfe, 0x45, 0xff, 0x4d, 0x0, 0x83, 0x0, 0x47,
+ 0xfd, 0x1b, 0xfe, 0xca, 0x0, 0xd1, 0xfe, 0x2b, 0xff, 0xab, 0xff, 0xfd,
+ 0xff, 0xe2, 0xff, 0xe0, 0xfe, 0x4b, 0xfe, 0x11, 0x0, 0xed, 0xff, 0xc2,
+ 0xfe, 0x7d, 0xfe, 0xb1, 0x0, 0xaa, 0xff, 0xdc, 0xfe, 0x98, 0xfe, 0x92,
+ 0xff, 0xb2, 0x0, 0x3a, 0xfe, 0xd7, 0xfd, 0xba, 0xff, 0xe8, 0xff, 0x44,
+ 0xfe, 0xf4, 0xfe, 0x8, 0x0, 0x73, 0xff, 0x46, 0xfe, 0x8c, 0xfe, 0xd7,
+ 0xff, 0xf1, 0xff, 0xea, 0xfe, 0x80, 0xff, 0x4b, 0x0, 0xa6, 0xff, 0x60,
+ 0xfe, 0xf7, 0xfe, 0xed, 0xff, 0x61, 0xff, 0x8f, 0xff, 0xca, 0xff, 0xc1,
+ 0xff, 0x2a, 0x0, 0x6c, 0xfe, 0xe6, 0xfe, 0x0, 0x0, 0xc8, 0xff, 0x28, 0xfe,
+ 0xb, 0xff, 0xb5, 0xff, 0x3, 0x0, 0xde, 0xfe, 0xd8, 0xff, 0x60, 0xff, 0x91,
+ 0x0, 0x19, 0xfe, 0x1e, 0xff, 0x4e, 0x0, 0x97, 0xff, 0xdb, 0xfe, 0x17, 0x0,
+ 0x16, 0x0, 0xfc, 0xff, 0xcf, 0xfe, 0x5c, 0xff, 0xaa, 0xff, 0x4d, 0x0,
+ 0xb0, 0xfe, 0xc5, 0xff, 0x54, 0x0, 0x98, 0xff, 0x61, 0xfe, 0xae, 0xff,
+ 0xab, 0xff, 0x4, 0x0, 0xc6, 0xfe, 0xce, 0xff, 0xc3, 0xff, 0x3c, 0x0, 0x1d,
+ 0xfe, 0xf2, 0xff, 0x2d, 0x0, 0x8d, 0xff, 0x83, 0xfe, 0x34, 0x0, 0xcc,
+ 0xff, 0x29, 0x0, 0xfd, 0xfe, 0x4d, 0x0, 0xee, 0xff, 0x28, 0x0, 0x2b, 0xfe,
+ 0xb0, 0xff, 0x12, 0x0, 0x99, 0xff, 0x33, 0xff, 0x84, 0x0, 0xa7, 0xff,
+ 0x55, 0x0, 0xa1, 0xfe, 0xc5, 0xff, 0xc2, 0xff, 0xf4, 0xff, 0x81, 0xfe,
+ 0x38, 0x0, 0x29, 0x0, 0xac, 0xff, 0xe3, 0xfe, 0x76, 0x0, 0xb3, 0xff, 0x52,
+ 0x0, 0xa6, 0xfe, 0x25, 0x0, 0x1f, 0x0, 0xee, 0xff, 0x93, 0xfe, 0x6a, 0x0,
+ 0x36, 0x0, 0xbc, 0xff, 0xe0, 0xfe, 0x3f, 0x0, 0x9e, 0xff, 0x6b, 0x0, 0xbe,
+ 0xfe, 0x31, 0x0, 0x19, 0x0, 0xf3, 0xff, 0x58, 0xfe, 0x2, 0x0, 0xf8, 0xff,
+ 0xd5, 0xff, 0xa, 0xff, 0x6d, 0x0, 0xcc, 0xff, 0x6d, 0x0, 0x70, 0xfe, 0xfb,
+ 0xff, 0x41, 0x0, 0xbf, 0xff, 0xcd, 0xfe, 0x62, 0x0, 0xe, 0x0, 0xf4, 0xff,
+ 0x20, 0xff, 0x3a, 0x0, 0xe4, 0xff, 0x4c, 0x0, 0x92, 0xfe, 0xe0, 0xff,
+ 0x41, 0x0, 0xcc, 0xff, 0xa, 0xff, 0x68, 0x0, 0xb, 0x0, 0x31, 0x0, 0x6,
+ 0xff, 0xa, 0x0, 0xfc, 0xff, 0x49, 0x0, 0xca, 0xfe, 0x4c, 0x0, 0x6a, 0x0,
+ 0xb9, 0xff, 0xff, 0xfe, 0x6d, 0x0, 0x6, 0x0, 0xb, 0x0, 0x21, 0xff, 0x4e,
+ 0x0, 0xfc, 0xff, 0x10, 0x0, 0xa0, 0xfe, 0x1e, 0x0, 0x3b, 0x0, 0x8c, 0xff,
+ 0x1c, 0xff, 0x5a, 0x0, 0xdf, 0xff, 0x22, 0x0, 0x13, 0xff, 0x24, 0x0, 0x5,
+ 0x0, 0xff, 0xff, 0xcf, 0xfe, 0x11, 0x0, 0x24, 0x0, 0xb7, 0xff, 0x62, 0xff,
+ 0x60, 0x0, 0xcb, 0xff, 0x55, 0x0, 0x8, 0xff, 0xdd, 0xff, 0x14, 0x0, 0xfa,
+ 0xff, 0x26, 0xff, 0x2b, 0x0, 0x35, 0x0, 0xe8, 0xff, 0x62, 0xff, 0x20, 0x0,
+ 0xd0, 0xff, 0x5c, 0x0, 0x25, 0xff, 0xf3, 0xff, 0x1e, 0x0, 0xf9, 0xff,
+ 0x24, 0xff, 0x2b, 0x0, 0x16, 0x0, 0xea, 0xff, 0x5e, 0xff, 0x1b, 0x0, 0xbe,
+ 0xff, 0x37, 0x0, 0x11, 0xff, 0xfe, 0xff, 0x18, 0x0, 0xc3, 0xff, 0x23,
+ 0xff, 0x28, 0x0, 0x9, 0x0, 0xe8, 0xff, 0x6b, 0xff, 0x2a, 0x0, 0xe1, 0xff,
+ 0x27, 0x0, 0x0, 0xff, 0xf5, 0xff, 0x22, 0x0, 0xb1, 0xff, 0x60, 0xff, 0x56,
+ 0x0, 0xf2, 0xff, 0xe, 0x0, 0x59, 0xff, 0x9, 0x0, 0xde, 0xff, 0x20, 0x0,
+ 0x21, 0xff, 0x2, 0x0, 0x24, 0x0, 0xc4, 0xff, 0x76, 0xff, 0x3e, 0x0, 0xe7,
+ 0xff, 0x27, 0x0, 0x5c, 0xff, 0xf0, 0xff, 0xec, 0xff, 0x16, 0x0, 0x3d,
+ 0xff, 0x16, 0x0, 0x2d, 0x0, 0xc9, 0xff, 0x76, 0xff, 0x29, 0x0, 0xd4, 0xff,
+ 0x27, 0x0, 0x5f, 0xff, 0xfd, 0xff, 0xf6, 0xff, 0xfb, 0xff, 0x3f, 0xff,
+ 0x13, 0x0, 0x25, 0x0, 0xd0, 0xff, 0x95, 0xff, 0x35, 0x0, 0xcf, 0xff, 0x35,
+ 0x0, 0x4b, 0xff, 0xe7, 0xff, 0x4, 0x0, 0xdc, 0xff, 0x54, 0xff, 0x18, 0x0,
+ 0x15, 0x0, 0xe4, 0xff, 0x8a, 0xff, 0x1e, 0x0, 0xc9, 0xff, 0x37, 0x0, 0x4e,
+ 0xff, 0xf4, 0xff, 0xb, 0x0, 0xd2, 0xff, 0x83, 0xff, 0x41, 0x0, 0x15, 0x0,
+ 0xf4, 0xff, 0x78, 0xff, 0xfb, 0xff, 0xe6, 0xff, 0x30, 0x0, 0x58, 0xff,
+ 0xf6, 0xff, 0x9, 0x0, 0xd7, 0xff, 0x75, 0xff, 0x1c, 0x0, 0xed, 0xff, 0xf6,
+ 0xff, 0x7c, 0xff, 0xf3, 0xff, 0xf6, 0xff, 0x24, 0x0, 0x57, 0xff, 0xf7,
+ 0xff, 0xa, 0x0, 0xd9, 0xff, 0x67, 0xff, 0x1, 0x0, 0xe3, 0xff, 0x3, 0x0,
+ 0x61, 0xff, 0xdb, 0xff, 0xa, 0x0, 0x16, 0x0, 0x4d, 0xff, 0xf1, 0xff, 0xff,
+ 0xff, 0xdb, 0xff, 0x93, 0xff, 0x23, 0x0, 0xd8, 0xff, 0x11, 0x0, 0x88,
+ 0xff, 0x3, 0x0, 0xb, 0x0, 0x19, 0x0, 0x7f, 0xff, 0x21, 0x0, 0xe, 0x0,
+ 0xfa, 0xff, 0xa3, 0xff, 0x24, 0x0, 0xee, 0xff, 0x1b, 0x0, 0x99, 0xff,
+ 0x15, 0x0, 0xf8, 0xff, 0xe, 0x0, 0x8a, 0xff, 0x27, 0x0, 0xf0, 0xff, 0xf0,
+ 0xff, 0xaf, 0xff, 0x1e, 0x0, 0xf8, 0xff, 0x12, 0x0, 0xab, 0xff, 0x23, 0x0,
+ 0x4, 0x0, 0x5, 0x0, 0x9b, 0xff, 0x2e, 0x0, 0xf5, 0xff, 0xf5, 0xff, 0xcc,
+ 0xff, 0x38, 0x0, 0x4, 0x0, 0x1a, 0x0, 0xa2, 0xff, 0x24, 0x0, 0xf8, 0xff,
+ 0x0, 0x0, 0xa9, 0xff, 0x3b, 0x0, 0xe7, 0xff, 0xff, 0xff, 0xc4, 0xff, 0x33,
+ 0x0, 0x0, 0x0, 0x19, 0x0, 0x98, 0xff, 0x25, 0x0, 0xf5, 0xff, 0xff, 0xff,
+ 0xb4, 0xff, 0x45, 0x0, 0xed, 0xff, 0x5, 0x0, 0xc1, 0xff, 0x2a, 0x0, 0x5,
+ 0x0, 0x15, 0x0, 0xae, 0xff, 0x27, 0x0, 0xef, 0xff, 0x0, 0x0, 0xc8, 0xff,
+ 0x39, 0x0, 0xea, 0xff, 0xb, 0x0, 0xcb, 0xff, 0x23, 0x0, 0x7, 0x0, 0x15,
+ 0x0, 0xb3, 0xff, 0x1a, 0x0, 0xec, 0xff, 0x1, 0x0, 0xd5, 0xff, 0x2a, 0x0,
+ 0xf4, 0xff, 0x12, 0x0, 0xc9, 0xff, 0x16, 0x0, 0x6, 0x0, 0xb, 0x0, 0xbc,
+ 0xff, 0x14, 0x0, 0xeb, 0xff, 0xfe, 0xff, 0xe3, 0xff, 0x27, 0x0, 0x1, 0x0,
+ 0x12, 0x0, 0xc5, 0xff, 0xb, 0x0, 0x6, 0x0, 0x5, 0x0, 0xc9, 0xff, 0x14,
+ 0x0, 0xef, 0xff, 0x2, 0x0, 0xe1, 0xff, 0x19, 0x0, 0x3, 0x0, 0x16, 0x0,
+ 0xc1, 0xff, 0x6, 0x0, 0x2, 0x0, 0x2, 0x0, 0xce, 0xff, 0x14, 0x0, 0xef,
+ 0xff, 0x0, 0x0, 0xe1, 0xff, 0x16, 0x0, 0x6, 0x0, 0x13, 0x0, 0xc3, 0xff,
+ 0x7, 0x0, 0x1, 0x0, 0xfd, 0xff, 0xd4, 0xff, 0x14, 0x0, 0xee, 0xff, 0x4,
+ 0x0, 0xe0, 0xff, 0x10, 0x0, 0x6, 0x0, 0x10, 0x0, 0xc1, 0xff, 0x1, 0x0,
+ 0xfa, 0xff, 0xf8, 0xff, 0xdb, 0xff, 0x14, 0x0, 0xf3, 0xff, 0x5, 0x0, 0xdc,
+ 0xff, 0x9, 0x0, 0x9, 0x0, 0xb, 0x0, 0xc3, 0xff, 0x2, 0x0, 0xf8, 0xff,
+ 0xf7, 0xff, 0xe1, 0xff, 0x12, 0x0, 0xf8, 0xff, 0x8, 0x0, 0xda, 0xff, 0x4,
+ 0x0, 0x7, 0x0, 0xa, 0x0, 0xc8, 0xff, 0x2, 0x0, 0xf4, 0xff, 0xf9, 0xff,
+ 0xe4, 0xff, 0xd, 0x0, 0xf9, 0xff, 0xe, 0x0, 0xd5, 0xff, 0xfe, 0xff, 0x7,
+ 0x0, 0x8, 0x0, 0xcb, 0xff, 0x3, 0x0, 0xf4, 0xff, 0xfb, 0xff, 0xe6, 0xff,
+ 0xc, 0x0, 0xfd, 0xff, 0xe, 0x0, 0xd2, 0xff, 0xfe, 0xff, 0x3, 0x0, 0x3,
+ 0x0, 0xd1, 0xff, 0x6, 0x0, 0xf1, 0xff, 0xfd, 0xff, 0xe5, 0xff, 0x9, 0x0,
+ 0xff, 0xff, 0xf, 0x0, 0xce, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x1, 0x0, 0xd4,
+ 0xff, 0x7, 0x0, 0xf3, 0xff, 0xff, 0xff, 0xe4, 0xff, 0x7, 0x0, 0x1, 0x0,
+ 0xd, 0x0, 0xce, 0xff, 0xfc, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0xda, 0xff, 0x9,
+ 0x0, 0xf5, 0xff, 0x2, 0x0, 0xe2, 0xff, 0x4, 0x0, 0x2, 0x0, 0xd, 0x0, 0xcd,
+ 0xff, 0xfc, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0xdf, 0xff, 0x8, 0x0, 0xf6,
+ 0xff, 0x4, 0x0, 0xdf, 0xff, 0xff, 0xff, 0x3, 0x0, 0xa, 0x0, 0xd0, 0xff,
+ 0xfd, 0xff, 0xf9, 0xff, 0xfb, 0xff, 0xe2, 0xff, 0x7, 0x0, 0xf9, 0xff, 0x6,
+ 0x0, 0xdd, 0xff, 0xfe, 0xff, 0x3, 0x0, 0x7, 0x0, 0xd3, 0xff, 0xff, 0xff,
+ 0xf8, 0xff, 0xfb, 0xff, 0xe4, 0xff, 0x6, 0x0, 0xfb, 0xff, 0x7, 0x0, 0xdb,
+ 0xff, 0xfc, 0xff, 0x1, 0x0, 0x4, 0x0, 0xd5, 0xff, 0x0, 0x0, 0xf8, 0xff,
+ 0xfc, 0xff, 0xe7, 0xff, 0x5, 0x0, 0xfd, 0xff, 0x9, 0x0, 0xd9, 0xff, 0xfb,
+ 0xff, 0x1, 0x0, 0x2, 0x0, 0xda, 0xff, 0x2, 0x0, 0xf7, 0xff, 0xfd, 0xff,
+ 0xe6, 0xff, 0x3, 0x0, 0xff, 0xff, 0x9, 0x0, 0xd8, 0xff, 0xfb, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xde, 0xff, 0x4, 0x0, 0xf8, 0xff, 0xff, 0xff, 0xe6,
+ 0xff, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0xd8, 0xff, 0xfd, 0xff, 0xfd, 0xff,
+ 0xfe, 0xff, 0xe1, 0xff, 0x5, 0x0, 0xf9, 0xff, 0x0, 0x0, 0xe5, 0xff, 0x1,
+ 0x0, 0x0, 0x0, 0x7, 0x0, 0xd9, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xfd, 0xff,
+ 0xe4, 0xff, 0x6, 0x0, 0xfa, 0xff, 0x2, 0x0, 0xe3, 0xff, 0x0, 0x0, 0x0,
+ 0x0, 0x6, 0x0, 0xdb, 0xff, 0x0, 0x0, 0xfb, 0xff, 0xfc, 0xff, 0xe7, 0xff,
+ 0x6, 0x0, 0xfc, 0xff, 0x4, 0x0, 0xe1, 0xff, 0xff, 0xff, 0x0, 0x0, 0x4,
+ 0x0, 0xdd, 0xff, 0x2, 0x0, 0xfa, 0xff, 0xfd, 0xff, 0xe7, 0xff, 0x5, 0x0,
+ 0xfc, 0xff, 0x5, 0x0, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2, 0x0, 0xdf,
+ 0xff, 0x3, 0x0, 0xfa, 0xff, 0xfe, 0xff, 0xe8, 0xff, 0x5, 0x0, 0xfe, 0xff,
+ 0x5, 0x0, 0xe0, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x1, 0x0, 0xe1, 0xff, 0x4,
+ 0x0, 0xfb, 0xff, 0xff, 0xff, 0xe8, 0xff, 0x4, 0x0, 0xff, 0xff, 0x6, 0x0,
+ 0xde, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x0, 0x0, 0xe1, 0xff, 0x5, 0x0, 0xfb,
+ 0xff, 0x1, 0x0, 0xe2, 0xff, 0x3, 0x0, 0xff, 0xff, 0x4, 0x0, 0xdb, 0xff,
+ 0x1, 0x0, 0xfd, 0xff, 0x0, 0x0, 0xdd, 0xff, 0x4, 0x0, 0xfd, 0xff, 0x1,
+ 0x0, 0xdc, 0xff, 0x2, 0x0, 0xfe, 0xff, 0x3, 0x0, 0xd9, 0xff, 0x2, 0x0,
+ 0xfd, 0xff, 0x0, 0x0, 0xdb, 0xff, 0x3, 0x0, 0xfd, 0xff, 0x2, 0x0, 0xdb,
+ 0xff, 0x2, 0x0, 0xfe, 0xff, 0x2, 0x0, 0xdb, 0xff, 0x2, 0x0, 0xfd, 0xff,
+ 0x1, 0x0, 0xdd, 0xff, 0x2, 0x0, 0xfd, 0xff, 0x2, 0x0, 0xde, 0xff, 0x2,
+ 0x0, 0xfe, 0xff, 0x2, 0x0, 0xe1, 0xff, 0x1, 0x0, 0xfe, 0xff, 0x1, 0x0,
+ 0xe3, 0xff, 0x1, 0x0, 0xfe, 0xff, 0x1, 0x0}},
+ {"WAV/Subject_002/SH/sh_hrir_order_2.wav",
+ {0x52, 0x49, 0x46, 0x46, 0x24, 0x12, 0x0, 0x0, 0x57, 0x41, 0x56, 0x45,
+ 0x66, 0x6d, 0x74, 0x20, 0x10, 0x0, 0x0, 0x0, 0x1, 0x0, 0x9, 0x0, 0x80,
+ 0xbb, 0x0, 0x0, 0x0, 0x2f, 0xd, 0x0, 0x12, 0x0, 0x10, 0x0, 0x64, 0x61,
+ 0x74, 0x61, 0x0, 0x12, 0x0, 0x0, 0xfe, 0xff, 0x4, 0x0, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf3,
+ 0xff, 0x19, 0x0, 0xfd, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0x0, 0x0, 0xff, 0xff,
+ 0xfe, 0xff, 0xfa, 0xff, 0xfa, 0xff, 0xc, 0x0, 0xfe, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xfe, 0xff,
+ 0x3, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xff, 0xff, 0xfe, 0xff, 0xa, 0x0, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0x4, 0x0, 0xfd, 0xff, 0x1, 0x0,
+ 0xfd, 0xff, 0xfe, 0xff, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x3, 0x0,
+ 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0xfc, 0xff, 0xfd, 0xff,
+ 0xfe, 0xff, 0x4, 0x0, 0xec, 0xff, 0x1, 0x0, 0x5, 0x0, 0x7, 0x0, 0xff,
+ 0xff, 0x9, 0x0, 0x9, 0x0, 0x9, 0x0, 0xfc, 0xff, 0xf2, 0xff, 0x5, 0x0,
+ 0xf7, 0xff, 0xf5, 0xff, 0x5, 0x0, 0xfd, 0xff, 0xfa, 0xff, 0x4, 0x0, 0xf3,
+ 0xff, 0xc7, 0xff, 0xfa, 0xff, 0x1d, 0x0, 0x23, 0x0, 0xf2, 0xff, 0xf, 0x0,
+ 0x7, 0x0, 0x19, 0x0, 0x47, 0xff, 0x74, 0xfe, 0xd9, 0xff, 0xfd, 0xff, 0x9,
+ 0x0, 0xdb, 0xff, 0xa4, 0x0, 0x65, 0x0, 0xb6, 0x0, 0x86, 0xfe, 0xff, 0xfc,
+ 0x13, 0x0, 0xdc, 0xff, 0xd0, 0xff, 0x18, 0x0, 0x19, 0x1, 0x93, 0x0, 0x5b,
+ 0x1, 0xc0, 0x1, 0x6b, 0x3, 0xa3, 0x0, 0x4f, 0xff, 0x1b, 0xff, 0xbb, 0x0,
+ 0x8e, 0xfe, 0x2f, 0xff, 0x56, 0xfe, 0x3, 0x6, 0x37, 0xc, 0xa1, 0xff, 0x47,
+ 0x0, 0x63, 0x0, 0x96, 0xff, 0x95, 0xfa, 0x9a, 0xfc, 0x1f, 0xfa, 0xa9,
+ 0xfd, 0xff, 0xfb, 0xa3, 0xff, 0xfe, 0x0, 0x3d, 0x1, 0x70, 0xff, 0x74, 0x0,
+ 0x71, 0xff, 0x67, 0x1, 0x6e, 0xf7, 0x70, 0xef, 0xa3, 0xff, 0xbe, 0xff,
+ 0xd7, 0xff, 0xb4, 0xff, 0xf7, 0x5, 0xd9, 0x2, 0x68, 0x7, 0x93, 0x2, 0x3a,
+ 0x2, 0x67, 0xfb, 0xe2, 0xff, 0xd9, 0x0, 0xd6, 0xfb, 0xcf, 0x2, 0x5c, 0x4,
+ 0x94, 0x0, 0x7e, 0x2, 0xbb, 0x2, 0x6f, 0x3, 0xf7, 0xfc, 0x1, 0xfc, 0xc8,
+ 0x3, 0x23, 0x2, 0xd3, 0x3, 0xed, 0xff, 0x74, 0xfc, 0x11, 0xfb, 0xb0, 0xc,
+ 0x90, 0x9, 0x42, 0x8, 0x25, 0x9, 0xd, 0xff, 0xdf, 0xfd, 0x11, 0x1, 0x54,
+ 0xfb, 0xb1, 0xf7, 0x22, 0xfa, 0xfa, 0x2, 0xe5, 0x4, 0xfe, 0xf9, 0xc2, 0x2,
+ 0x3b, 0x1, 0xfb, 0x2, 0x65, 0xf4, 0x1a, 0xee, 0xc5, 0xf4, 0x5d, 0xe7,
+ 0x45, 0xe6, 0x84, 0xfc, 0xa1, 0x2, 0x61, 0xfc, 0x4a, 0x4, 0xcd, 0xfc,
+ 0x45, 0xfd, 0x38, 0x6, 0x17, 0x6, 0x21, 0x3, 0xbf, 0x5, 0x55, 0xfd, 0x37,
+ 0xfc, 0x9e, 0xfe, 0x9a, 0x7, 0xea, 0xa, 0xcd, 0xfd, 0xb7, 0xd, 0xb8, 0xc,
+ 0x47, 0xfa, 0xde, 0xf8, 0x67, 0xff, 0x8a, 0xfe, 0x49, 0x8, 0x89, 0x13,
+ 0x93, 0xfb, 0xc9, 0xf6, 0x9e, 0xfc, 0x89, 0xfb, 0x1a, 0xed, 0xf0, 0xec,
+ 0xb0, 0xf4, 0x20, 0x5, 0xde, 0xd, 0x7a, 0xb, 0x5f, 0xff, 0x9d, 0x6, 0x50,
+ 0x6, 0x31, 0xff, 0xc9, 0xfb, 0xc9, 0xf3, 0x41, 0x1, 0x8b, 0xfd, 0x9f, 0x0,
+ 0x43, 0x4, 0xf6, 0xf9, 0xae, 0xfe, 0x72, 0x1, 0xc0, 0x5, 0x91, 0x4, 0xcd,
+ 0x6, 0xff, 0x10, 0x81, 0xf2, 0x62, 0xa, 0x50, 0xa, 0xd9, 0xf7, 0x86, 0xf8,
+ 0xdd, 0x7, 0x5d, 0xf8, 0x71, 0x0, 0xa1, 0xb, 0x1b, 0xfd, 0x37, 0xf7, 0x65,
+ 0x7, 0xff, 0xff, 0x19, 0xe, 0x1e, 0x16, 0x62, 0xf1, 0x64, 0x4, 0xd4, 0x3,
+ 0xb1, 0xe, 0x35, 0x0, 0x71, 0xf8, 0xe8, 0x9, 0xba, 0x3, 0x6b, 0x3, 0x3d,
+ 0x0, 0xfb, 0x8, 0x1d, 0x14, 0xda, 0xa, 0x44, 0xf, 0xad, 0x4, 0xec, 0x9,
+ 0xb3, 0xfb, 0xe2, 0x1, 0x1, 0xf6, 0xb1, 0xf9, 0xf9, 0x2, 0xc0, 0xf8, 0x4e,
+ 0xf2, 0x27, 0xf5, 0x86, 0xff, 0xee, 0xff, 0xdc, 0xff, 0xf5, 0xf4, 0xc8,
+ 0x5, 0x26, 0x4, 0x7d, 0xf0, 0x1a, 0xf6, 0xce, 0xf6, 0xda, 0xf8, 0xce,
+ 0xfd, 0x2d, 0xfd, 0xc, 0x3, 0xd1, 0x9, 0x20, 0x4, 0x0, 0xf9, 0x11, 0x5,
+ 0x9, 0x0, 0x1c, 0xfc, 0xa7, 0x2, 0x89, 0x1, 0x9c, 0x6, 0x65, 0x1, 0xd1,
+ 0xf3, 0x1, 0xc, 0xcc, 0xc, 0xb0, 0x4, 0x61, 0x1, 0xd8, 0xfe, 0x2b, 0xf8,
+ 0x93, 0x10, 0xd8, 0x8, 0xcd, 0x0, 0xca, 0x8, 0xaa, 0x0, 0x17, 0x5, 0xd,
+ 0x0, 0xa9, 0x3, 0xa5, 0xff, 0xd0, 0xc, 0x27, 0x8, 0xb0, 0x5, 0x73, 0x0,
+ 0xa8, 0xfa, 0xbf, 0x0, 0x5c, 0x0, 0x2b, 0x2, 0x13, 0xfd, 0x99, 0x7, 0xb8,
+ 0x2, 0x51, 0x2, 0xa1, 0x0, 0x65, 0x1, 0x6, 0x3, 0x6d, 0x3, 0x3f, 0xfd,
+ 0x74, 0xfd, 0x8e, 0x7, 0xb2, 0xfb, 0x49, 0xfc, 0xdb, 0xf8, 0x4, 0xf9,
+ 0x16, 0xf9, 0x91, 0xfe, 0x49, 0xfe, 0xe, 0xff, 0xb9, 0x1, 0xff, 0xfb,
+ 0x2b, 0xfb, 0xae, 0xfc, 0x1f, 0xff, 0x4c, 0xfe, 0x17, 0xfd, 0xf8, 0xfd,
+ 0xc5, 0xfb, 0xd7, 0x3, 0x5f, 0xfb, 0xe9, 0xfc, 0xb8, 0x0, 0xf4, 0xfa,
+ 0x3a, 0x2, 0xf, 0xfe, 0xa9, 0x2, 0x7d, 0xfd, 0xba, 0x2, 0x68, 0xfe, 0x16,
+ 0xfa, 0xf, 0x2, 0x14, 0x0, 0x1b, 0x1, 0xe5, 0xff, 0xe2, 0x1, 0xce, 0xfa,
+ 0x1f, 0x5, 0xd, 0x5, 0x5c, 0x1, 0xd, 0x7, 0x37, 0x3, 0x91, 0x4, 0x2a, 0x2,
+ 0xbb, 0x2, 0x5c, 0x1, 0x71, 0x5, 0x25, 0x0, 0xc9, 0xfc, 0x21, 0x5, 0xf2,
+ 0xfc, 0x5d, 0xfe, 0xad, 0x0, 0x48, 0x3, 0x14, 0x2, 0x74, 0x3, 0x9d, 0x1,
+ 0xe7, 0xfe, 0x13, 0xff, 0x74, 0x3, 0x3f, 0xfc, 0xa6, 0xff, 0xf7, 0x2,
+ 0x17, 0x6, 0x6e, 0x1, 0xc4, 0xfe, 0x66, 0xfe, 0x99, 0xfb, 0x74, 0xff,
+ 0xd9, 0xfa, 0x11, 0xfc, 0x7, 0x3, 0xd8, 0x4, 0x3c, 0xfe, 0xcd, 0xfb, 0xd5,
+ 0xf6, 0x5c, 0x0, 0xb2, 0x2, 0x58, 0xff, 0x61, 0xfe, 0x2e, 0x2, 0x76, 0xff,
+ 0xd8, 0x1, 0xc6, 0xfc, 0xcc, 0xfc, 0xfa, 0xfe, 0x42, 0xfe, 0xfd, 0x1,
+ 0x6d, 0x0, 0xf6, 0x2, 0x61, 0xff, 0x18, 0x0, 0xed, 0xfe, 0x12, 0xfa, 0xad,
+ 0x0, 0x3a, 0xff, 0x2e, 0x2, 0xe9, 0x0, 0xac, 0x0, 0x55, 0xfc, 0x9c, 0x2,
+ 0x6a, 0x4, 0x10, 0xfb, 0x64, 0x2, 0x88, 0x1, 0x43, 0x1, 0xa3, 0x2, 0x44,
+ 0x0, 0x8e, 0x1, 0xe0, 0x0, 0x49, 0x2, 0xdd, 0xfa, 0x1e, 0xfd, 0x35, 0xff,
+ 0xa3, 0xfd, 0xb4, 0xff, 0xbd, 0xfe, 0xe8, 0x0, 0xff, 0xfd, 0xf6, 0xff,
+ 0xb0, 0xf9, 0xed, 0xff, 0xa8, 0x4, 0xed, 0xff, 0xdd, 0xff, 0x5b, 0xff,
+ 0x6c, 0x2, 0xc9, 0xfe, 0x70, 0xfc, 0x90, 0xfb, 0x3, 0xfe, 0xe6, 0xfc,
+ 0xe9, 0xfd, 0xf2, 0xfd, 0xec, 0x0, 0xc4, 0x1, 0x13, 0xfc, 0xdb, 0xfd,
+ 0xe7, 0xf9, 0x4f, 0xfe, 0x2f, 0x0, 0xae, 0xff, 0x55, 0xff, 0x7b, 0xff,
+ 0x3c, 0xfe, 0xc0, 0xff, 0x1a, 0xfe, 0x2f, 0xfc, 0xe5, 0x0, 0xe4, 0xfe,
+ 0xa7, 0x1, 0x85, 0x1, 0x3a, 0x0, 0x4f, 0xfe, 0xe9, 0xfd, 0x12, 0xfe, 0xfc,
+ 0xfa, 0x8a, 0x1, 0x3b, 0xfe, 0xee, 0x1, 0x7e, 0x0, 0x1d, 0x0, 0x7d, 0xfd,
+ 0x9, 0x0, 0x51, 0x1, 0xa0, 0xfc, 0xcb, 0x1, 0x18, 0x1, 0x73, 0x2, 0x58,
+ 0x1, 0xfe, 0xff, 0x4b, 0x1, 0x7b, 0x1, 0xd7, 0xfe, 0x2, 0xfe, 0xe4, 0xfe,
+ 0x28, 0xfe, 0xa1, 0xff, 0x20, 0xff, 0x79, 0xff, 0x83, 0x0, 0xe9, 0xfe,
+ 0x4b, 0xff, 0x95, 0xfd, 0xa2, 0xff, 0x78, 0x2, 0xcf, 0xfe, 0xb, 0x0, 0xc8,
+ 0xff, 0xf4, 0x1, 0x8d, 0xff, 0x1a, 0xfd, 0xbf, 0xfd, 0x14, 0xfe, 0xa1,
+ 0xfe, 0xa4, 0xfd, 0xb, 0x1, 0x74, 0x0, 0x5c, 0x0, 0x3a, 0xfd, 0x23, 0xfe,
+ 0x99, 0xfd, 0x1a, 0xff, 0xf8, 0xff, 0xee, 0xff, 0xb1, 0x0, 0xd, 0x0, 0xed,
+ 0xfe, 0xed, 0xfe, 0xe3, 0xfe, 0x57, 0xfe, 0x37, 0x0, 0x22, 0xff, 0x6e,
+ 0x0, 0x8d, 0x0, 0x25, 0x0, 0xc9, 0xff, 0xcd, 0xfe, 0xbf, 0xfd, 0x81, 0xfd,
+ 0x54, 0x0, 0xe7, 0xff, 0x2, 0x1, 0x83, 0x0, 0xff, 0xff, 0xbd, 0xfe, 0x70,
+ 0xff, 0x91, 0xff, 0xe4, 0xfd, 0xc5, 0x0, 0x45, 0x2, 0x4e, 0x1, 0xcc, 0x0,
+ 0xdb, 0xff, 0x88, 0x0, 0x28, 0xff, 0x39, 0xfe, 0x22, 0xfd, 0x48, 0xff,
+ 0x83, 0x0, 0x9e, 0xff, 0x95, 0xff, 0x69, 0xff, 0x5d, 0xff, 0xaa, 0xfe,
+ 0x30, 0xff, 0x45, 0xfe, 0xbb, 0xff, 0x2a, 0x2, 0xfe, 0xff, 0xaf, 0xff,
+ 0xa4, 0xff, 0xdd, 0x0, 0xf9, 0xfe, 0xe7, 0xfd, 0x50, 0xfe, 0x58, 0xfe,
+ 0xc1, 0xfe, 0xe7, 0xfe, 0x65, 0xff, 0xbb, 0xff, 0xc3, 0xff, 0xc2, 0xfd,
+ 0x2f, 0xfe, 0xab, 0xfd, 0x22, 0x0, 0xf6, 0xff, 0xa0, 0x0, 0xeb, 0xff, 0xc,
+ 0x0, 0x7b, 0xff, 0x9a, 0xff, 0x9a, 0xfe, 0xe8, 0xfd, 0x2e, 0x1, 0x49,
+ 0xff, 0xe5, 0x0, 0x61, 0x0, 0xfd, 0xff, 0xb2, 0xff, 0xfd, 0xfe, 0x79,
+ 0xfe, 0xc0, 0xfd, 0xc4, 0x0, 0x50, 0x0, 0xe2, 0x0, 0xa4, 0x0, 0xaf, 0xff,
+ 0xff, 0xfe, 0xde, 0xff, 0xa4, 0xff, 0xc2, 0xfe, 0x40, 0x0, 0x8e, 0x0, 0xd,
+ 0x1, 0x26, 0x0, 0xd4, 0xff, 0x90, 0x0, 0x68, 0xff, 0xb8, 0xfe, 0x49, 0xfe,
+ 0x5, 0xff, 0xa4, 0xff, 0xb8, 0xff, 0xa1, 0xff, 0x64, 0xff, 0x99, 0xff,
+ 0xeb, 0xfe, 0xd0, 0xfe, 0xe, 0xff, 0x75, 0xff, 0x62, 0x1, 0xc6, 0xff,
+ 0x1d, 0x0, 0xca, 0xff, 0x9d, 0x0, 0x4f, 0xff, 0xd8, 0xfd, 0xb2, 0xfe,
+ 0x4a, 0xfe, 0x28, 0xff, 0x6b, 0xff, 0xdf, 0xff, 0xd1, 0xff, 0x9a, 0xff,
+ 0x2, 0xff, 0x98, 0xfe, 0xf6, 0xfe, 0x8f, 0xff, 0x47, 0x0, 0x6c, 0x0, 0x96,
+ 0x0, 0xf, 0x0, 0xeb, 0xff, 0x11, 0x0, 0x7d, 0xfe, 0xa4, 0xff, 0x3a, 0x0,
+ 0x8b, 0xff, 0x61, 0x0, 0x14, 0x0, 0x10, 0x0, 0xe2, 0xff, 0x75, 0xff, 0xbb,
+ 0xfe, 0x10, 0x0, 0x96, 0x0, 0xd, 0x1, 0x79, 0x0, 0x3d, 0x0, 0xac, 0xff,
+ 0x7c, 0xff, 0x74, 0x0, 0xfb, 0xfe, 0x73, 0x0, 0x57, 0x0, 0xbf, 0x0, 0x59,
+ 0x0, 0xd4, 0xff, 0xf8, 0xff, 0x6e, 0x0, 0xe5, 0xff, 0x61, 0xfe, 0x67,
+ 0xff, 0x22, 0xff, 0x2c, 0x0, 0xe0, 0xff, 0x61, 0xff, 0xeb, 0xff, 0xb7,
+ 0xff, 0x4c, 0x0, 0xae, 0xfe, 0xf, 0x0, 0x9e, 0xff, 0xf7, 0x0, 0xe9, 0xff,
+ 0xf0, 0xff, 0x36, 0x0, 0x67, 0x0, 0x77, 0x0, 0xce, 0xfd, 0xb2, 0xff, 0x46,
+ 0xff, 0x62, 0xff, 0xb3, 0xff, 0x91, 0xff, 0x1e, 0x0, 0x9b, 0xff, 0xe7,
+ 0xff, 0xa2, 0xfe, 0xff, 0xff, 0x18, 0x0, 0x52, 0x0, 0x84, 0x0, 0x31, 0x0,
+ 0x24, 0x0, 0x15, 0x0, 0xc2, 0x0, 0x68, 0xfe, 0x22, 0x0, 0x34, 0x0, 0x28,
+ 0xff, 0x6f, 0x0, 0x11, 0x0, 0x46, 0x0, 0xce, 0xff, 0x42, 0x0, 0xc, 0xff,
+ 0x15, 0x0, 0x16, 0x0, 0x60, 0x0, 0x91, 0x0, 0x5f, 0x0, 0xa, 0x0, 0xf7,
+ 0xff, 0xce, 0x0, 0x2c, 0xff, 0xb, 0x0, 0x88, 0xff, 0xe4, 0xff, 0x14, 0x0,
+ 0x26, 0x0, 0x1a, 0x0, 0x7a, 0x0, 0x4f, 0x0, 0x8d, 0xfe, 0x96, 0xff, 0x4c,
+ 0xff, 0xbc, 0xff, 0xd6, 0xff, 0xc, 0x0, 0x1a, 0x0, 0xec, 0xff, 0x77, 0x0,
+ 0xd6, 0xfe, 0x32, 0x0, 0xce, 0xff, 0x3f, 0x0, 0xf3, 0xff, 0x17, 0x0, 0xa,
+ 0x0, 0x47, 0x0, 0x28, 0x0, 0x53, 0xfe, 0xc1, 0xff, 0xac, 0xff, 0x82, 0xff,
+ 0xbc, 0xff, 0xbd, 0xff, 0xf, 0x0, 0x76, 0xff, 0x33, 0x0, 0xb8, 0xfe, 0x3b,
+ 0x0, 0x2c, 0x0, 0x44, 0x0, 0x33, 0x0, 0x50, 0x0, 0x3f, 0x0, 0x0, 0x0,
+ 0xaa, 0x0, 0x71, 0xfe, 0x55, 0x0, 0xeb, 0xff, 0x88, 0xff, 0x2b, 0x0, 0xf5,
+ 0xff, 0x5, 0x0, 0xb6, 0xff, 0x2f, 0x0, 0xfd, 0xfe, 0x6c, 0x0, 0x26, 0x0,
+ 0x6b, 0x0, 0x31, 0x0, 0x3f, 0x0, 0x12, 0x0, 0x0, 0x0, 0xae, 0x0, 0xda,
+ 0xfe, 0x73, 0x0, 0xcb, 0xff, 0xb4, 0xff, 0xc9, 0xff, 0x1d, 0x0, 0x3b, 0x0,
+ 0x14, 0x0, 0x12, 0x0, 0xe9, 0xfe, 0x2c, 0x0, 0xc5, 0xff, 0x2a, 0x0, 0xef,
+ 0xff, 0xb, 0x0, 0x2a, 0x0, 0xa3, 0xff, 0x56, 0x0, 0xe4, 0xfe, 0x21, 0x0,
+ 0xeb, 0xff, 0x15, 0x0, 0xe5, 0xff, 0x17, 0x0, 0x3e, 0x0, 0x1, 0x0, 0x28,
+ 0x0, 0x5d, 0xfe, 0xfe, 0xff, 0xae, 0xff, 0x99, 0xff, 0xd1, 0xff, 0xf1,
+ 0xff, 0x17, 0x0, 0x9c, 0xff, 0x2d, 0x0, 0xee, 0xfe, 0x9a, 0x0, 0x12, 0x0,
+ 0x32, 0x0, 0x11, 0x0, 0x1d, 0x0, 0x24, 0x0, 0x3, 0x0, 0x4c, 0x0, 0x82,
+ 0xfe, 0x40, 0x0, 0xed, 0xff, 0x8b, 0xff, 0xef, 0xff, 0xe8, 0xff, 0x7, 0x0,
+ 0xf1, 0xff, 0xfd, 0xff, 0xde, 0xfe, 0x71, 0x0, 0x4b, 0x0, 0x4e, 0x0, 0x10,
+ 0x0, 0x51, 0x0, 0xf5, 0xff, 0x60, 0x0, 0x3b, 0x0, 0xd0, 0xfe, 0x32, 0x0,
+ 0xfb, 0xff, 0xd1, 0xff, 0xd9, 0xff, 0x16, 0x0, 0x1, 0x0, 0x25, 0x0, 0xdb,
+ 0xff, 0xac, 0xfe, 0x9, 0x0, 0xf3, 0xff, 0x16, 0x0, 0x11, 0x0, 0x32, 0x0,
+ 0xf0, 0xff, 0x36, 0x0, 0x24, 0x0, 0xa8, 0xfe, 0x6a, 0x0, 0xde, 0xff, 0xcc,
+ 0xff, 0xf2, 0xff, 0x12, 0x0, 0xf1, 0xff, 0x5c, 0x0, 0xf0, 0xff, 0xb9,
+ 0xfe, 0x71, 0x0, 0xbb, 0xff, 0x3, 0x0, 0xf9, 0xff, 0xec, 0xff, 0xee, 0xff,
+ 0xf8, 0xff, 0x2d, 0x0, 0xde, 0xfe, 0x98, 0x0, 0x32, 0x0, 0x2c, 0x0, 0x10,
+ 0x0, 0x2e, 0x0, 0x3, 0x0, 0x64, 0x0, 0x31, 0x0, 0xbc, 0xfe, 0x37, 0x0,
+ 0x19, 0x0, 0xb2, 0xff, 0xef, 0xff, 0xff, 0xff, 0xef, 0xff, 0xfd, 0xff,
+ 0x25, 0x0, 0x35, 0xff, 0x5e, 0x0, 0x4c, 0x0, 0x3c, 0x0, 0x1d, 0x0, 0x2f,
+ 0x0, 0x1d, 0x0, 0x24, 0x0, 0x6c, 0x0, 0xf5, 0xfe, 0x20, 0x0, 0x1c, 0x0,
+ 0xa3, 0xff, 0xf4, 0xff, 0xf9, 0xff, 0x5a, 0x0, 0xf0, 0xff, 0x33, 0x0,
+ 0x35, 0xff, 0x59, 0x0, 0xf5, 0xff, 0x1c, 0x0, 0x22, 0x0, 0x6, 0x0, 0x45,
+ 0x0, 0xfb, 0xff, 0x54, 0x0, 0x0, 0xff, 0x61, 0x0, 0xfc, 0xff, 0xcb, 0xff,
+ 0x12, 0x0, 0x5, 0x0, 0x16, 0x0, 0x48, 0x0, 0x10, 0x0, 0xcc, 0xfe, 0x3d,
+ 0x0, 0x3d, 0x0, 0xe8, 0xff, 0x1a, 0x0, 0x1a, 0x0, 0xf7, 0xff, 0x59, 0x0,
+ 0x28, 0x0, 0xf9, 0xfe, 0x6d, 0x0, 0x23, 0x0, 0xde, 0xff, 0xff, 0xff, 0xd,
+ 0x0, 0x7, 0x0, 0x45, 0x0, 0xf5, 0xff, 0xdb, 0xfe, 0x20, 0x0, 0xee, 0xff,
+ 0x9a, 0xff, 0xf9, 0xff, 0xf4, 0xff, 0x9, 0x0, 0xf4, 0xff, 0xf6, 0xff,
+ 0xfa, 0xfe, 0x51, 0x0, 0x30, 0x0, 0xc1, 0xff, 0x1b, 0x0, 0x1e, 0x0, 0x12,
+ 0x0, 0x43, 0x0, 0xfa, 0xff, 0xe1, 0xfe, 0x34, 0x0, 0xfb, 0xff, 0x7f, 0xff,
+ 0xf1, 0xff, 0xd7, 0xff, 0xee, 0xff, 0x3, 0x0, 0xee, 0xff, 0x12, 0xff,
+ 0x41, 0x0, 0x29, 0x0, 0x2e, 0x0, 0x18, 0x0, 0x2, 0x0, 0xc0, 0xff, 0x55,
+ 0x0, 0x2e, 0x0, 0xf1, 0xfe, 0x14, 0x0, 0x2c, 0x0, 0xf6, 0xff, 0xf1, 0xff,
+ 0xf1, 0xff, 0xb9, 0xff, 0x5f, 0x0, 0x12, 0x0, 0x4d, 0xff, 0x39, 0x0, 0x11,
+ 0x0, 0x66, 0x0, 0x9, 0x0, 0xf7, 0xff, 0xb0, 0xff, 0x21, 0x0, 0x54, 0x0,
+ 0x4a, 0xff, 0x43, 0x0, 0x23, 0x0, 0x4d, 0x0, 0x1b, 0x0, 0xb, 0x0, 0xe5,
+ 0xff, 0x2c, 0x0, 0x44, 0x0, 0x33, 0xff, 0x4, 0x0, 0x38, 0x0, 0x2a, 0x0,
+ 0x11, 0x0, 0xef, 0xff, 0xf8, 0xff, 0x16, 0x0, 0x65, 0x0, 0x5e, 0xff, 0x52,
+ 0x0, 0x12, 0x0, 0x5d, 0x0, 0x1c, 0x0, 0xf4, 0xff, 0xc3, 0xff, 0x56, 0x0,
+ 0x46, 0x0, 0x19, 0xff, 0x28, 0x0, 0xe, 0x0, 0xd, 0x0, 0xa, 0x0, 0xf6,
+ 0xff, 0xca, 0xff, 0x2b, 0x0, 0x38, 0x0, 0x4d, 0xff, 0x45, 0x0, 0x21, 0x0,
+ 0x31, 0x0, 0xf8, 0xff, 0x5, 0x0, 0xe5, 0xff, 0x1d, 0x0, 0x60, 0x0, 0x30,
+ 0xff, 0x32, 0x0, 0xc7, 0xff, 0xf1, 0xff, 0xfc, 0xff, 0xed, 0xff, 0xe8,
+ 0xff, 0xf8, 0xff, 0x2e, 0x0, 0x41, 0xff, 0x26, 0x0, 0x2d, 0x0, 0x2, 0x0,
+ 0x3a, 0x0, 0x20, 0x0, 0x21, 0x0, 0x10, 0x0, 0x45, 0x0, 0x39, 0xff, 0x1a,
+ 0x0, 0x1d, 0x0, 0xcb, 0xff, 0x6, 0x0, 0xf5, 0xff, 0x20, 0x0, 0x1f, 0x0,
+ 0x21, 0x0, 0x44, 0xff, 0x12, 0x0, 0xff, 0xff, 0x1d, 0x0, 0xf, 0x0, 0xde,
+ 0xff, 0xfd, 0xff, 0x24, 0x0, 0x1f, 0x0, 0x47, 0xff, 0x21, 0x0, 0x3c, 0x0,
+ 0x1, 0x0, 0xe, 0x0, 0xfd, 0xff, 0x1d, 0x0, 0x40, 0x0, 0x0, 0x0, 0x3e,
+ 0xff, 0x4, 0x0, 0x4, 0x0, 0xda, 0xff, 0xda, 0xff, 0xd7, 0xff, 0x18, 0x0,
+ 0x5, 0x0, 0x9, 0x0, 0x72, 0xff, 0x31, 0x0, 0x22, 0x0, 0x7, 0x0, 0xf7,
+ 0xff, 0xed, 0xff, 0x22, 0x0, 0x5, 0x0, 0x10, 0x0, 0x28, 0xff, 0xf4, 0xff,
+ 0x12, 0x0, 0xc4, 0xff, 0xf7, 0xff, 0xf5, 0xff, 0x28, 0x0, 0xf7, 0xff, 0x3,
+ 0x0, 0x4e, 0xff, 0x1b, 0x0, 0xeb, 0xff, 0x15, 0x0, 0xf6, 0xff, 0xf3, 0xff,
+ 0x9, 0x0, 0xb, 0x0, 0x12, 0x0, 0x57, 0xff, 0x3e, 0x0, 0xfe, 0xff, 0xe3,
+ 0xff, 0xf9, 0xff, 0xf7, 0xff, 0xa, 0x0, 0x3, 0x0, 0xec, 0xff, 0x45, 0xff,
+ 0x26, 0x0, 0xc, 0x0, 0xf5, 0xff, 0x2, 0x0, 0x9, 0x0, 0x7, 0x0, 0x9, 0x0,
+ 0x10, 0x0, 0x5a, 0xff, 0x41, 0x0, 0xde, 0xff, 0xea, 0xff, 0xe7, 0xff,
+ 0xfa, 0xff, 0xf6, 0xff, 0xf9, 0xff, 0x6, 0x0, 0x55, 0xff, 0x35, 0x0, 0xf8,
+ 0xff, 0xf5, 0xff, 0xe, 0x0, 0x3, 0x0, 0x6, 0x0, 0xea, 0xff, 0x12, 0x0,
+ 0x54, 0xff, 0x1e, 0x0, 0x13, 0x0, 0xf8, 0xff, 0xb, 0x0, 0xfd, 0xff, 0x25,
+ 0x0, 0x7, 0x0, 0x22, 0x0, 0x5c, 0xff, 0x24, 0x0, 0xee, 0xff, 0xe4, 0xff,
+ 0xee, 0xff, 0xe5, 0xff, 0x13, 0x0, 0xe8, 0xff, 0x11, 0x0, 0x6d, 0xff,
+ 0x41, 0x0, 0x1c, 0x0, 0xd, 0x0, 0x1e, 0x0, 0x1, 0x0, 0x13, 0x0, 0xe, 0x0,
+ 0x3, 0x0, 0x50, 0xff, 0xc, 0x0, 0x9, 0x0, 0xc7, 0xff, 0xf7, 0xff, 0xea,
+ 0xff, 0x1a, 0x0, 0xff, 0xff, 0x4, 0x0, 0x7c, 0xff, 0x2a, 0x0, 0xfe, 0xff,
+ 0x9, 0x0, 0xfe, 0xff, 0xf0, 0xff, 0xa, 0x0, 0xf3, 0xff, 0xf, 0x0, 0x6b,
+ 0xff, 0x27, 0x0, 0x17, 0x0, 0x1, 0x0, 0x16, 0x0, 0xfe, 0xff, 0x19, 0x0,
+ 0xff, 0xff, 0xf8, 0xff, 0x62, 0xff, 0x4, 0x0, 0xfc, 0xff, 0xf9, 0xff,
+ 0xfb, 0xff, 0xf8, 0xff, 0x18, 0x0, 0xee, 0xff, 0x1d, 0x0, 0x7d, 0xff,
+ 0x30, 0x0, 0xf8, 0xff, 0xb, 0x0, 0x2, 0x0, 0xf9, 0xff, 0xd, 0x0, 0xf6,
+ 0xff, 0xb, 0x0, 0x65, 0xff, 0x1f, 0x0, 0x16, 0x0, 0x1, 0x0, 0x13, 0x0,
+ 0x2, 0x0, 0x12, 0x0, 0x3, 0x0, 0x8, 0x0, 0x82, 0xff, 0x1b, 0x0, 0x5, 0x0,
+ 0xfd, 0xff, 0xf5, 0xff, 0xf9, 0xff, 0x7, 0x0, 0xf8, 0xff, 0xe, 0x0, 0x88,
+ 0xff, 0x1a, 0x0, 0xf9, 0xff, 0x1, 0x0, 0x7, 0x0, 0xf8, 0xff, 0x4, 0x0,
+ 0xe4, 0xff, 0xff, 0xff, 0x6f, 0xff, 0xfc, 0xff, 0x16, 0x0, 0x1d, 0x0,
+ 0x22, 0x0, 0x8, 0x0, 0x17, 0x0, 0x4, 0x0, 0x1c, 0x0, 0x7d, 0xff, 0x0, 0x0,
+ 0xee, 0xff, 0xe4, 0xff, 0xf6, 0xff, 0xe7, 0xff, 0xa, 0x0, 0xea, 0xff, 0xf,
+ 0x0, 0x95, 0xff, 0x20, 0x0, 0x0, 0x0, 0x9, 0x0, 0x1a, 0x0, 0xff, 0xff,
+ 0x5, 0x0, 0xfb, 0xff, 0x6, 0x0, 0x6e, 0xff, 0xe4, 0xff, 0x26, 0x0, 0xff,
+ 0xff, 0x20, 0x0, 0x5, 0x0, 0x15, 0x0, 0x7, 0x0, 0x12, 0x0, 0x8e, 0xff,
+ 0xeb, 0xff, 0xf7, 0xff, 0x0, 0x0, 0x7, 0x0, 0xe3, 0xff, 0x8, 0x0, 0xec,
+ 0xff, 0x17, 0x0, 0x8c, 0xff, 0xf7, 0xff, 0xf9, 0xff, 0xf3, 0xff, 0x0, 0x0,
+ 0xf6, 0xff, 0x12, 0x0, 0x0, 0x0, 0x3, 0x0, 0x67, 0xff, 0xbe, 0xff, 0x1a,
+ 0x0, 0xef, 0xff, 0x1, 0x0, 0x5, 0x0, 0x13, 0x0, 0xf1, 0xff, 0x18, 0x0,
+ 0xa4, 0xff, 0x9, 0x0, 0xeb, 0xff, 0x1f, 0x0, 0x15, 0x0, 0xe7, 0xff, 0xfa,
+ 0xff, 0xf5, 0xff, 0x12, 0x0, 0x90, 0xff, 0x11, 0x0, 0xeb, 0xff, 0xe6,
+ 0xff, 0x3, 0x0, 0xee, 0xff, 0xf4, 0xff, 0xed, 0xff, 0xfa, 0xff, 0x89,
+ 0xff, 0xfa, 0xff, 0x21, 0x0, 0xff, 0xff, 0x10, 0x0, 0x10, 0x0, 0xff, 0xff,
+ 0xec, 0xff, 0x10, 0x0, 0xab, 0xff, 0x16, 0x0, 0xe7, 0xff, 0xf, 0x0, 0x11,
+ 0x0, 0xe6, 0xff, 0xfa, 0xff, 0xf2, 0xff, 0xd, 0x0, 0x9f, 0xff, 0x19, 0x0,
+ 0xe7, 0xff, 0x0, 0x0, 0xc, 0x0, 0xf1, 0xff, 0xef, 0xff, 0xf7, 0xff, 0xa,
+ 0x0, 0x94, 0xff, 0x16, 0x0, 0x6, 0x0, 0xf9, 0xff, 0x1a, 0x0, 0x0, 0x0,
+ 0xa, 0x0, 0x7, 0x0, 0xf7, 0xff, 0xb6, 0xff, 0x2a, 0x0, 0xea, 0xff, 0x9,
+ 0x0, 0x7, 0x0, 0xee, 0xff, 0xa, 0x0, 0x9, 0x0, 0x6, 0x0, 0xaa, 0xff, 0x3d,
+ 0x0, 0x6, 0x0, 0x6, 0x0, 0x7, 0x0, 0xa, 0x0, 0xf4, 0xff, 0x4, 0x0, 0xf6,
+ 0xff, 0x96, 0xff, 0x2b, 0x0, 0x1, 0x0, 0xeb, 0xff, 0x12, 0x0, 0x1, 0x0,
+ 0xa, 0x0, 0x0, 0x0, 0xdf, 0xff, 0xc1, 0xff, 0x3e, 0x0, 0xd7, 0xff, 0xf5,
+ 0xff, 0xef, 0xff, 0xf0, 0xff, 0x6, 0x0, 0x8, 0x0, 0xfe, 0xff, 0xb6, 0xff,
+ 0x3a, 0x0, 0xf2, 0xff, 0xf8, 0xff, 0xf7, 0xff, 0x6, 0x0, 0xf2, 0xff, 0xf6,
+ 0xff, 0xf2, 0xff, 0xb7, 0xff, 0x28, 0x0, 0x3, 0x0, 0x17, 0x0, 0x11, 0x0,
+ 0x9, 0x0, 0xa, 0x0, 0x3, 0x0, 0xfa, 0xff, 0xcf, 0xff, 0x2e, 0x0, 0xe9,
+ 0xff, 0xfc, 0xff, 0xe8, 0xff, 0xf7, 0xff, 0x4, 0x0, 0x2, 0x0, 0x14, 0x0,
+ 0xce, 0xff, 0x3a, 0x0, 0x3, 0x0, 0xa, 0x0, 0x7, 0x0, 0xe, 0x0, 0xfa, 0xff,
+ 0xf6, 0xff, 0x4, 0x0, 0xc7, 0xff, 0x28, 0x0, 0xfb, 0xff, 0x10, 0x0, 0xa,
+ 0x0, 0x6, 0x0, 0xe, 0x0, 0x2, 0x0, 0x4, 0x0, 0xcf, 0xff, 0x24, 0x0, 0xde,
+ 0xff, 0xf6, 0xff, 0xe1, 0xff, 0xf8, 0xff, 0xf8, 0xff, 0xf1, 0xff, 0x1b,
+ 0x0, 0xcc, 0xff, 0x3a, 0x0, 0xfc, 0xff, 0xc, 0x0, 0x6, 0x0, 0xe, 0x0,
+ 0xfb, 0xff, 0xf9, 0xff, 0x1, 0x0, 0xbf, 0xff, 0x1b, 0x0, 0xee, 0xff, 0xfc,
+ 0xff, 0x0, 0x0, 0xfa, 0xff, 0xc, 0x0, 0xf6, 0xff, 0xc, 0x0, 0xd5, 0xff,
+ 0x2a, 0x0, 0xe6, 0xff, 0xf9, 0xff, 0xf0, 0xff, 0xfd, 0xff, 0xf8, 0xff,
+ 0xf0, 0xff, 0x20, 0x0, 0xc9, 0xff, 0x2f, 0x0, 0x1, 0x0, 0xfc, 0xff, 0x14,
+ 0x0, 0xa, 0x0, 0x4, 0x0, 0xf5, 0xff, 0x2, 0x0, 0xcd, 0xff, 0x17, 0x0,
+ 0xfa, 0xff, 0xfe, 0xff, 0x7, 0x0, 0xf9, 0xff, 0xc, 0x0, 0xf9, 0xff, 0x1d,
+ 0x0, 0xd5, 0xff, 0x28, 0x0, 0xee, 0xff, 0xf1, 0xff, 0xef, 0xff, 0xfd,
+ 0xff, 0xff, 0xff, 0xfb, 0xff, 0x18, 0x0, 0xcb, 0xff, 0x25, 0x0, 0x4, 0x0,
+ 0x6, 0x0, 0x11, 0x0, 0x2, 0x0, 0xa, 0x0, 0xfe, 0xff, 0x2, 0x0, 0xd1, 0xff,
+ 0x13, 0x0, 0xf9, 0xff, 0x0, 0x0, 0xfe, 0xff, 0xf2, 0xff, 0xf, 0x0, 0x1,
+ 0x0, 0x1c, 0x0, 0xcd, 0xff, 0x1b, 0x0, 0xf2, 0xff, 0xf0, 0xff, 0xf2, 0xff,
+ 0xfc, 0xff, 0x2, 0x0, 0xf5, 0xff, 0x12, 0x0, 0xcf, 0xff, 0x1e, 0x0, 0x9,
+ 0x0, 0x11, 0x0, 0x15, 0x0, 0x3, 0x0, 0x10, 0x0, 0x1, 0x0, 0x7, 0x0, 0xd4,
+ 0xff, 0x5, 0x0, 0xf6, 0xff, 0xf8, 0xff, 0xf0, 0xff, 0xf1, 0xff, 0xf, 0x0,
+ 0xff, 0xff, 0x1e, 0x0, 0xda, 0xff, 0x19, 0x0, 0xfa, 0xff, 0x4, 0x0, 0xf5,
+ 0xff, 0x2, 0x0, 0x3, 0x0, 0xff, 0xff, 0xf, 0x0, 0xd2, 0xff, 0x13, 0x0,
+ 0x7, 0x0, 0x10, 0x0, 0xb, 0x0, 0xfe, 0xff, 0x14, 0x0, 0x7, 0x0, 0x4, 0x0,
+ 0xd6, 0xff, 0x1, 0x0, 0xf7, 0xff, 0xfb, 0xff, 0xea, 0xff, 0xf2, 0xff, 0xb,
+ 0x0, 0x0, 0x0, 0x1e, 0x0, 0xd5, 0xff, 0x19, 0x0, 0xfa, 0xff, 0x2, 0x0,
+ 0xf9, 0xff, 0x5, 0x0, 0x6, 0x0, 0x3, 0x0, 0x4, 0x0, 0xcc, 0xff, 0x7, 0x0,
+ 0x0, 0x0, 0x9, 0x0, 0x6, 0x0, 0xfc, 0xff, 0x10, 0x0, 0x2, 0x0, 0x6, 0x0,
+ 0xd7, 0xff, 0x2, 0x0, 0xf7, 0xff, 0xfb, 0xff, 0xeb, 0xff, 0xf8, 0xff, 0x7,
+ 0x0, 0x0, 0x0, 0x19, 0x0, 0xd2, 0xff, 0x12, 0x0, 0xfe, 0xff, 0xfd, 0xff,
+ 0xff, 0xff, 0x6, 0x0, 0x8, 0x0, 0x1, 0x0, 0x1, 0x0, 0xd4, 0xff, 0x5, 0x0,
+ 0x5, 0x0, 0xc, 0x0, 0x9, 0x0, 0xfb, 0xff, 0xf, 0x0, 0x5, 0x0, 0xe, 0x0,
+ 0xd6, 0xff, 0x3, 0x0, 0xf6, 0xff, 0xf5, 0xff, 0xe8, 0xff, 0xf8, 0xff, 0x5,
+ 0x0, 0x3, 0x0, 0x13, 0x0, 0xd3, 0xff, 0xf, 0x0, 0x2, 0x0, 0x7, 0x0, 0x1,
+ 0x0, 0x8, 0x0, 0x7, 0x0, 0x4, 0x0, 0xfe, 0xff, 0xd4, 0xff, 0x0, 0x0, 0x0,
+ 0x0, 0x7, 0x0, 0xff, 0xff, 0xfa, 0xff, 0xe, 0x0, 0x7, 0x0, 0xa, 0x0, 0xd8,
+ 0xff, 0x2, 0x0, 0xf6, 0xff, 0xf4, 0xff, 0xe8, 0xff, 0xfb, 0xff, 0x3, 0x0,
+ 0x1, 0x0, 0xe, 0x0, 0xd5, 0xff, 0xc, 0x0, 0x6, 0x0, 0xa, 0x0, 0x8, 0x0,
+ 0x8, 0x0, 0x9, 0x0, 0x5, 0x0, 0xfb, 0xff, 0xd5, 0xff, 0xf9, 0xff, 0xfe,
+ 0xff, 0x1, 0x0, 0xfb, 0xff, 0xf8, 0xff, 0xa, 0x0, 0x3, 0x0, 0xb, 0x0,
+ 0xda, 0xff, 0x5, 0x0, 0xf8, 0xff, 0xfa, 0xff, 0xf0, 0xff, 0x1, 0x0, 0xff,
+ 0xff, 0x0, 0x0, 0x9, 0x0, 0xd2, 0xff, 0x8, 0x0, 0x4, 0x0, 0x8, 0x0, 0x9,
+ 0x0, 0x7, 0x0, 0x9, 0x0, 0x3, 0x0, 0xf9, 0xff, 0xd7, 0xff, 0xfb, 0xff,
+ 0xf9, 0xff, 0x0, 0x0, 0xf8, 0xff, 0xf9, 0xff, 0x5, 0x0, 0x1, 0x0, 0xb,
+ 0x0, 0xd8, 0xff, 0x5, 0x0, 0xf7, 0xff, 0xf7, 0xff, 0xf4, 0xff, 0x2, 0x0,
+ 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0xd3, 0xff, 0x3, 0x0, 0x5, 0x0, 0x7,
+ 0x0, 0xb, 0x0, 0x5, 0x0, 0x8, 0x0, 0x1, 0x0, 0xf9, 0xff, 0xda, 0xff, 0xf9,
+ 0xff, 0xfb, 0xff, 0xfe, 0xff, 0xf6, 0xff, 0xf9, 0xff, 0x3, 0x0, 0x0, 0x0,
+ 0xa, 0x0, 0xd8, 0xff, 0x4, 0x0, 0xfb, 0xff, 0xf9, 0xff, 0xf8, 0xff, 0x4,
+ 0x0, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xd6, 0xff, 0x1, 0x0, 0x5, 0x0,
+ 0x9, 0x0, 0xb, 0x0, 0x2, 0x0, 0x7, 0x0, 0x2, 0x0, 0xfa, 0xff, 0xda, 0xff,
+ 0xf9, 0xff, 0xf8, 0xff, 0xfa, 0xff, 0xf3, 0xff, 0xf9, 0xff, 0x1, 0x0, 0x0,
+ 0x0, 0x7, 0x0, 0xd9, 0xff, 0x5, 0x0, 0xfd, 0xff, 0xfe, 0xff, 0xfe, 0xff,
+ 0x6, 0x0, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xd7, 0xff, 0xfc, 0xff, 0x2,
+ 0x0, 0x6, 0x0, 0x8, 0x0, 0xff, 0xff, 0x7, 0x0, 0x2, 0x0, 0xfa, 0xff, 0xdc,
+ 0xff, 0xfa, 0xff, 0xf8, 0xff, 0xf9, 0xff, 0xf2, 0xff, 0xfb, 0xff, 0xfe,
+ 0xff, 0xff, 0xff, 0x5, 0x0, 0xd8, 0xff, 0x6, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
+ 0x0, 0x6, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf7, 0xff, 0xd9, 0xff, 0xfc, 0xff,
+ 0x2, 0x0, 0x5, 0x0, 0x5, 0x0, 0xfd, 0xff, 0x6, 0x0, 0x1, 0x0, 0xfd, 0xff,
+ 0xdd, 0xff, 0xfe, 0xff, 0xf9, 0xff, 0xfa, 0xff, 0xf3, 0xff, 0xfc, 0xff,
+ 0xfe, 0xff, 0xff, 0xff, 0x4, 0x0, 0xd8, 0xff, 0x5, 0x0, 0x1, 0x0, 0x2,
+ 0x0, 0x5, 0x0, 0x5, 0x0, 0x2, 0x0, 0x0, 0x0, 0xf6, 0xff, 0xda, 0xff, 0xfd,
+ 0xff, 0x0, 0x0, 0x4, 0x0, 0x2, 0x0, 0xfb, 0xff, 0x4, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xdd, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xf4, 0xff, 0xfd,
+ 0xff, 0xfe, 0xff, 0xff, 0xff, 0x1, 0x0, 0xd9, 0xff, 0x5, 0x0, 0x2, 0x0,
+ 0x4, 0x0, 0x7, 0x0, 0x4, 0x0, 0x3, 0x0, 0x0, 0x0, 0xf7, 0xff, 0xdc, 0xff,
+ 0xfc, 0xff, 0xfe, 0xff, 0x1, 0x0, 0xff, 0xff, 0xfb, 0xff, 0x3, 0x0, 0x0,
+ 0x0, 0x2, 0x0, 0xde, 0xff, 0x2, 0x0, 0xfa, 0xff, 0xfa, 0xff, 0xf7, 0xff,
+ 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x0, 0x0, 0xda, 0xff, 0x3, 0x0, 0x2,
+ 0x0, 0x4, 0x0, 0x8, 0x0, 0x3, 0x0, 0x3, 0x0, 0x0, 0x0, 0xf8, 0xff, 0xde,
+ 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xfa, 0xff, 0x1,
+ 0x0, 0x0, 0x0, 0x3, 0x0, 0xde, 0xff, 0x3, 0x0, 0xfb, 0xff, 0xfc, 0xff,
+ 0xfa, 0xff, 0x1, 0x0, 0xfd, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xdb, 0xff, 0x2,
+ 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0xfa,
+ 0xff, 0xdf, 0xff, 0xfd, 0xff, 0xfb, 0xff, 0xfe, 0xff, 0xfa, 0xff, 0xfa,
+ 0xff, 0x0, 0x0, 0xfe, 0xff, 0x4, 0x0, 0xde, 0xff, 0x4, 0x0, 0xfd, 0xff,
+ 0xfd, 0xff, 0xfd, 0xff, 0x2, 0x0, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff,
+ 0xdd, 0xff, 0x1, 0x0, 0x2, 0x0, 0x5, 0x0, 0x7, 0x0, 0x0, 0x0, 0x4, 0x0,
+ 0xff, 0xff, 0xfc, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xfd, 0xff,
+ 0xf9, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x4, 0x0, 0xdf, 0xff, 0x5,
+ 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xff,
+ 0xfb, 0xff, 0xdf, 0xff, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x5, 0x0, 0xfe,
+ 0xff, 0x4, 0x0, 0xff, 0xff, 0xfe, 0xff, 0xe2, 0xff, 0x0, 0x0, 0xfa, 0xff,
+ 0xfc, 0xff, 0xf8, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x3, 0x0,
+ 0xdf, 0xff, 0x5, 0x0, 0xff, 0xff, 0x0, 0x0, 0x2, 0x0, 0x2, 0x0, 0x1, 0x0,
+ 0xfe, 0xff, 0xfb, 0xff, 0xdf, 0xff, 0x0, 0x0, 0xff, 0xff, 0x2, 0x0, 0x2,
+ 0x0, 0xfd, 0xff, 0x4, 0x0, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0x2, 0x0,
+ 0xfb, 0xff, 0xfc, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0xfe, 0xff,
+ 0x1, 0x0, 0xdf, 0xff, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x3, 0x0, 0x3, 0x0,
+ 0x2, 0x0, 0xff, 0xff, 0xfb, 0xff, 0xe1, 0xff, 0x0, 0x0, 0xfe, 0xff, 0x1,
+ 0x0, 0x0, 0x0, 0xfc, 0xff, 0x3, 0x0, 0xff, 0xff, 0x0, 0x0, 0xe3, 0xff,
+ 0x3, 0x0, 0xfb, 0xff, 0xfc, 0xff, 0xfa, 0xff, 0xfe, 0xff, 0xfe, 0xff,
+ 0xfe, 0xff, 0x0, 0x0, 0xe0, 0xff, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x4, 0x0,
+ 0x2, 0x0, 0x2, 0x0, 0xff, 0xff, 0xfb, 0xff, 0xe2, 0xff, 0xff, 0xff, 0xfd,
+ 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0x1, 0x0, 0xff, 0xff, 0x2, 0x0,
+ 0xe3, 0xff, 0x3, 0x0, 0xfc, 0xff, 0xfd, 0xff, 0xfb, 0xff, 0x0, 0x0, 0xfe,
+ 0xff, 0xfe, 0xff, 0xff, 0xff, 0xe1, 0xff, 0x3, 0x0, 0x1, 0x0, 0x2, 0x0,
+ 0x4, 0x0, 0x1, 0x0, 0x3, 0x0, 0xff, 0xff, 0xfc, 0xff, 0xe4, 0xff, 0x0,
+ 0x0, 0xfc, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0x1, 0x0, 0xff, 0xff,
+ 0x2, 0x0, 0xe4, 0xff, 0x5, 0x0, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0x1,
+ 0x0, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xe2, 0xff, 0x2, 0x0, 0x0, 0x0,
+ 0x2, 0x0, 0x4, 0x0, 0x0, 0x0, 0x3, 0x0, 0xff, 0xff, 0xfd, 0xff, 0xe5,
+ 0xff, 0x1, 0x0, 0xfc, 0xff, 0xfd, 0xff, 0xfb, 0xff, 0xfd, 0xff, 0x0, 0x0,
+ 0xff, 0xff, 0x2, 0x0, 0xe4, 0xff, 0x5, 0x0, 0xfe, 0xff, 0xfe, 0xff, 0xff,
+ 0xff, 0x1, 0x0, 0x0, 0x0, 0xfe, 0xff, 0xfd, 0xff, 0xe2, 0xff, 0x1, 0x0,
+ 0xff, 0xff, 0x1, 0x0, 0x3, 0x0, 0xff, 0xff, 0x4, 0x0, 0xff, 0xff, 0xfe,
+ 0xff, 0xe5, 0xff, 0x1, 0x0, 0xfc, 0xff, 0xfc, 0xff, 0xfb, 0xff, 0xfd,
+ 0xff, 0x0, 0x0, 0xfe, 0xff, 0x1, 0x0, 0xe3, 0xff, 0x4, 0x0, 0xff, 0xff,
+ 0xff, 0xff, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0xff, 0xff, 0xfd, 0xff, 0xe2,
+ 0xff, 0x1, 0x0, 0xff, 0xff, 0x0, 0x0, 0x1, 0x0, 0xfe, 0xff, 0x2, 0x0,
+ 0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0x2, 0x0, 0xfc, 0xff, 0xfd, 0xff,
+ 0xfb, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0xe0, 0xff, 0x3,
+ 0x0, 0xff, 0xff, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0xff, 0xff, 0xfd,
+ 0xff, 0xdf, 0xff, 0x0, 0x0, 0xfe, 0xff, 0x0, 0x0, 0x0, 0x0, 0xfe, 0xff,
+ 0x2, 0x0, 0xff, 0xff, 0x0, 0x0, 0xe0, 0xff, 0x2, 0x0, 0xfd, 0xff, 0xfe,
+ 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0xde,
+ 0xff, 0x2, 0x0, 0xff, 0xff, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0,
+ 0x0, 0xfe, 0xff, 0xe0, 0xff, 0x0, 0x0, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfe, 0xff, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0xe1, 0xff, 0x1, 0x0, 0xfe,
+ 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
+ 0xe2, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x1,
+ 0x0, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0x0, 0x0, 0xfe, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0xff, 0x0,
+ 0x0, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff,
+ 0x0, 0x0, 0xe8, 0xff, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xec, 0xff, 0x0, 0x0, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xee,
+ 0xff, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0}},
+ {"WAV/Subject_002/SH/sh_hrir_order_3.wav",
+ {0x52, 0x49, 0x46, 0x46, 0x24, 0x20, 0x0, 0x0, 0x57, 0x41, 0x56, 0x45,
+ 0x66, 0x6d, 0x74, 0x20, 0x10, 0x0, 0x0, 0x0, 0x1, 0x0, 0x10, 0x0, 0x80,
+ 0xbb, 0x0, 0x0, 0x0, 0x70, 0x17, 0x0, 0x20, 0x0, 0x10, 0x0, 0x64, 0x61,
+ 0x74, 0x61, 0x0, 0x20, 0x0, 0x0, 0xfe, 0xff, 0x4, 0x0, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
+ 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff,
+ 0xf3, 0xff, 0x18, 0x0, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff,
+ 0xfc, 0xff, 0xfd, 0xff, 0xf9, 0xff, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0,
+ 0x0, 0x1, 0x0, 0x0, 0x0, 0xff, 0xff, 0xfd, 0xff, 0xa, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xfe,
+ 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x2,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0,
+ 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff,
+ 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0xff, 0xff,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x6, 0x0, 0xf3, 0xff,
+ 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x2, 0x0, 0x2, 0x0, 0x5, 0x0,
+ 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x0,
+ 0x0, 0x0, 0x0, 0xf1, 0xff, 0xfd, 0xff, 0x0, 0x0, 0x0, 0x0, 0xfc, 0xff,
+ 0x2, 0x0, 0xff, 0xff, 0x8, 0x0, 0x2, 0x0, 0xfe, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xfd, 0xff, 0x2, 0x0, 0x0, 0x0, 0xa, 0x0, 0xef, 0xff, 0x4, 0x0,
+ 0xfe, 0xff, 0xfd, 0xff, 0x8, 0x0, 0x8, 0x0, 0x4, 0x0, 0x7, 0x0, 0x2, 0x0,
+ 0x0, 0x0, 0x6, 0x0, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0x1, 0x0, 0xde,
+ 0xff, 0x7b, 0xff, 0xf9, 0xff, 0x2, 0x0, 0x3, 0x0, 0xf7, 0xff, 0x41, 0x0,
+ 0xff, 0xff, 0x79, 0x0, 0x4c, 0x0, 0xfd, 0xff, 0x38, 0x0, 0xff, 0xff, 0xfb,
+ 0xff, 0x6, 0x0, 0xff, 0xff, 0xb2, 0xff, 0xd3, 0xfe, 0x9, 0x0, 0x0, 0x0,
+ 0xff, 0xff, 0xe, 0x0, 0x9c, 0x0, 0x5, 0x0, 0xe, 0x1, 0xa3, 0x0, 0x1, 0x0,
+ 0x82, 0x0, 0xfc, 0xff, 0xff, 0xff, 0xf8, 0xff, 0x2, 0x0, 0x51, 0x0, 0xf0,
+ 0x0, 0xfa, 0xff, 0xfe, 0xff, 0x1, 0x0, 0xfa, 0xff, 0x6f, 0xff, 0x7, 0x0,
+ 0x1, 0xff, 0x42, 0xff, 0x3, 0x0, 0x6f, 0xff, 0x1, 0x0, 0xfd, 0xff, 0xff,
+ 0xff, 0x0, 0x0, 0x75, 0x1, 0x13, 0x5, 0xf7, 0xff, 0xc, 0x0, 0xc, 0x0,
+ 0xf3, 0xff, 0x1, 0xfd, 0xf8, 0xff, 0xd5, 0xfa, 0x42, 0xfc, 0xf7, 0xff,
+ 0x13, 0xfd, 0xfc, 0xff, 0xf5, 0xff, 0xf, 0x0, 0xfe, 0xff, 0x7b, 0xfe,
+ 0x93, 0xfc, 0x1d, 0x0, 0xfb, 0xff, 0xfe, 0xff, 0x2f, 0x0, 0x5e, 0x1, 0x19,
+ 0x0, 0x21, 0x2, 0xb0, 0x0, 0x10, 0x0, 0xad, 0x0, 0x1, 0x0, 0x2, 0x0, 0xd5,
+ 0xff, 0x1, 0x0, 0x3f, 0xfc, 0xb4, 0xf5, 0xfa, 0xff, 0xa5, 0xff, 0x7e,
+ 0xff, 0xf4, 0xff, 0xa2, 0x4, 0x14, 0x0, 0xb6, 0x8, 0x40, 0x5, 0xd, 0x0,
+ 0xb3, 0x3, 0xf9, 0xff, 0x1d, 0x0, 0x13, 0x0, 0x3d, 0x0, 0xbb, 0x3, 0xc0,
+ 0x7, 0xc2, 0xfe, 0x8d, 0xff, 0x61, 0xff, 0x50, 0xfe, 0x99, 0xfc, 0x7, 0x0,
+ 0x6d, 0xfb, 0x9e, 0xff, 0xfe, 0xff, 0xf0, 0xfe, 0x28, 0x0, 0x59, 0x0, 0x1,
+ 0x1, 0x47, 0x0, 0xcd, 0x4, 0x66, 0x8, 0xf8, 0xfe, 0xf8, 0x0, 0x60, 0x1,
+ 0xb1, 0xfe, 0x9d, 0xfe, 0x5, 0xff, 0xf2, 0xfb, 0x4c, 0x0, 0x2e, 0xff,
+ 0x47, 0x1, 0x71, 0x0, 0x95, 0xff, 0x36, 0x0, 0x5e, 0xff, 0x15, 0xff, 0xd8,
+ 0xfb, 0x77, 0x5, 0x4f, 0x1, 0xd9, 0x1, 0xb5, 0x7, 0xf9, 0x4, 0xd, 0x0,
+ 0x59, 0x3, 0x5c, 0x0, 0xd, 0x0, 0x89, 0x3, 0xaa, 0xff, 0x98, 0xfe, 0x91,
+ 0xfa, 0x20, 0xff, 0xbd, 0xfa, 0xf9, 0xf3, 0x0, 0x2, 0x2d, 0x0, 0x67, 0x0,
+ 0x54, 0x2, 0xaa, 0x2, 0xf6, 0x1, 0x4f, 0x9, 0x47, 0x4, 0xa0, 0x1, 0x6d,
+ 0x0, 0xec, 0xfd, 0x68, 0xff, 0x78, 0x1, 0xc2, 0xff, 0xfc, 0xf7, 0x41,
+ 0xef, 0x68, 0xf9, 0x8, 0xff, 0x21, 0xfe, 0x5d, 0xf6, 0x5d, 0x2, 0xd6,
+ 0xff, 0x8e, 0xb, 0xa0, 0x4, 0xe0, 0xff, 0xe, 0xff, 0x64, 0xff, 0xb5, 0x3,
+ 0x7d, 0x8, 0x36, 0x1, 0xf0, 0x1, 0x9c, 0xb, 0x12, 0xfe, 0xe8, 0xfc, 0x14,
+ 0xfc, 0x8c, 0xfe, 0x6f, 0xfb, 0xdd, 0xff, 0x25, 0xef, 0xf, 0xf1, 0xcf,
+ 0xff, 0x68, 0xf9, 0x51, 0x4, 0x1, 0x2, 0x53, 0xfa, 0xad, 0x1, 0x74, 0xff,
+ 0xbf, 0x6, 0xdd, 0xff, 0x56, 0x0, 0x70, 0x2, 0x3d, 0x0, 0xdd, 0xfa, 0x4c,
+ 0xff, 0x2f, 0xf3, 0xac, 0xf5, 0x59, 0xff, 0x48, 0xf9, 0x24, 0x0, 0xe0,
+ 0xf8, 0x45, 0xff, 0x0, 0xfe, 0x32, 0xf9, 0x2e, 0xf4, 0x9b, 0x3, 0x4f, 0x1,
+ 0xd5, 0xff, 0xe5, 0x4, 0xfa, 0x2, 0xc0, 0xff, 0x9a, 0x4, 0x8f, 0xfe, 0xcb,
+ 0xff, 0x5f, 0xff, 0x22, 0xfc, 0x9, 0x1, 0xce, 0x1, 0xf9, 0x0, 0x0, 0x2,
+ 0x2, 0x3, 0xc5, 0xff, 0x8b, 0xfb, 0xa3, 0xf4, 0x3a, 0x1, 0x9d, 0xfa, 0xa7,
+ 0xfe, 0x2d, 0x0, 0x3f, 0xfb, 0x11, 0xff, 0xb6, 0xfa, 0xd2, 0x2, 0x9a, 0x6,
+ 0x5f, 0xfa, 0x3d, 0x7, 0x70, 0x3, 0xe0, 0x6, 0x29, 0xfd, 0x27, 0xfd, 0xc,
+ 0x2, 0xbb, 0xfa, 0xe9, 0xf5, 0x5b, 0xfd, 0x42, 0xfe, 0x3f, 0xfe, 0x3a,
+ 0xfd, 0x45, 0xf6, 0xc0, 0xff, 0xf9, 0xfc, 0x7f, 0x5, 0x49, 0xfc, 0xa0,
+ 0x7, 0xd, 0x11, 0x87, 0x9, 0x4f, 0x5, 0xb, 0xf, 0xd9, 0x5, 0x1d, 0x3,
+ 0xc2, 0x4, 0xa, 0xf0, 0x8, 0x0, 0x60, 0x3, 0x94, 0x5, 0x69, 0xfe, 0xfc,
+ 0xfd, 0x1a, 0x2, 0xe4, 0xf5, 0xe3, 0xd, 0xd9, 0x10, 0x9a, 0x0, 0x6f, 0xd,
+ 0xf1, 0x2, 0x40, 0xfd, 0xb1, 0x2, 0xef, 0x0, 0x6f, 0xff, 0x2e, 0xa, 0x67,
+ 0x2, 0xb, 0xc, 0x9, 0x4, 0xa9, 0xfc, 0xe3, 0xfe, 0xb4, 0x4, 0x7, 0xd,
+ 0xf1, 0x15, 0xed, 0xf4, 0x46, 0xfe, 0x4c, 0xfe, 0x59, 0xfd, 0x9b, 0xf6,
+ 0x6c, 0xfe, 0x1e, 0xfc, 0x5, 0xb, 0x58, 0xfe, 0x3c, 0x6, 0x8f, 0xfe, 0xcb,
+ 0x0, 0xa, 0xfc, 0x9, 0x0, 0x58, 0x1, 0x5f, 0x10, 0x61, 0xfc, 0xb4, 0xf4,
+ 0x8a, 0x8, 0xe7, 0x2, 0xf6, 0xf9, 0x63, 0x0, 0x42, 0xeb, 0x5e, 0x6, 0xf6,
+ 0xfe, 0x71, 0x5, 0xce, 0xfa, 0xf3, 0x3, 0x8, 0xfe, 0xd7, 0xf2, 0xf0, 0xfd,
+ 0xb3, 0xfa, 0xf6, 0x8, 0xb9, 0xb, 0xfb, 0x5, 0xa, 0x6, 0x90, 0xff, 0x1d,
+ 0x1, 0x69, 0x5, 0x9f, 0x2, 0x2a, 0x2, 0xa4, 0xfd, 0xd1, 0xfc, 0xca, 0xfc,
+ 0x21, 0x4, 0xf3, 0x1, 0x41, 0x2, 0x1f, 0x7, 0x19, 0x0, 0xe6, 0x3, 0xc2,
+ 0xf9, 0x69, 0x0, 0xc5, 0xfe, 0x9d, 0x3, 0x29, 0xf9, 0x51, 0xfc, 0x1d, 0x3,
+ 0x96, 0xfe, 0xf0, 0x1, 0x72, 0x0, 0x73, 0xfe, 0x7a, 0x7, 0x4a, 0xfe, 0x32,
+ 0x6, 0xa2, 0xf3, 0xfd, 0xf4, 0x50, 0xf7, 0xba, 0xf0, 0xb6, 0x0, 0x8a,
+ 0xfa, 0xcd, 0xf1, 0x37, 0xfe, 0x9b, 0xfc, 0x55, 0x3, 0x5e, 0x5, 0xfa, 0x1,
+ 0x54, 0x4, 0x1c, 0x1, 0xd1, 0x3, 0x4a, 0x1, 0x93, 0x1, 0xc, 0xfb, 0x38,
+ 0xf8, 0x8f, 0x2, 0x6c, 0xff, 0xc4, 0x0, 0x19, 0x8, 0xb3, 0x1, 0xb0, 0xfe,
+ 0x98, 0x2, 0xbf, 0xff, 0xae, 0x2, 0xa1, 0xfe, 0xd3, 0x4, 0x85, 0x4, 0x95,
+ 0xff, 0x86, 0x9, 0x31, 0xfd, 0xf9, 0xf9, 0x8, 0x9, 0x15, 0xfe, 0x36, 0x2,
+ 0x9e, 0xc, 0x9, 0x3, 0x88, 0xfd, 0x4a, 0x0, 0xe1, 0xfd, 0x49, 0xfe, 0xc4,
+ 0xfc, 0x6c, 0x5, 0x8, 0xa, 0x58, 0xfb, 0xa7, 0x6, 0x69, 0x9, 0x7c, 0x0,
+ 0x71, 0x3, 0xbf, 0x5, 0xd8, 0xf6, 0x1b, 0x18, 0x9e, 0x1, 0x13, 0xfe, 0x22,
+ 0xff, 0x60, 0x3, 0x2d, 0xfd, 0xa2, 0xff, 0x91, 0x4, 0x6, 0x8, 0xd7, 0xfb,
+ 0x24, 0x4, 0x10, 0x4, 0x91, 0x3, 0xdc, 0x0, 0x21, 0xb, 0xf6, 0x0, 0xa1,
+ 0x14, 0xbb, 0x2, 0xb4, 0x3, 0xf0, 0x2, 0x6b, 0x3, 0x26, 0x5, 0x5b, 0x0,
+ 0x51, 0x0, 0xf7, 0xfc, 0x8f, 0xf9, 0xba, 0x5, 0xb6, 0xfb, 0xf, 0x1, 0x9f,
+ 0xf9, 0x4e, 0x4, 0x50, 0x8, 0xf7, 0x7, 0xc9, 0x0, 0xff, 0x0, 0xd0, 0xfe,
+ 0x65, 0x1, 0xb8, 0xff, 0x0, 0xa, 0xc9, 0xfc, 0x77, 0xff, 0x97, 0xff, 0xef,
+ 0xfa, 0x6e, 0x0, 0x48, 0x1, 0xdc, 0xfb, 0x83, 0xff, 0x14, 0x0, 0x11, 0x3,
+ 0xa9, 0xfd, 0xb3, 0x2, 0xbe, 0xff, 0x5f, 0x3, 0xdf, 0xfd, 0x44, 0xff,
+ 0xc6, 0xfc, 0xe9, 0xf9, 0xa, 0xfb, 0xec, 0xf1, 0x7a, 0xfd, 0x1d, 0x1,
+ 0x75, 0xfb, 0x9f, 0x2, 0x26, 0xfc, 0x67, 0x1, 0xd9, 0xff, 0x77, 0x0, 0x27,
+ 0x1, 0x7e, 0x0, 0xe3, 0xff, 0xf2, 0xfa, 0x7a, 0x0, 0x1c, 0xfc, 0x14, 0xfd,
+ 0x1d, 0x1, 0x89, 0xfd, 0x1c, 0xfd, 0x70, 0x3, 0x1d, 0x1, 0xff, 0x1, 0x3e,
+ 0x3, 0xbf, 0x0, 0xfd, 0xfd, 0xb8, 0xfd, 0x6f, 0xfe, 0x10, 0x0, 0x23, 0xfe,
+ 0x3c, 0x1, 0x2f, 0xfe, 0x78, 0xff, 0xea, 0x2, 0xe7, 0xfc, 0x38, 0xff,
+ 0xde, 0x4, 0x8d, 0x2, 0x81, 0xfb, 0x19, 0x2, 0x50, 0x1, 0xb6, 0xfe, 0xc9,
+ 0x0, 0x9a, 0x0, 0x27, 0x2, 0x78, 0xfe, 0xa0, 0xfc, 0x31, 0xff, 0xe7, 0xfc,
+ 0x58, 0x4, 0x40, 0x1, 0xe2, 0x0, 0xca, 0x2, 0xb1, 0x6, 0x8f, 0xfd, 0xea,
+ 0x6, 0xcd, 0x1, 0xda, 0xff, 0xff, 0xff, 0x4a, 0xff, 0x59, 0xff, 0x31, 0x3,
+ 0x4c, 0xff, 0x57, 0x1, 0x3d, 0xfb, 0xfe, 0x6, 0xc8, 0x0, 0xe9, 0xfe, 0x72,
+ 0x3, 0xbf, 0x2, 0x89, 0x6, 0x2e, 0x6, 0x14, 0x0, 0x66, 0x2, 0xa0, 0xfe,
+ 0x68, 0xff, 0xae, 0xfe, 0x28, 0x2, 0x27, 0xfd, 0xc7, 0xff, 0x51, 0xf7,
+ 0x46, 0xfc, 0x12, 0x0, 0xd8, 0x1, 0x8a, 0xfd, 0xa4, 0x2, 0xa0, 0xfe, 0xc5,
+ 0x1, 0x32, 0xfb, 0x14, 0x1, 0x4b, 0x0, 0xe1, 0x0, 0xd, 0x0, 0x97, 0x0,
+ 0xf0, 0xfd, 0xfa, 0x0, 0x4f, 0xfc, 0xc2, 0xfa, 0xf8, 0x1, 0x26, 0x2, 0x6b,
+ 0xfd, 0x47, 0xff, 0x71, 0x3, 0x13, 0xfd, 0x8a, 0xfa, 0x66, 0x1, 0x76, 0x0,
+ 0x59, 0xfe, 0x79, 0xff, 0x90, 0xfe, 0x60, 0x1, 0x77, 0xfc, 0x71, 0xf8,
+ 0xcb, 0xfc, 0x53, 0xff, 0xb7, 0xfe, 0x8a, 0xfc, 0x8e, 0xfb, 0x29, 0x2,
+ 0x3d, 0xfb, 0xb, 0xfc, 0x91, 0xfe, 0xe0, 0xfe, 0xb2, 0xfc, 0xe0, 0xfe,
+ 0x54, 0xff, 0x88, 0xff, 0x82, 0x1, 0x1f, 0xf9, 0xdc, 0xff, 0x22, 0x2,
+ 0x73, 0xfc, 0xca, 0x1, 0xff, 0xff, 0xaa, 0xfb, 0x69, 0xfd, 0x20, 0xfd,
+ 0xf4, 0xfe, 0xbc, 0xfd, 0xc4, 0xfd, 0x22, 0x0, 0x48, 0xfe, 0x51, 0x1,
+ 0xe1, 0x0, 0x61, 0xf9, 0xf0, 0x1, 0x27, 0xfc, 0x82, 0xfe, 0x54, 0x3, 0xe6,
+ 0xff, 0xce, 0xfe, 0x52, 0xfd, 0x15, 0x0, 0xf9, 0xff, 0x94, 0xfd, 0x4f,
+ 0xfe, 0xbb, 0x0, 0xe0, 0xff, 0x2a, 0x0, 0x44, 0x0, 0x72, 0xf9, 0x1f, 0x3,
+ 0x69, 0xfd, 0xc1, 0x1, 0x4a, 0x0, 0x97, 0x2, 0x81, 0x0, 0xeb, 0x3, 0xb3,
+ 0xff, 0x7b, 0xff, 0xf2, 0xfe, 0xcb, 0xff, 0xb5, 0xff, 0x3d, 0x1, 0x14,
+ 0x1, 0x33, 0x1, 0x62, 0xfc, 0x82, 0x2, 0x3, 0x1, 0x10, 0x0, 0x9e, 0xff,
+ 0xad, 0x2, 0x23, 0x2, 0xd6, 0x1, 0xdd, 0xfe, 0xc2, 0x1, 0xc6, 0xfe, 0x85,
+ 0x0, 0xe, 0xff, 0x9a, 0xff, 0xee, 0xff, 0x4c, 0xfd, 0x77, 0xfa, 0x1, 0xfd,
+ 0xcc, 0x0, 0x52, 0x1, 0x24, 0xfd, 0x5, 0x0, 0x24, 0x0, 0x6e, 0xfe, 0xc5,
+ 0xfd, 0x4c, 0x0, 0x92, 0xfe, 0xd6, 0xff, 0xbb, 0xfe, 0xe, 0x0, 0x76, 0x0,
+ 0x20, 0xff, 0x57, 0xfe, 0xdd, 0xfc, 0xbe, 0x2, 0xc4, 0x1, 0xed, 0xfe,
+ 0xd0, 0x0, 0x47, 0x1, 0xfe, 0xfd, 0x98, 0xfd, 0x4c, 0x0, 0xbf, 0xff, 0xb,
+ 0xff, 0x20, 0xff, 0xe3, 0xfd, 0xf2, 0x1, 0xf7, 0xfb, 0xb, 0xfd, 0xca,
+ 0xfd, 0x44, 0xff, 0xb7, 0xfe, 0xc0, 0xff, 0xdf, 0xff, 0xfd, 0xfd, 0x97,
+ 0xfc, 0xa1, 0xfe, 0xb1, 0xfe, 0xd5, 0xfd, 0x84, 0xfe, 0xc4, 0x0, 0x66,
+ 0xfe, 0xef, 0xfe, 0x2a, 0xff, 0x98, 0xfe, 0x43, 0x0, 0xb2, 0x0, 0xbd,
+ 0xfe, 0x17, 0x2, 0x86, 0x0, 0x65, 0xfe, 0x17, 0x0, 0xb4, 0xff, 0xa0, 0xfe,
+ 0xf5, 0xfe, 0xf8, 0xfe, 0x5a, 0x1, 0x56, 0xfe, 0x7f, 0x0, 0x1b, 0xfe,
+ 0xc4, 0xfc, 0xf6, 0x2, 0x94, 0xfc, 0x14, 0xff, 0x5, 0x1, 0x45, 0x0, 0x26,
+ 0x1, 0x59, 0xfe, 0x56, 0x1, 0x82, 0xff, 0xd5, 0xff, 0x7c, 0xff, 0xb4,
+ 0xff, 0x6f, 0x0, 0x48, 0xff, 0xfb, 0xfd, 0x42, 0xfc, 0xaf, 0x1, 0x6f,
+ 0xfe, 0x42, 0x0, 0x36, 0x0, 0x2d, 0x1, 0x41, 0x0, 0x5c, 0x1, 0x30, 0x1,
+ 0x27, 0x0, 0x45, 0x0, 0x57, 0x0, 0x55, 0xff, 0x60, 0x0, 0xcf, 0xff, 0xdf,
+ 0xff, 0xda, 0xff, 0xa5, 0x0, 0x28, 0x1, 0x9d, 0x1, 0x88, 0xff, 0x4e, 0x0,
+ 0x3f, 0x2, 0x1d, 0x1, 0x9a, 0x0, 0x67, 0x1, 0xad, 0x0, 0xb4, 0xff, 0x28,
+ 0xff, 0xaa, 0xff, 0x70, 0xff, 0x3f, 0xfd, 0x0, 0xfe, 0x20, 0xfe, 0xcc,
+ 0xff, 0xd1, 0x1, 0xb6, 0xfd, 0xa, 0x0, 0x48, 0x0, 0x44, 0xfe, 0x9d, 0xff,
+ 0x8, 0x0, 0x22, 0x0, 0x8a, 0xff, 0x8f, 0xff, 0xfa, 0x0, 0x72, 0xff, 0x91,
+ 0xff, 0x92, 0xfe, 0x91, 0xfc, 0x86, 0x2, 0xe1, 0x0, 0xb0, 0xff, 0xca,
+ 0xff, 0x35, 0x0, 0x18, 0xfe, 0x1a, 0x0, 0xbf, 0x0, 0x3e, 0x1, 0xde, 0xff,
+ 0x24, 0x0, 0xb6, 0xfe, 0x32, 0x1, 0x8d, 0xfd, 0xba, 0xfc, 0xd5, 0xfe,
+ 0xc2, 0xfe, 0xbc, 0xff, 0x9c, 0xff, 0x26, 0xfd, 0xbd, 0xfe, 0xa2, 0xfa,
+ 0x6a, 0x1, 0x68, 0xff, 0x6, 0x0, 0x32, 0xff, 0x66, 0x0, 0x1e, 0x0, 0x3b,
+ 0xff, 0xfd, 0xfe, 0xac, 0xfd, 0x71, 0x1, 0x40, 0xff, 0x71, 0xff, 0x4e,
+ 0x2, 0xf2, 0xfe, 0x97, 0xff, 0x78, 0xfd, 0x9, 0x2, 0x61, 0xff, 0x75, 0x0,
+ 0x94, 0xff, 0xae, 0x0, 0xa0, 0xff, 0xa0, 0x0, 0xba, 0xfe, 0xd0, 0xfc,
+ 0x7a, 0x1, 0x96, 0xfe, 0xd, 0xff, 0x5b, 0x1, 0x5c, 0xff, 0x47, 0x0, 0x96,
+ 0xfe, 0x5b, 0x2, 0xc5, 0xff, 0xae, 0x0, 0xab, 0xff, 0xd5, 0xff, 0x6, 0x0,
+ 0xa1, 0xff, 0x9c, 0xfe, 0xc1, 0xfc, 0xd5, 0x0, 0x8, 0x0, 0xa5, 0xff, 0x32,
+ 0x0, 0x53, 0x0, 0x41, 0x0, 0x97, 0xfe, 0x11, 0x2, 0xad, 0xff, 0x27, 0x1,
+ 0x13, 0x0, 0x9b, 0xff, 0x94, 0x0, 0xd3, 0xff, 0xfb, 0xfe, 0x58, 0xfd,
+ 0xcf, 0xff, 0xc9, 0x1, 0xfd, 0xff, 0xe1, 0xff, 0x4f, 0x0, 0xe9, 0x1, 0x98,
+ 0xfe, 0x6c, 0x1, 0xa9, 0x0, 0x84, 0x1, 0xf1, 0xff, 0xf5, 0xfe, 0x0, 0x0,
+ 0x82, 0x0, 0x55, 0xfe, 0xc3, 0xfd, 0x3b, 0xfe, 0xd9, 0x0, 0x2a, 0x0, 0x1a,
+ 0xfe, 0x39, 0xff, 0xce, 0xff, 0xe4, 0xfd, 0x17, 0x0, 0xfd, 0xff, 0xe9,
+ 0x0, 0xc5, 0xff, 0xa0, 0xff, 0xbc, 0x0, 0x6, 0x0, 0x82, 0xff, 0x75, 0xff,
+ 0x67, 0xfe, 0xd3, 0x1, 0xd5, 0xff, 0xe, 0x0, 0x5a, 0xff, 0x4d, 0x0, 0x96,
+ 0xfe, 0x1, 0x0, 0x41, 0x0, 0xf7, 0x0, 0x10, 0x0, 0x28, 0x0, 0x90, 0xff,
+ 0x72, 0x0, 0x73, 0xfd, 0xc4, 0xfd, 0x72, 0xff, 0xc3, 0xff, 0x9d, 0xff,
+ 0x77, 0x0, 0xe7, 0xfe, 0xe6, 0xfe, 0x6f, 0xfe, 0xe, 0x0, 0x56, 0xff, 0xab,
+ 0xff, 0xad, 0xff, 0xfe, 0xff, 0x47, 0x0, 0x91, 0xff, 0xc9, 0xfe, 0x7b,
+ 0xff, 0x3e, 0x0, 0x8b, 0x0, 0x6, 0x0, 0x31, 0x1, 0xcf, 0xff, 0x93, 0xff,
+ 0xe2, 0xff, 0xfd, 0xff, 0x29, 0xff, 0x22, 0x0, 0x9e, 0xff, 0x3b, 0x0, 0x1,
+ 0x0, 0xe0, 0xff, 0x18, 0xfe, 0x52, 0xfe, 0xb7, 0x0, 0x7b, 0xff, 0x9e,
+ 0xff, 0x49, 0x0, 0xe3, 0xff, 0x41, 0x0, 0x1b, 0x0, 0xe, 0x0, 0x47, 0xff,
+ 0x5, 0x0, 0x87, 0xff, 0x81, 0xff, 0x44, 0x0, 0x2b, 0xff, 0xc6, 0xfe, 0x20,
+ 0xff, 0x6e, 0x0, 0xe2, 0xff, 0xfc, 0xff, 0x21, 0x0, 0xa6, 0x0, 0x43, 0x0,
+ 0x42, 0x0, 0xab, 0xff, 0xf0, 0xff, 0x64, 0x0, 0xba, 0xff, 0x89, 0xff,
+ 0x35, 0x0, 0x37, 0x0, 0x2f, 0xff, 0xd8, 0xff, 0x5a, 0xff, 0x3e, 0x0, 0x70,
+ 0x0, 0x81, 0xff, 0xc3, 0xff, 0x36, 0x1, 0xb2, 0xff, 0x7c, 0xff, 0x3f, 0x0,
+ 0x86, 0x0, 0xc2, 0xff, 0x6a, 0xff, 0xed, 0xff, 0x14, 0x0, 0x76, 0xfe,
+ 0xd4, 0xfe, 0xee, 0xfe, 0x22, 0x0, 0x8e, 0x0, 0x12, 0xff, 0xe6, 0xff,
+ 0xab, 0xff, 0xf2, 0xff, 0x3e, 0xff, 0xde, 0xff, 0x2d, 0x0, 0xe0, 0xff,
+ 0xf3, 0xff, 0x25, 0x0, 0x24, 0x0, 0x9a, 0xfe, 0xea, 0xff, 0x36, 0xff,
+ 0x77, 0x0, 0x8c, 0x0, 0xb0, 0x0, 0x36, 0x0, 0x4f, 0x0, 0xb4, 0xff, 0x4e,
+ 0xff, 0x34, 0x0, 0xd, 0x0, 0xaf, 0xff, 0x1f, 0x0, 0xc4, 0xff, 0xcf, 0x0,
+ 0xae, 0xfd, 0x9d, 0xff, 0xa3, 0xff, 0x6d, 0xff, 0x93, 0x0, 0xe9, 0xff,
+ 0x29, 0xff, 0x5b, 0xff, 0x3f, 0xff, 0x6e, 0xff, 0x69, 0xff, 0x77, 0xff,
+ 0xb2, 0xff, 0x42, 0x0, 0x3f, 0x0, 0xe1, 0xff, 0xb2, 0xfe, 0x5c, 0x0, 0x92,
+ 0x0, 0x4e, 0x0, 0xd, 0x0, 0xc2, 0x0, 0x59, 0x0, 0x4, 0x0, 0x9a, 0x0, 0xd9,
+ 0xff, 0xb, 0x0, 0x1, 0x0, 0xef, 0xff, 0x59, 0x0, 0xc6, 0xff, 0x73, 0x0,
+ 0xea, 0xfd, 0x43, 0xff, 0xd5, 0x0, 0x22, 0xff, 0xb9, 0xff, 0x65, 0x0,
+ 0x1d, 0x0, 0x87, 0x0, 0x5b, 0x0, 0xf8, 0xff, 0xfd, 0xff, 0x59, 0xff, 0xb4,
+ 0xff, 0xcb, 0xff, 0x6b, 0x0, 0x35, 0x0, 0x37, 0xff, 0xe9, 0xff, 0x25, 0x0,
+ 0xa1, 0x0, 0x92, 0x0, 0x2, 0x0, 0x34, 0x0, 0xd, 0x0, 0x9c, 0x0, 0x0, 0x0,
+ 0xe, 0x0, 0xd8, 0xff, 0x34, 0x0, 0x28, 0x0, 0x53, 0x0, 0x87, 0x0, 0xcc,
+ 0xfe, 0x71, 0xff, 0xc4, 0xff, 0x60, 0x0, 0x3, 0x0, 0xf5, 0xff, 0x20, 0x0,
+ 0x37, 0x0, 0xfc, 0xff, 0xe2, 0xff, 0x5b, 0x0, 0xc7, 0xff, 0xf6, 0xff,
+ 0xc9, 0xff, 0x2c, 0x0, 0x2f, 0x0, 0x9e, 0xfe, 0xdb, 0xff, 0xae, 0xff, 0x7,
+ 0x0, 0xdf, 0xff, 0x8e, 0xff, 0x1, 0x0, 0xce, 0xff, 0x23, 0x0, 0x8b, 0xff,
+ 0x1a, 0x0, 0x7b, 0xff, 0xdf, 0xff, 0xe, 0x0, 0x22, 0x0, 0x4c, 0x0, 0xe4,
+ 0xfe, 0x7c, 0x0, 0x8f, 0xff, 0x99, 0x0, 0x5a, 0x0, 0x34, 0x0, 0xee, 0xff,
+ 0xef, 0xff, 0x39, 0x0, 0xc3, 0xff, 0x3, 0x0, 0xac, 0xff, 0x39, 0x0, 0x35,
+ 0x0, 0xb2, 0xff, 0x13, 0x0, 0xfe, 0xfd, 0x35, 0xff, 0xef, 0xff, 0x6f,
+ 0xff, 0xfe, 0xff, 0x44, 0x0, 0xe6, 0xff, 0x66, 0xff, 0x2a, 0x0, 0xcc,
+ 0xff, 0xce, 0xff, 0x7a, 0xff, 0x2d, 0x0, 0x11, 0x0, 0xc, 0x0, 0xe0, 0xff,
+ 0xf2, 0xfe, 0x48, 0x0, 0x4c, 0x0, 0xce, 0xff, 0x1d, 0x0, 0x7e, 0x0, 0x3c,
+ 0x0, 0x20, 0x0, 0xd3, 0x0, 0xf4, 0xff, 0x3f, 0x0, 0xdd, 0xff, 0x32, 0x0,
+ 0xb, 0x0, 0x17, 0x0, 0x58, 0x0, 0x81, 0xfe, 0xc0, 0xff, 0x43, 0x0, 0x64,
+ 0xff, 0x3c, 0x0, 0x4, 0x0, 0xd0, 0xff, 0xc4, 0xff, 0x90, 0x0, 0x1a, 0x0,
+ 0xc9, 0xff, 0x95, 0xff, 0x1f, 0x0, 0xe7, 0xff, 0x6f, 0x0, 0x92, 0xff,
+ 0x15, 0xff, 0x35, 0x0, 0xdf, 0xff, 0x6f, 0x0, 0xa, 0x0, 0x7d, 0x0, 0x48,
+ 0x0, 0x62, 0x0, 0xe1, 0x0, 0x22, 0x0, 0x20, 0x0, 0xf9, 0xff, 0x36, 0x0,
+ 0x2b, 0x0, 0xed, 0xff, 0x55, 0x0, 0x94, 0xfe, 0x4e, 0x0, 0x7b, 0xff, 0xe8,
+ 0xff, 0xfa, 0xff, 0x20, 0x0, 0xd6, 0xff, 0x4b, 0x0, 0x2c, 0x0, 0xb, 0x0,
+ 0xf8, 0xff, 0xab, 0xff, 0x23, 0x0, 0x9, 0x0, 0xf6, 0xff, 0x12, 0x0, 0xe9,
+ 0xfe, 0x87, 0x0, 0xaa, 0xff, 0x15, 0x0, 0x11, 0x0, 0xf4, 0xff, 0x1b, 0x0,
+ 0x57, 0xff, 0x5d, 0x0, 0xfe, 0xff, 0xdc, 0xff, 0xef, 0xff, 0x41, 0x0,
+ 0x47, 0x0, 0xed, 0xff, 0x7, 0x0, 0x9b, 0xfe, 0x5d, 0x0, 0xec, 0xff, 0x17,
+ 0x0, 0xe6, 0xff, 0x71, 0x0, 0x31, 0x0, 0xd7, 0xff, 0x4d, 0x0, 0xdb, 0xff,
+ 0x44, 0x0, 0xea, 0xff, 0x18, 0x0, 0x23, 0x0, 0xe1, 0xff, 0x24, 0x0, 0x54,
+ 0xfe, 0x32, 0x0, 0xef, 0xff, 0xba, 0xff, 0x20, 0x0, 0x4, 0x0, 0xb2, 0xff,
+ 0x84, 0xff, 0x2d, 0x0, 0xc9, 0xff, 0xf0, 0xff, 0xbe, 0xff, 0x2e, 0x0,
+ 0x59, 0x0, 0xf3, 0xff, 0xe4, 0xff, 0xfe, 0xfe, 0x93, 0x0, 0xe0, 0xff,
+ 0x65, 0x0, 0x7, 0x0, 0x65, 0x0, 0x13, 0x0, 0xfa, 0xff, 0x92, 0x0, 0xd,
+ 0x0, 0x29, 0x0, 0x35, 0x0, 0x54, 0x0, 0x77, 0x0, 0xa1, 0xff, 0x8, 0x0,
+ 0x5b, 0xfe, 0xe8, 0xff, 0x12, 0x0, 0x95, 0xff, 0xb7, 0xff, 0x3d, 0x0, 0x2,
+ 0x0, 0xc, 0x0, 0x67, 0x0, 0xe0, 0xff, 0x15, 0x0, 0xc8, 0xff, 0x0, 0x0,
+ 0x1f, 0x0, 0x9, 0x0, 0xf2, 0xff, 0x21, 0xff, 0xc5, 0x0, 0x1, 0x0, 0x4c,
+ 0x0, 0x2e, 0x0, 0xa, 0x0, 0xb, 0x0, 0x1f, 0x0, 0x8c, 0x0, 0xfb, 0xff,
+ 0x2f, 0x0, 0x8, 0x0, 0x21, 0x0, 0x20, 0x0, 0xfa, 0xff, 0x2e, 0x0, 0xc0,
+ 0xfe, 0x49, 0x0, 0xcc, 0xff, 0xee, 0xff, 0x23, 0x0, 0xc3, 0xff, 0x19, 0x0,
+ 0x8d, 0xff, 0x3f, 0x0, 0x3, 0x0, 0x1b, 0x0, 0xd6, 0xff, 0x1c, 0x0, 0xf8,
+ 0xff, 0x10, 0x0, 0xc9, 0xff, 0xf4, 0xfe, 0x8b, 0x0, 0xfb, 0xff, 0xe3,
+ 0xff, 0xfa, 0xff, 0xfd, 0xff, 0x56, 0x0, 0xc2, 0xff, 0x81, 0x0, 0xe9,
+ 0xff, 0x3b, 0x0, 0xf7, 0xff, 0x8, 0x0, 0xe4, 0xff, 0xf3, 0xff, 0x42, 0x0,
+ 0xbd, 0xfe, 0xa4, 0x0, 0xe0, 0xff, 0xf6, 0xff, 0x16, 0x0, 0xed, 0xff,
+ 0xdb, 0xff, 0xe7, 0xff, 0xd, 0x0, 0xc, 0x0, 0xed, 0xff, 0xf5, 0xff, 0x24,
+ 0x0, 0xee, 0xff, 0xe5, 0xff, 0xe6, 0xff, 0xb4, 0xfe, 0x24, 0x0, 0xe2,
+ 0xff, 0xf8, 0xff, 0xf5, 0xff, 0xe0, 0xff, 0xf1, 0xff, 0xa3, 0xff, 0x10,
+ 0x0, 0xa, 0x0, 0xde, 0xff, 0x7, 0x0, 0x3d, 0x0, 0x41, 0x0, 0xd2, 0xff,
+ 0xdf, 0xff, 0xcf, 0xfe, 0x54, 0x0, 0x22, 0x0, 0xfe, 0xff, 0xec, 0xff,
+ 0x3b, 0x0, 0x3, 0x0, 0x65, 0x0, 0x36, 0x0, 0x20, 0x0, 0x20, 0x0, 0x1e,
+ 0x0, 0x17, 0x0, 0xf9, 0xff, 0xdc, 0xff, 0x29, 0x0, 0xb0, 0xfe, 0x53, 0x0,
+ 0x1a, 0x0, 0xc5, 0xff, 0x2d, 0x0, 0xba, 0xff, 0xcb, 0xff, 0xc9, 0xff, 0xe,
+ 0x0, 0x28, 0x0, 0xc1, 0xff, 0xf3, 0xff, 0x16, 0x0, 0xe9, 0xff, 0x16, 0x0,
+ 0xb8, 0xff, 0x2d, 0xff, 0x75, 0x0, 0xfa, 0xff, 0x3e, 0x0, 0x11, 0x0, 0x7,
+ 0x0, 0x36, 0x0, 0x1e, 0x0, 0x48, 0x0, 0x1e, 0x0, 0x31, 0x0, 0x2d, 0x0,
+ 0x1a, 0x0, 0xe5, 0xff, 0xec, 0xff, 0xf, 0x0, 0x85, 0xfe, 0x14, 0x0, 0xe7,
+ 0xff, 0xbc, 0xff, 0xf7, 0xff, 0x5, 0x0, 0xf8, 0xff, 0x56, 0x0, 0xe2, 0xff,
+ 0xf7, 0xff, 0x10, 0x0, 0xd6, 0xff, 0x0, 0x0, 0xc5, 0xff, 0x16, 0x0, 0xed,
+ 0xff, 0xf, 0xff, 0xa7, 0x0, 0xd0, 0xff, 0x43, 0x0, 0x2f, 0x0, 0xdb, 0xff,
+ 0xf8, 0xff, 0x37, 0x0, 0xe7, 0xff, 0xfa, 0xff, 0xed, 0xff, 0x1c, 0x0,
+ 0x34, 0x0, 0xf, 0x0, 0xed, 0xff, 0xee, 0xff, 0xbe, 0xfe, 0x3a, 0x0, 0xea,
+ 0xff, 0xed, 0xff, 0xf2, 0xff, 0x12, 0x0, 0xe2, 0xff, 0x51, 0x0, 0xdc,
+ 0xff, 0xe9, 0xff, 0x3, 0x0, 0xfb, 0xff, 0x14, 0x0, 0xe4, 0xff, 0xf0, 0xff,
+ 0xd8, 0xff, 0xcf, 0xfe, 0x5a, 0x0, 0x39, 0x0, 0xd3, 0xff, 0x13, 0x0, 0x1a,
+ 0x0, 0xd2, 0xff, 0x49, 0x0, 0x10, 0x0, 0xe7, 0xff, 0xf0, 0xff, 0x3, 0x0,
+ 0x5, 0x0, 0xeb, 0xff, 0xe7, 0xff, 0x6, 0x0, 0xb, 0xff, 0x7f, 0x0, 0x35,
+ 0x0, 0x22, 0x0, 0x34, 0x0, 0x24, 0x0, 0xd8, 0xff, 0x27, 0x0, 0x8, 0x0,
+ 0x14, 0x0, 0xe2, 0xff, 0x3b, 0x0, 0x25, 0x0, 0xe8, 0xff, 0xe7, 0xff, 0xd8,
+ 0xff, 0xd8, 0xfe, 0x23, 0x0, 0x3f, 0x0, 0xf3, 0xff, 0xe9, 0xff, 0x3a, 0x0,
+ 0x1c, 0x0, 0x1, 0x0, 0x2a, 0x0, 0xec, 0xff, 0xf7, 0xff, 0x17, 0x0, 0x17,
+ 0x0, 0xe7, 0xff, 0xf6, 0xff, 0xf3, 0xff, 0x21, 0xff, 0xae, 0x0, 0x3e, 0x0,
+ 0x23, 0x0, 0x2, 0x0, 0x1f, 0x0, 0xc, 0x0, 0x58, 0x0, 0x23, 0x0, 0xdc,
+ 0xff, 0x9, 0x0, 0x7, 0x0, 0x1c, 0x0, 0xd1, 0xff, 0xfe, 0xff, 0x24, 0x0,
+ 0xf3, 0xfe, 0x64, 0x0, 0xf0, 0xff, 0x7, 0x0, 0x12, 0x0, 0xd9, 0xff, 0xd8,
+ 0xff, 0xe1, 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xcf, 0xff, 0xe5, 0xff, 0x40,
+ 0x0, 0xf0, 0xff, 0x13, 0x0, 0xb9, 0xff, 0x1c, 0xff, 0x44, 0x0, 0x6, 0x0,
+ 0x20, 0x0, 0xde, 0xff, 0x35, 0x0, 0xc, 0x0, 0x4c, 0x0, 0x17, 0x0, 0xd0,
+ 0xff, 0x17, 0x0, 0x7, 0x0, 0x27, 0x0, 0x8, 0x0, 0xe6, 0xff, 0x1a, 0x0,
+ 0xcb, 0xfe, 0x31, 0x0, 0xc, 0x0, 0xb7, 0xff, 0xf8, 0xff, 0xfa, 0xff, 0x98,
+ 0xff, 0x36, 0x0, 0xda, 0xff, 0xda, 0xff, 0xcf, 0xff, 0xd3, 0xff, 0x9, 0x0,
+ 0xed, 0xff, 0xc, 0x0, 0xd9, 0xff, 0x1e, 0xff, 0x4e, 0x0, 0xf6, 0xff, 0x1c,
+ 0x0, 0xe, 0x0, 0xf7, 0xff, 0xdf, 0xff, 0xdc, 0xff, 0xd, 0x0, 0xe6, 0xff,
+ 0xd0, 0xff, 0x1e, 0x0, 0x27, 0x0, 0x28, 0x0, 0xea, 0xff, 0xdc, 0xff, 0xec,
+ 0xfe, 0xa, 0x0, 0x37, 0x0, 0xdf, 0xff, 0xc0, 0xff, 0x37, 0x0, 0x14, 0x0,
+ 0x36, 0x0, 0x1, 0x0, 0xd6, 0xff, 0xfd, 0xff, 0xff, 0xff, 0x7, 0x0, 0xf1,
+ 0xff, 0xa, 0x0, 0x5, 0x0, 0xc, 0xff, 0x30, 0x0, 0x46, 0x0, 0xcd, 0xff,
+ 0xe3, 0xff, 0xcf, 0xff, 0xef, 0xff, 0xfa, 0xff, 0xf4, 0xff, 0xe6, 0xff,
+ 0xc9, 0xff, 0xde, 0xff, 0x21, 0x0, 0xf3, 0xff, 0x2b, 0x0, 0xde, 0xff,
+ 0x42, 0xff, 0x37, 0x0, 0xf, 0x0, 0x20, 0x0, 0xf4, 0xff, 0xe5, 0xff, 0x2,
+ 0x0, 0x8, 0x0, 0x0, 0x0, 0xf0, 0xff, 0xfb, 0xff, 0x2, 0x0, 0x1b, 0x0, 0x8,
+ 0x0, 0xb, 0x0, 0xdc, 0xff, 0xe8, 0xfe, 0xe6, 0xff, 0x2, 0x0, 0xdf, 0xff,
+ 0xd1, 0xff, 0xff, 0xff, 0xd4, 0xff, 0x28, 0x0, 0x19, 0x0, 0xdd, 0xff,
+ 0xff, 0xff, 0xd1, 0xff, 0xe7, 0xff, 0xf9, 0xff, 0x15, 0x0, 0x0, 0x0, 0x51,
+ 0xff, 0x41, 0x0, 0x19, 0x0, 0x43, 0x0, 0x13, 0x0, 0xdb, 0xff, 0xca, 0xff,
+ 0x36, 0x0, 0x47, 0x0, 0x6, 0x0, 0xeb, 0xff, 0xff, 0xff, 0xe, 0x0, 0x3,
+ 0x0, 0xe, 0x0, 0x1f, 0x0, 0x2d, 0xff, 0xfa, 0xff, 0x21, 0x0, 0x23, 0x0,
+ 0xe, 0x0, 0xf3, 0xff, 0xde, 0xff, 0xf6, 0xff, 0x4b, 0x0, 0x6, 0x0, 0xf1,
+ 0xff, 0xfb, 0xff, 0xe, 0x0, 0xf0, 0xff, 0x1d, 0x0, 0x8, 0x0, 0x45, 0xff,
+ 0x2e, 0x0, 0x3e, 0x0, 0x2a, 0x0, 0x1, 0x0, 0x19, 0x0, 0xf6, 0xff, 0x28,
+ 0x0, 0x7f, 0x0, 0xfe, 0xff, 0x21, 0x0, 0x2, 0x0, 0xff, 0xff, 0xcc, 0xff,
+ 0xc, 0x0, 0x5e, 0x0, 0x44, 0xff, 0x47, 0x0, 0x25, 0x0, 0x1e, 0x0, 0x34,
+ 0x0, 0xe1, 0xff, 0xc6, 0xff, 0xfd, 0xff, 0x34, 0x0, 0x12, 0x0, 0xf7, 0xff,
+ 0xfb, 0xff, 0x1a, 0x0, 0xcd, 0xff, 0x1a, 0x0, 0x21, 0x0, 0x53, 0xff, 0x11,
+ 0x0, 0xfc, 0xff, 0x2a, 0x0, 0x11, 0x0, 0xf9, 0xff, 0xed, 0xff, 0xe7, 0xff,
+ 0x48, 0x0, 0x4, 0x0, 0xd, 0x0, 0x16, 0x0, 0x23, 0x0, 0xfd, 0xff, 0xf2,
+ 0xff, 0x33, 0x0, 0x37, 0xff, 0x13, 0x0, 0x18, 0x0, 0xfa, 0xff, 0xf4, 0xff,
+ 0x2, 0x0, 0xe7, 0xff, 0x35, 0x0, 0x37, 0x0, 0xfd, 0xff, 0x10, 0x0, 0xf2,
+ 0xff, 0x10, 0x0, 0xd8, 0xff, 0xf3, 0xff, 0x49, 0x0, 0x4c, 0xff, 0x35, 0x0,
+ 0xe, 0x0, 0x4, 0x0, 0x1c, 0x0, 0xdc, 0xff, 0xeb, 0xff, 0xe0, 0xff, 0x2a,
+ 0x0, 0xfd, 0xff, 0xda, 0xff, 0xfc, 0xff, 0x33, 0x0, 0x2, 0x0, 0x4, 0x0,
+ 0xa, 0x0, 0x6d, 0xff, 0x1d, 0x0, 0x16, 0x0, 0x22, 0x0, 0xf8, 0xff, 0x1b,
+ 0x0, 0x2e, 0x0, 0xe, 0x0, 0x5b, 0x0, 0xf0, 0xff, 0x1b, 0x0, 0x19, 0x0,
+ 0x2d, 0x0, 0xb, 0x0, 0xf5, 0xff, 0x2f, 0x0, 0x2e, 0xff, 0x8, 0x0, 0x1c,
+ 0x0, 0xc4, 0xff, 0xf5, 0xff, 0xf7, 0xff, 0xf9, 0xff, 0xb, 0x0, 0x25, 0x0,
+ 0xea, 0xff, 0x1, 0x0, 0xdc, 0xff, 0xe, 0x0, 0xed, 0xff, 0x1b, 0x0, 0x9,
+ 0x0, 0x68, 0xff, 0x45, 0x0, 0xed, 0xff, 0xe, 0x0, 0x18, 0x0, 0xec, 0xff,
+ 0x7, 0x0, 0xef, 0xff, 0x16, 0x0, 0xf5, 0xff, 0xf4, 0xff, 0x18, 0x0, 0x26,
+ 0x0, 0x19, 0x0, 0xf6, 0xff, 0xec, 0xff, 0x26, 0xff, 0xf5, 0xff, 0xe2,
+ 0xff, 0xe4, 0xff, 0xeb, 0xff, 0x10, 0x0, 0x12, 0x0, 0xf, 0x0, 0x10, 0x0,
+ 0xe3, 0xff, 0x4, 0x0, 0xfe, 0xff, 0x5, 0x0, 0xa, 0x0, 0xed, 0xff, 0xf3,
+ 0xff, 0x41, 0xff, 0x2d, 0x0, 0xf6, 0xff, 0xf2, 0xff, 0x0, 0x0, 0xfc, 0xff,
+ 0x6, 0x0, 0x2a, 0x0, 0x24, 0x0, 0xeb, 0xff, 0xf2, 0xff, 0x3, 0x0, 0x0,
+ 0x0, 0xe, 0x0, 0x2, 0x0, 0x10, 0x0, 0x50, 0xff, 0x2b, 0x0, 0xf4, 0xff,
+ 0x6, 0x0, 0x12, 0x0, 0xf7, 0xff, 0x25, 0x0, 0xf7, 0xff, 0x3, 0x0, 0xf8,
+ 0xff, 0xe6, 0xff, 0x18, 0x0, 0x22, 0x0, 0x1a, 0x0, 0x0, 0x0, 0xe6, 0xff,
+ 0x40, 0xff, 0xb, 0x0, 0xf4, 0xff, 0xee, 0xff, 0xea, 0xff, 0x16, 0x0, 0x32,
+ 0x0, 0xfb, 0xff, 0x11, 0x0, 0xe5, 0xff, 0x9, 0x0, 0x0, 0x0, 0x9, 0x0, 0xf,
+ 0x0, 0xf9, 0xff, 0x5, 0x0, 0x55, 0xff, 0x3e, 0x0, 0xfc, 0xff, 0xee, 0xff,
+ 0xc, 0x0, 0x9, 0x0, 0x5, 0x0, 0x1b, 0x0, 0xd, 0x0, 0xf4, 0xff, 0x0, 0x0,
+ 0x3, 0x0, 0x2, 0x0, 0x0, 0x0, 0xb, 0x0, 0x5, 0x0, 0x5b, 0xff, 0x13, 0x0,
+ 0xea, 0xff, 0xed, 0xff, 0x18, 0x0, 0xfd, 0xff, 0x16, 0x0, 0xce, 0xff,
+ 0xfa, 0xff, 0xf7, 0xff, 0xef, 0xff, 0xa, 0x0, 0x1d, 0x0, 0x13, 0x0, 0x1,
+ 0x0, 0xe3, 0xff, 0x61, 0xff, 0x5, 0x0, 0xfc, 0xff, 0x2, 0x0, 0xec, 0xff,
+ 0x21, 0x0, 0x30, 0x0, 0x1c, 0x0, 0x1f, 0x0, 0xed, 0xff, 0x18, 0x0, 0xf,
+ 0x0, 0x5, 0x0, 0x5, 0x0, 0xf9, 0xff, 0x25, 0x0, 0x4a, 0xff, 0x1b, 0x0,
+ 0x14, 0x0, 0xe2, 0xff, 0x10, 0x0, 0xf1, 0xff, 0x8, 0x0, 0x14, 0x0, 0xf,
+ 0x0, 0xf8, 0xff, 0xec, 0xff, 0xf8, 0xff, 0x2, 0x0, 0x8, 0x0, 0x15, 0x0,
+ 0xf5, 0xff, 0x7e, 0xff, 0x1e, 0x0, 0xfc, 0xff, 0x15, 0x0, 0x19, 0x0, 0xf7,
+ 0xff, 0x2c, 0x0, 0xe9, 0xff, 0x23, 0x0, 0xf9, 0xff, 0x7, 0x0, 0x13, 0x0,
+ 0xb, 0x0, 0x23, 0x0, 0xf6, 0xff, 0xfe, 0xff, 0x59, 0xff, 0x2, 0x0, 0x3,
+ 0x0, 0xe3, 0xff, 0xfc, 0xff, 0xa, 0x0, 0x17, 0x0, 0x8, 0x0, 0x20, 0x0,
+ 0xf7, 0xff, 0x1b, 0x0, 0xf0, 0xff, 0xef, 0xff, 0xff, 0xff, 0x2, 0x0, 0x11,
+ 0x0, 0x77, 0xff, 0x3c, 0x0, 0x8, 0x0, 0xf6, 0xff, 0x1f, 0x0, 0xe5, 0xff,
+ 0x0, 0x0, 0xf1, 0xff, 0x19, 0x0, 0x1, 0x0, 0xf5, 0xff, 0xfc, 0xff, 0x0,
+ 0x0, 0xf, 0x0, 0x1, 0x0, 0xf3, 0xff, 0x7d, 0xff, 0x11, 0x0, 0xf8, 0xff,
+ 0xc, 0x0, 0x2, 0x0, 0xfc, 0xff, 0x18, 0x0, 0xec, 0xff, 0x1a, 0x0, 0xf8,
+ 0xff, 0x4, 0x0, 0xc, 0x0, 0x5, 0x0, 0xf, 0x0, 0xf0, 0xff, 0xf5, 0xff,
+ 0x68, 0xff, 0xfe, 0xff, 0xa, 0x0, 0xe6, 0xff, 0xf1, 0xff, 0x4, 0x0, 0x9,
+ 0x0, 0x9, 0x0, 0x1a, 0x0, 0xf5, 0xff, 0x0, 0x0, 0xf4, 0xff, 0xf2, 0xff,
+ 0xfd, 0xff, 0x0, 0x0, 0x6, 0x0, 0x84, 0xff, 0x20, 0x0, 0xd, 0x0, 0xb, 0x0,
+ 0x13, 0x0, 0xeb, 0xff, 0x5, 0x0, 0x8, 0x0, 0x7, 0x0, 0x3, 0x0, 0xec, 0xff,
+ 0x10, 0x0, 0x6, 0x0, 0xb, 0x0, 0xfa, 0xff, 0xed, 0xff, 0x6f, 0xff, 0xf4,
+ 0xff, 0xf6, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff, 0xff, 0x15, 0x0, 0xe9,
+ 0xff, 0x8, 0x0, 0xf4, 0xff, 0xfe, 0xff, 0x6, 0x0, 0x6, 0x0, 0x6, 0x0,
+ 0xef, 0xff, 0xe8, 0xff, 0x79, 0xff, 0x17, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2,
+ 0x0, 0x4, 0x0, 0x2, 0x0, 0x1c, 0x0, 0x18, 0x0, 0xf8, 0xff, 0x9, 0x0, 0xfe,
+ 0xff, 0xf0, 0xff, 0xf2, 0xff, 0xf8, 0xff, 0xc, 0x0, 0x73, 0xff, 0x17, 0x0,
+ 0xfd, 0xff, 0xf9, 0xff, 0x1d, 0x0, 0xe8, 0xff, 0xf5, 0xff, 0xf3, 0xff,
+ 0x0, 0x0, 0xfa, 0xff, 0xe9, 0xff, 0x3, 0x0, 0x6, 0x0, 0xfc, 0xff, 0xfc,
+ 0xff, 0xe6, 0xff, 0x8a, 0xff, 0xf, 0x0, 0xf7, 0xff, 0x9, 0x0, 0x5, 0x0,
+ 0xf, 0x0, 0x11, 0x0, 0xf0, 0xff, 0x21, 0x0, 0xe9, 0xff, 0xb, 0x0, 0xb,
+ 0x0, 0x8, 0x0, 0xfa, 0xff, 0xf0, 0xff, 0x8, 0x0, 0x79, 0xff, 0x15, 0x0,
+ 0x6, 0x0, 0xee, 0xff, 0x7, 0x0, 0x1, 0x0, 0xed, 0xff, 0x18, 0x0, 0xe, 0x0,
+ 0xf2, 0xff, 0xfe, 0xff, 0xf4, 0xff, 0xf1, 0xff, 0xea, 0xff, 0x9, 0x0, 0x6,
+ 0x0, 0x89, 0xff, 0x18, 0x0, 0xfa, 0xff, 0x3, 0x0, 0x13, 0x0, 0xec, 0xff,
+ 0xfd, 0xff, 0xe7, 0xff, 0x7, 0x0, 0xf5, 0xff, 0xe9, 0xff, 0x7, 0x0, 0xf,
+ 0x0, 0x8, 0x0, 0xf8, 0xff, 0xf0, 0xff, 0x83, 0xff, 0xf1, 0xff, 0xf9, 0xff,
+ 0x6, 0x0, 0xf3, 0xff, 0x9, 0x0, 0x1c, 0x0, 0xfc, 0xff, 0x22, 0x0, 0xf5,
+ 0xff, 0xd, 0x0, 0x5, 0x0, 0x6, 0x0, 0xf8, 0xff, 0xf0, 0xff, 0x14, 0x0,
+ 0x78, 0xff, 0x11, 0x0, 0x1, 0x0, 0xed, 0xff, 0x9, 0x0, 0xe9, 0xff, 0xf4,
+ 0xff, 0xc, 0x0, 0x6, 0x0, 0xf8, 0xff, 0xf6, 0xff, 0xef, 0xff, 0xfd, 0xff,
+ 0xf6, 0xff, 0xa, 0x0, 0x3, 0x0, 0x9a, 0xff, 0x27, 0x0, 0xfb, 0xff, 0x18,
+ 0x0, 0x15, 0x0, 0xeb, 0xff, 0x10, 0x0, 0xf1, 0xff, 0x6, 0x0, 0xf5, 0xff,
+ 0xf8, 0xff, 0xd, 0x0, 0x1a, 0x0, 0x8, 0x0, 0xf8, 0xff, 0x0, 0x0, 0x7d,
+ 0xff, 0xf0, 0xff, 0x8, 0x0, 0xf8, 0xff, 0xf9, 0xff, 0x10, 0x0, 0x1f, 0x0,
+ 0xf8, 0xff, 0x1a, 0x0, 0xf7, 0xff, 0x8, 0x0, 0xfc, 0xff, 0x7, 0x0, 0xf5,
+ 0xff, 0xf9, 0xff, 0xe, 0x0, 0x8e, 0xff, 0x1f, 0x0, 0xf, 0x0, 0xff, 0xff,
+ 0x15, 0x0, 0xfe, 0xff, 0xfa, 0xff, 0xf, 0x0, 0xa, 0x0, 0x5, 0x0, 0xf6,
+ 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0x7, 0x0, 0x0, 0x0, 0x88, 0xff,
+ 0xf5, 0xff, 0xf7, 0xff, 0xd, 0x0, 0xe, 0x0, 0xed, 0xff, 0xf, 0x0, 0xe8,
+ 0xff, 0x12, 0x0, 0x7, 0x0, 0xf5, 0xff, 0x3, 0x0, 0x10, 0x0, 0xa, 0x0, 0x5,
+ 0x0, 0xf5, 0xff, 0x8a, 0xff, 0xfb, 0xff, 0xf9, 0xff, 0x1, 0x0, 0xf8, 0xff,
+ 0xa, 0x0, 0x11, 0x0, 0x2, 0x0, 0x19, 0x0, 0xf0, 0xff, 0xb, 0x0, 0xf5,
+ 0xff, 0x8, 0x0, 0xff, 0xff, 0xf6, 0xff, 0x13, 0x0, 0x8d, 0xff, 0x25, 0x0,
+ 0x2, 0x0, 0xfc, 0xff, 0x15, 0x0, 0xf5, 0xff, 0xe9, 0xff, 0xc, 0x0, 0xeb,
+ 0xff, 0xf4, 0xff, 0xf4, 0xff, 0xf0, 0xff, 0x5, 0x0, 0xf6, 0xff, 0x9, 0x0,
+ 0xf2, 0xff, 0x8c, 0xff, 0xe9, 0xff, 0xf9, 0xff, 0x12, 0x0, 0x5, 0x0, 0xfb,
+ 0xff, 0x17, 0x0, 0xe5, 0xff, 0x1b, 0x0, 0xfd, 0xff, 0xfc, 0xff, 0x0, 0x0,
+ 0x10, 0x0, 0xa, 0x0, 0x4, 0x0, 0xf6, 0xff, 0x90, 0xff, 0x0, 0x0, 0xff,
+ 0xff, 0xfd, 0xff, 0xe7, 0xff, 0x13, 0x0, 0x4, 0x0, 0xe, 0x0, 0x1c, 0x0,
+ 0xec, 0xff, 0x9, 0x0, 0xf3, 0xff, 0x4, 0x0, 0x5, 0x0, 0xf7, 0xff, 0x1d,
+ 0x0, 0x96, 0xff, 0x24, 0x0, 0xfa, 0xff, 0xee, 0xff, 0xd, 0x0, 0xed, 0xff,
+ 0xd1, 0xff, 0xf7, 0xff, 0xe7, 0xff, 0xef, 0xff, 0xe7, 0xff, 0xe4, 0xff,
+ 0x8, 0x0, 0x2, 0x0, 0xd, 0x0, 0xef, 0xff, 0xa3, 0xff, 0xfe, 0xff, 0xfe,
+ 0xff, 0x16, 0x0, 0x17, 0x0, 0x9, 0x0, 0xc, 0x0, 0xef, 0xff, 0xb, 0x0,
+ 0xfa, 0xff, 0x6, 0x0, 0x9, 0x0, 0x9, 0x0, 0xa, 0x0, 0x2, 0x0, 0xf9, 0xff,
+ 0xa0, 0xff, 0x19, 0x0, 0x9, 0x0, 0xff, 0xff, 0x1, 0x0, 0x9, 0x0, 0xf5,
+ 0xff, 0xb, 0x0, 0xfe, 0xff, 0xf4, 0xff, 0x5, 0x0, 0xf9, 0xff, 0x3, 0x0,
+ 0xff, 0xff, 0xf9, 0xff, 0x12, 0x0, 0xc2, 0xff, 0x4b, 0x0, 0xf1, 0xff, 0xb,
+ 0x0, 0x16, 0x0, 0xe3, 0xff, 0xd4, 0xff, 0xf5, 0xff, 0xf4, 0xff, 0x2, 0x0,
+ 0xf0, 0xff, 0xfa, 0xff, 0xd, 0x0, 0x2, 0x0, 0x4, 0x0, 0xf3, 0xff, 0xa8,
+ 0xff, 0xe, 0x0, 0xf3, 0xff, 0x1, 0x0, 0x16, 0x0, 0x7, 0x0, 0x4, 0x0, 0xef,
+ 0xff, 0xf6, 0xff, 0xb, 0x0, 0x5, 0x0, 0xd, 0x0, 0xff, 0xff, 0x7, 0x0, 0x2,
+ 0x0, 0xe7, 0xff, 0xb6, 0xff, 0x2d, 0x0, 0xe, 0x0, 0x7, 0x0, 0xa, 0x0, 0xd,
+ 0x0, 0xe9, 0xff, 0xd, 0x0, 0xe8, 0xff, 0xff, 0xff, 0x3, 0x0, 0xff, 0xff,
+ 0xfe, 0xff, 0x3, 0x0, 0xfa, 0xff, 0x3, 0x0, 0xc6, 0xff, 0x3c, 0x0, 0xf3,
+ 0xff, 0x14, 0x0, 0x11, 0x0, 0xee, 0xff, 0xd6, 0xff, 0xf8, 0xff, 0xe3,
+ 0xff, 0x8, 0x0, 0xf4, 0xff, 0x2, 0x0, 0xa, 0x0, 0x5, 0x0, 0x8, 0x0, 0xf4,
+ 0xff, 0xab, 0xff, 0x17, 0x0, 0xe7, 0xff, 0xed, 0xff, 0x4, 0x0, 0xfe, 0xff,
+ 0xa, 0x0, 0xec, 0xff, 0xd8, 0xff, 0xfe, 0xff, 0x2, 0x0, 0x10, 0x0, 0x5,
+ 0x0, 0x15, 0x0, 0xff, 0xff, 0xf1, 0xff, 0xc0, 0xff, 0x47, 0x0, 0x1, 0x0,
+ 0xf9, 0xff, 0x0, 0x0, 0xfe, 0xff, 0xe6, 0xff, 0xc, 0x0, 0xe6, 0xff, 0xf7,
+ 0xff, 0xfe, 0xff, 0x2, 0x0, 0x4, 0x0, 0xa, 0x0, 0xfc, 0xff, 0x8, 0x0,
+ 0xc3, 0xff, 0x38, 0x0, 0xf7, 0xff, 0x2, 0x0, 0x3, 0x0, 0xf7, 0xff, 0xe0,
+ 0xff, 0xed, 0xff, 0x4, 0x0, 0xfe, 0xff, 0xf0, 0xff, 0xfb, 0xff, 0x6, 0x0,
+ 0x7, 0x0, 0x5, 0x0, 0xf3, 0xff, 0xc5, 0xff, 0x2e, 0x0, 0xfb, 0xff, 0x1,
+ 0x0, 0xfb, 0xff, 0x10, 0x0, 0x14, 0x0, 0xef, 0xff, 0xb, 0x0, 0xf0, 0xff,
+ 0x5, 0x0, 0xa, 0x0, 0x7, 0x0, 0xf, 0x0, 0xf4, 0xff, 0x2, 0x0, 0xd1, 0xff,
+ 0x48, 0x0, 0x7, 0x0, 0xc, 0x0, 0x1, 0x0, 0xfc, 0xff, 0xef, 0xff, 0xfe,
+ 0xff, 0x1a, 0x0, 0xf5, 0xff, 0xf5, 0xff, 0xf7, 0xff, 0xa, 0x0, 0x6, 0x0,
+ 0x1, 0x0, 0x6, 0x0, 0xd2, 0xff, 0x37, 0x0, 0x1, 0x0, 0xd, 0x0, 0x4, 0x0,
+ 0xf9, 0xff, 0xfe, 0xff, 0xf6, 0xff, 0x23, 0x0, 0xfc, 0xff, 0xf7, 0xff,
+ 0xfe, 0xff, 0xc, 0x0, 0xf, 0x0, 0x8, 0x0, 0xf7, 0xff, 0xbf, 0xff, 0x31,
+ 0x0, 0xfc, 0xff, 0xf9, 0xff, 0xfb, 0xff, 0x3, 0x0, 0x18, 0x0, 0xf8, 0xff,
+ 0x10, 0x0, 0xeb, 0xff, 0x6, 0x0, 0xfe, 0xff, 0xc, 0x0, 0x9, 0x0, 0xfb,
+ 0xff, 0x0, 0x0, 0xd1, 0xff, 0x4b, 0x0, 0xf1, 0xff, 0xe, 0x0, 0x6, 0x0,
+ 0xea, 0xff, 0xf5, 0xff, 0xf3, 0xff, 0x2c, 0x0, 0xf0, 0xff, 0xf6, 0xff,
+ 0xed, 0xff, 0x10, 0x0, 0x9, 0x0, 0xfb, 0xff, 0x4, 0x0, 0xc6, 0xff, 0x30,
+ 0x0, 0xf5, 0xff, 0x4, 0x0, 0x0, 0x0, 0xfa, 0xff, 0xb, 0x0, 0xfa, 0xff,
+ 0x24, 0x0, 0xf8, 0xff, 0xfc, 0xff, 0xf9, 0xff, 0x8, 0x0, 0xf, 0x0, 0xff,
+ 0xff, 0xf8, 0xff, 0xbc, 0xff, 0x2e, 0x0, 0xfe, 0xff, 0xf5, 0xff, 0xf7,
+ 0xff, 0x6, 0x0, 0x12, 0x0, 0xf0, 0xff, 0x17, 0x0, 0xee, 0xff, 0x0, 0x0,
+ 0xf8, 0xff, 0xc, 0x0, 0x8, 0x0, 0xfe, 0xff, 0xff, 0xff, 0xdd, 0xff, 0x46,
+ 0x0, 0xfa, 0xff, 0x16, 0x0, 0x9, 0x0, 0xf5, 0xff, 0xfa, 0xff, 0xf1, 0xff,
+ 0x31, 0x0, 0xfd, 0xff, 0xfa, 0xff, 0xf7, 0xff, 0xe, 0x0, 0x9, 0x0, 0x2,
+ 0x0, 0x2, 0x0, 0xc1, 0xff, 0x15, 0x0, 0x4, 0x0, 0xf9, 0xff, 0xfd, 0xff,
+ 0x4, 0x0, 0x16, 0x0, 0xf9, 0xff, 0x21, 0x0, 0x1, 0x0, 0x1, 0x0, 0xfa,
+ 0xff, 0x5, 0x0, 0x9, 0x0, 0xb, 0x0, 0xf2, 0xff, 0xd0, 0xff, 0x26, 0x0,
+ 0x2, 0x0, 0x1, 0x0, 0xfd, 0xff, 0x8, 0x0, 0x12, 0x0, 0xf6, 0xff, 0x1c,
+ 0x0, 0xf0, 0xff, 0x7, 0x0, 0xfa, 0xff, 0xf, 0x0, 0x5, 0x0, 0xfe, 0xff,
+ 0x2, 0x0, 0xdb, 0xff, 0x2c, 0x0, 0xef, 0xff, 0x10, 0x0, 0xe, 0x0, 0xf0,
+ 0xff, 0xf7, 0xff, 0xf7, 0xff, 0x22, 0x0, 0xfd, 0xff, 0xfc, 0xff, 0xf9,
+ 0xff, 0xb, 0x0, 0x7, 0x0, 0x5, 0x0, 0xfb, 0xff, 0xc4, 0xff, 0x3, 0x0, 0x1,
+ 0x0, 0xfc, 0xff, 0xfa, 0xff, 0x2, 0x0, 0x11, 0x0, 0x5, 0x0, 0x13, 0x0,
+ 0xfb, 0xff, 0x4, 0x0, 0xff, 0xff, 0x1, 0x0, 0x6, 0x0, 0x8, 0x0, 0xf6,
+ 0xff, 0xd7, 0xff, 0x19, 0x0, 0x3, 0x0, 0x5, 0x0, 0xfa, 0xff, 0x1, 0x0,
+ 0x2, 0x0, 0xf9, 0xff, 0xe, 0x0, 0xef, 0xff, 0x3, 0x0, 0xfa, 0xff, 0x8,
+ 0x0, 0x1, 0x0, 0xfb, 0xff, 0x3, 0x0, 0xde, 0xff, 0x12, 0x0, 0xf5, 0xff,
+ 0xa, 0x0, 0x8, 0x0, 0xef, 0xff, 0xf7, 0xff, 0xf2, 0xff, 0x15, 0x0, 0x1,
+ 0x0, 0xf8, 0xff, 0xf7, 0xff, 0x4, 0x0, 0x6, 0x0, 0x4, 0x0, 0xf9, 0xff,
+ 0xcd, 0xff, 0xf6, 0xff, 0xc, 0x0, 0xf6, 0xff, 0xf8, 0xff, 0x5, 0x0, 0x14,
+ 0x0, 0x9, 0x0, 0xa, 0x0, 0xfb, 0xff, 0x6, 0x0, 0xff, 0xff, 0xff, 0xff,
+ 0x2, 0x0, 0x0, 0x0, 0xfc, 0xff, 0xdc, 0xff, 0xf, 0x0, 0x3, 0x0, 0x5, 0x0,
+ 0xff, 0xff, 0xf5, 0xff, 0xff, 0xff, 0xf6, 0xff, 0xf, 0x0, 0xf3, 0xff,
+ 0xfe, 0xff, 0xf5, 0xff, 0x9, 0x0, 0x0, 0x0, 0xf7, 0xff, 0x1, 0x0, 0xe1,
+ 0xff, 0xb, 0x0, 0xf9, 0xff, 0xe, 0x0, 0x7, 0x0, 0xee, 0xff, 0xfe, 0xff,
+ 0xfb, 0xff, 0x15, 0x0, 0x1, 0x0, 0xfb, 0xff, 0xf9, 0xff, 0x2, 0x0, 0x4,
+ 0x0, 0x1, 0x0, 0xfb, 0xff, 0xc8, 0xff, 0xf1, 0xff, 0xa, 0x0, 0xf5, 0xff,
+ 0xf8, 0xff, 0x2, 0x0, 0x12, 0x0, 0x9, 0x0, 0x4, 0x0, 0xf7, 0xff, 0x4, 0x0,
+ 0xfd, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xe1, 0xff,
+ 0x14, 0x0, 0xfb, 0xff, 0xd, 0x0, 0x2, 0x0, 0xf3, 0xff, 0xfd, 0xff, 0xf1,
+ 0xff, 0xf, 0x0, 0xf5, 0xff, 0xfb, 0xff, 0xf5, 0xff, 0x8, 0x0, 0x2, 0x0,
+ 0xf5, 0xff, 0x4, 0x0, 0xd8, 0xff, 0x2, 0x0, 0xfa, 0xff, 0x4, 0x0, 0x4,
+ 0x0, 0xf6, 0xff, 0x0, 0x0, 0xfb, 0xff, 0x8, 0x0, 0x3, 0x0, 0xfb, 0xff,
+ 0xf8, 0xff, 0xff, 0xff, 0x6, 0x0, 0xff, 0xff, 0xf7, 0xff, 0xc9, 0xff,
+ 0xf3, 0xff, 0x8, 0x0, 0xf0, 0xff, 0xf9, 0xff, 0x4, 0x0, 0x11, 0x0, 0x2,
+ 0x0, 0x1, 0x0, 0xf8, 0xff, 0x3, 0x0, 0xfd, 0xff, 0x1, 0x0, 0x1, 0x0, 0xfa,
+ 0xff, 0xff, 0xff, 0xe3, 0xff, 0x14, 0x0, 0xfa, 0xff, 0xa, 0x0, 0x6, 0x0,
+ 0xf6, 0xff, 0xf9, 0xff, 0xf3, 0xff, 0xd, 0x0, 0xfb, 0xff, 0xfc, 0xff,
+ 0xf8, 0xff, 0x9, 0x0, 0x2, 0x0, 0xf7, 0xff, 0x3, 0x0, 0xd2, 0xff, 0xfb,
+ 0xff, 0xfb, 0xff, 0xfc, 0xff, 0x4, 0x0, 0xfc, 0xff, 0x3, 0x0, 0xfd, 0xff,
+ 0x8, 0x0, 0x6, 0x0, 0xfe, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0x4, 0x0, 0x3,
+ 0x0, 0xf5, 0xff, 0xd1, 0xff, 0xfc, 0xff, 0x9, 0x0, 0xf8, 0xff, 0xfd, 0xff,
+ 0x5, 0x0, 0xf, 0x0, 0x2, 0x0, 0x5, 0x0, 0xf7, 0xff, 0x6, 0x0, 0xff, 0xff,
+ 0x3, 0x0, 0xff, 0xff, 0xfa, 0xff, 0x3, 0x0, 0xe2, 0xff, 0x11, 0x0, 0xf7,
+ 0xff, 0xa, 0x0, 0x9, 0x0, 0xf2, 0xff, 0xf7, 0xff, 0xf0, 0xff, 0x9, 0x0,
+ 0xff, 0xff, 0xfc, 0xff, 0xf7, 0xff, 0x9, 0x0, 0x1, 0x0, 0xfa, 0xff, 0xff,
+ 0xff, 0xd2, 0xff, 0xf5, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0x3, 0x0, 0xfe,
+ 0xff, 0x4, 0x0, 0xff, 0xff, 0x0, 0x0, 0x5, 0x0, 0x2, 0x0, 0x0, 0x0, 0xfe,
+ 0xff, 0x3, 0x0, 0x2, 0x0, 0xf8, 0xff, 0xd2, 0xff, 0xfd, 0xff, 0x7, 0x0,
+ 0xf7, 0xff, 0xfd, 0xff, 0x4, 0x0, 0x5, 0x0, 0xfe, 0xff, 0xfc, 0xff, 0xf9,
+ 0xff, 0x5, 0x0, 0xfd, 0xff, 0x3, 0x0, 0xfe, 0xff, 0xf9, 0xff, 0x3, 0x0,
+ 0xe3, 0xff, 0xd, 0x0, 0xf5, 0xff, 0xa, 0x0, 0x8, 0x0, 0xf3, 0xff, 0xf4,
+ 0xff, 0xf0, 0xff, 0x7, 0x0, 0x4, 0x0, 0xfa, 0xff, 0xf9, 0xff, 0x7, 0x0,
+ 0x3, 0x0, 0xfc, 0xff, 0xfe, 0xff, 0xd1, 0xff, 0xf1, 0xff, 0x3, 0x0, 0xf9,
+ 0xff, 0xfd, 0xff, 0x3, 0x0, 0x6, 0x0, 0x3, 0x0, 0xfd, 0xff, 0x5, 0x0, 0x3,
+ 0x0, 0x2, 0x0, 0xfd, 0xff, 0x1, 0x0, 0x2, 0x0, 0xf8, 0xff, 0xd7, 0xff,
+ 0xfe, 0xff, 0x7, 0x0, 0xfc, 0xff, 0xfc, 0xff, 0x0, 0x0, 0x3, 0x0, 0xfc,
+ 0xff, 0xfd, 0xff, 0xfc, 0xff, 0x2, 0x0, 0xfd, 0xff, 0x5, 0x0, 0xfe, 0xff,
+ 0xf9, 0xff, 0x3, 0x0, 0xe4, 0xff, 0x8, 0x0, 0xf7, 0xff, 0xd, 0x0, 0x6,
+ 0x0, 0xf3, 0xff, 0xf6, 0xff, 0xf7, 0xff, 0x5, 0x0, 0x6, 0x0, 0xfb, 0xff,
+ 0xfc, 0xff, 0x5, 0x0, 0x3, 0x0, 0xfe, 0xff, 0xfd, 0xff, 0xcf, 0xff, 0xee,
+ 0xff, 0x3, 0x0, 0xf7, 0xff, 0xfb, 0xff, 0x2, 0x0, 0x7, 0x0, 0x6, 0x0,
+ 0xf8, 0xff, 0x3, 0x0, 0x3, 0x0, 0x2, 0x0, 0xfe, 0xff, 0x1, 0x0, 0x1, 0x0,
+ 0xf8, 0xff, 0xdc, 0xff, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0xfe, 0xff, 0xfd,
+ 0xff, 0xfe, 0xff, 0xfb, 0xff, 0xfe, 0xff, 0xfb, 0xff, 0x0, 0x0, 0xfd,
+ 0xff, 0x7, 0x0, 0xff, 0xff, 0xf9, 0xff, 0x5, 0x0, 0xe0, 0xff, 0x3, 0x0,
+ 0xf6, 0xff, 0x9, 0x0, 0x5, 0x0, 0xf5, 0xff, 0xf6, 0xff, 0xf8, 0xff, 0x0,
+ 0x0, 0x7, 0x0, 0xfa, 0xff, 0xfd, 0xff, 0x3, 0x0, 0x3, 0x0, 0xff, 0xff,
+ 0xf9, 0xff, 0xd0, 0xff, 0xef, 0xff, 0x6, 0x0, 0xf7, 0xff, 0xf9, 0xff, 0x3,
+ 0x0, 0x8, 0x0, 0x5, 0x0, 0xf7, 0xff, 0x1, 0x0, 0x2, 0x0, 0x3, 0x0, 0xfe,
+ 0xff, 0x1, 0x0, 0x0, 0x0, 0xfa, 0xff, 0xdf, 0xff, 0x5, 0x0, 0x1, 0x0, 0x5,
+ 0x0, 0x0, 0x0, 0xfc, 0xff, 0xfb, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xfd, 0xff,
+ 0xfe, 0xff, 0xfd, 0xff, 0x6, 0x0, 0xff, 0xff, 0xf8, 0xff, 0x4, 0x0, 0xdf,
+ 0xff, 0xff, 0xff, 0xf7, 0xff, 0x5, 0x0, 0x4, 0x0, 0xf7, 0xff, 0xf9, 0xff,
+ 0xfb, 0xff, 0xff, 0xff, 0x8, 0x0, 0xfb, 0xff, 0x0, 0x0, 0x2, 0x0, 0x3,
+ 0x0, 0x1, 0x0, 0xf8, 0xff, 0xd2, 0xff, 0xf2, 0xff, 0x5, 0x0, 0xf7, 0xff,
+ 0xfa, 0xff, 0x6, 0x0, 0x7, 0x0, 0x7, 0x0, 0xf7, 0xff, 0xfe, 0xff, 0x3,
+ 0x0, 0x5, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0xfd, 0xff, 0xe0, 0xff,
+ 0x8, 0x0, 0xfb, 0xff, 0x7, 0x0, 0x3, 0x0, 0xfb, 0xff, 0xf9, 0xff, 0xf7,
+ 0xff, 0x0, 0x0, 0xfe, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x7, 0x0, 0x0, 0x0,
+ 0xfb, 0xff, 0x3, 0x0, 0xdc, 0xff, 0xfd, 0xff, 0xf7, 0xff, 0x3, 0x0, 0x4,
+ 0x0, 0xfb, 0xff, 0xfb, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0x5, 0x0, 0xfc, 0xff,
+ 0x3, 0x0, 0x0, 0x0, 0x3, 0x0, 0x3, 0x0, 0xf8, 0xff, 0xd2, 0xff, 0xf6,
+ 0xff, 0x5, 0x0, 0xf7, 0xff, 0xfb, 0xff, 0x6, 0x0, 0x5, 0x0, 0x5, 0x0,
+ 0xf7, 0xff, 0xfc, 0xff, 0x2, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xfe,
+ 0xff, 0xfe, 0xff, 0xe2, 0xff, 0xa, 0x0, 0xfa, 0xff, 0x9, 0x0, 0x4, 0x0,
+ 0xfa, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0x2, 0x0, 0xff, 0xff, 0xfb, 0xff,
+ 0xfe, 0xff, 0x6, 0x0, 0x1, 0x0, 0xfc, 0xff, 0x3, 0x0, 0xda, 0xff, 0xfa,
+ 0xff, 0xfb, 0xff, 0xff, 0xff, 0x2, 0x0, 0xff, 0xff, 0xfe, 0xff, 0x1, 0x0,
+ 0xfb, 0xff, 0x5, 0x0, 0xfe, 0xff, 0x3, 0x0, 0xfe, 0xff, 0x3, 0x0, 0x4,
+ 0x0, 0xf8, 0xff, 0xd6, 0xff, 0xf9, 0xff, 0x5, 0x0, 0xfa, 0xff, 0xfc, 0xff,
+ 0x4, 0x0, 0x4, 0x0, 0x2, 0x0, 0xfa, 0xff, 0xfb, 0xff, 0x2, 0x0, 0x3, 0x0,
+ 0x1, 0x0, 0xff, 0xff, 0xfe, 0xff, 0x1, 0x0, 0xe4, 0xff, 0xa, 0x0, 0xf9,
+ 0xff, 0x9, 0x0, 0x5, 0x0, 0xf9, 0xff, 0xf7, 0xff, 0xf8, 0xff, 0x2, 0x0,
+ 0x0, 0x0, 0xfb, 0xff, 0xfe, 0xff, 0x5, 0x0, 0x1, 0x0, 0xfe, 0xff, 0x1,
+ 0x0, 0xd8, 0xff, 0xf8, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0x0, 0x0, 0xff, 0xff,
+ 0x1, 0x0, 0x2, 0x0, 0xfa, 0xff, 0x3, 0x0, 0xff, 0xff, 0x3, 0x0, 0xfe,
+ 0xff, 0x2, 0x0, 0x3, 0x0, 0xf8, 0xff, 0xd9, 0xff, 0xfd, 0xff, 0x4, 0x0,
+ 0xfd, 0xff, 0xfd, 0xff, 0x2, 0x0, 0x2, 0x0, 0x0, 0x0, 0xfc, 0xff, 0xfa,
+ 0xff, 0x1, 0x0, 0x1, 0x0, 0x2, 0x0, 0xff, 0xff, 0xfd, 0xff, 0x2, 0x0,
+ 0xe4, 0xff, 0x8, 0x0, 0xf8, 0xff, 0x7, 0x0, 0x5, 0x0, 0xf8, 0xff, 0xf8,
+ 0xff, 0xf7, 0xff, 0x1, 0x0, 0x1, 0x0, 0xfb, 0xff, 0xfd, 0xff, 0x4, 0x0,
+ 0x2, 0x0, 0xff, 0xff, 0xff, 0xff, 0xd8, 0xff, 0xf7, 0xff, 0x0, 0x0, 0xfb,
+ 0xff, 0xff, 0xff, 0x1, 0x0, 0x2, 0x0, 0x3, 0x0, 0xfa, 0xff, 0x1, 0x0, 0x0,
+ 0x0, 0x2, 0x0, 0xfe, 0xff, 0x1, 0x0, 0x4, 0x0, 0xfa, 0xff, 0xdc, 0xff,
+ 0x0, 0x0, 0x3, 0x0, 0xff, 0xff, 0xfe, 0xff, 0x1, 0x0, 0x0, 0x0, 0xfe,
+ 0xff, 0xfe, 0xff, 0xfb, 0xff, 0x0, 0x0, 0xff, 0xff, 0x3, 0x0, 0xff, 0xff,
+ 0xfc, 0xff, 0x3, 0x0, 0xe4, 0xff, 0x7, 0x0, 0xf8, 0xff, 0x7, 0x0, 0x5,
+ 0x0, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0x2, 0x0, 0x2, 0x0, 0xfb, 0xff,
+ 0xfd, 0xff, 0x3, 0x0, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xff, 0xd8, 0xff, 0xf7,
+ 0xff, 0x1, 0x0, 0xf9, 0xff, 0xfe, 0xff, 0x2, 0x0, 0x4, 0x0, 0x4, 0x0,
+ 0xfa, 0xff, 0xff, 0xff, 0x1, 0x0, 0x2, 0x0, 0xfe, 0xff, 0x1, 0x0, 0x2,
+ 0x0, 0xfb, 0xff, 0xe0, 0xff, 0x4, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0xff,
+ 0xff, 0xfe, 0xff, 0xfc, 0xff, 0x0, 0x0, 0xfb, 0xff, 0xff, 0xff, 0xfe,
+ 0xff, 0x3, 0x0, 0xff, 0xff, 0xfc, 0xff, 0x3, 0x0, 0xe3, 0xff, 0x4, 0x0,
+ 0xf9, 0xff, 0x5, 0x0, 0x5, 0x0, 0xfa, 0xff, 0xfa, 0xff, 0xfb, 0xff, 0x1,
+ 0x0, 0x2, 0x0, 0xfc, 0xff, 0xfe, 0xff, 0x2, 0x0, 0x3, 0x0, 0x1, 0x0, 0xfd,
+ 0xff, 0xd9, 0xff, 0xf8, 0xff, 0x2, 0x0, 0xf9, 0xff, 0xfd, 0xff, 0x3, 0x0,
+ 0x4, 0x0, 0x3, 0x0, 0xfb, 0xff, 0xfe, 0xff, 0x2, 0x0, 0x2, 0x0, 0xff,
+ 0xff, 0x1, 0x0, 0x1, 0x0, 0xfd, 0xff, 0xe2, 0xff, 0x6, 0x0, 0xff, 0xff,
+ 0x3, 0x0, 0x1, 0x0, 0xfd, 0xff, 0xfc, 0xff, 0xfb, 0xff, 0x1, 0x0, 0xfc,
+ 0xff, 0xfe, 0xff, 0xfd, 0xff, 0x4, 0x0, 0x0, 0x0, 0xfd, 0xff, 0x3, 0x0,
+ 0xe2, 0xff, 0x3, 0x0, 0xfa, 0xff, 0x3, 0x0, 0x4, 0x0, 0xfb, 0xff, 0xfc,
+ 0xff, 0xfc, 0xff, 0x0, 0x0, 0x3, 0x0, 0xfd, 0xff, 0xff, 0xff, 0x1, 0x0,
+ 0x4, 0x0, 0x2, 0x0, 0xfc, 0xff, 0xda, 0xff, 0xfa, 0xff, 0x3, 0x0, 0xfa,
+ 0xff, 0xfd, 0xff, 0x3, 0x0, 0x4, 0x0, 0x2, 0x0, 0xfc, 0xff, 0xfd, 0xff,
+ 0x2, 0x0, 0x1, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xfe, 0xff, 0xe5,
+ 0xff, 0x8, 0x0, 0xfe, 0xff, 0x4, 0x0, 0x2, 0x0, 0xfc, 0xff, 0xfc, 0xff,
+ 0xf9, 0xff, 0x3, 0x0, 0xfd, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0x4, 0x0, 0x1,
+ 0x0, 0xfd, 0xff, 0x3, 0x0, 0xe1, 0xff, 0x1, 0x0, 0xfb, 0xff, 0x1, 0x0,
+ 0x3, 0x0, 0xfc, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0x0, 0x0, 0x2, 0x0, 0xfe,
+ 0xff, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x2, 0x0, 0xfc, 0xff, 0xdc, 0xff,
+ 0xfd, 0xff, 0x3, 0x0, 0xfb, 0xff, 0xfd, 0xff, 0x3, 0x0, 0x3, 0x0, 0x1,
+ 0x0, 0xfd, 0xff, 0xfc, 0xff, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
+ 0xff, 0x0, 0x0, 0xe6, 0xff, 0x9, 0x0, 0xfc, 0xff, 0x5, 0x0, 0x3, 0x0,
+ 0xfb, 0xff, 0xfb, 0xff, 0xf9, 0xff, 0x3, 0x0, 0xff, 0xff, 0xfe, 0xff,
+ 0xfd, 0xff, 0x4, 0x0, 0x1, 0x0, 0xfd, 0xff, 0x1, 0x0, 0xe0, 0xff, 0xfe,
+ 0xff, 0xfd, 0xff, 0xff, 0xff, 0x1, 0x0, 0xfe, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x2, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x3, 0x0,
+ 0x2, 0x0, 0xfb, 0xff, 0xdf, 0xff, 0xff, 0xff, 0x3, 0x0, 0xfc, 0xff, 0xfe,
+ 0xff, 0x2, 0x0, 0x2, 0x0, 0x0, 0x0, 0xfe, 0xff, 0xfc, 0xff, 0x1, 0x0, 0x0,
+ 0x0, 0x1, 0x0, 0x0, 0x0, 0xfe, 0xff, 0x1, 0x0, 0xe6, 0xff, 0x8, 0x0, 0xfb,
+ 0xff, 0x5, 0x0, 0x3, 0x0, 0xfa, 0xff, 0xfb, 0xff, 0xf9, 0xff, 0x3, 0x0,
+ 0x0, 0x0, 0xfd, 0xff, 0xfd, 0xff, 0x3, 0x0, 0x2, 0x0, 0xff, 0xff, 0x0,
+ 0x0, 0xe0, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0x1, 0x0, 0xff, 0xff,
+ 0x1, 0x0, 0x0, 0x0, 0xfe, 0xff, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0xff, 0xff,
+ 0x2, 0x0, 0x1, 0x0, 0xfb, 0xff, 0xe1, 0xff, 0x2, 0x0, 0x2, 0x0, 0xfe,
+ 0xff, 0xfe, 0xff, 0x0, 0x0, 0x1, 0x0, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff,
+ 0x1, 0x0, 0xff, 0xff, 0x2, 0x0, 0x0, 0x0, 0xfe, 0xff, 0x1, 0x0, 0xe7,
+ 0xff, 0x7, 0x0, 0xfb, 0xff, 0x4, 0x0, 0x4, 0x0, 0xfa, 0xff, 0xfb, 0xff,
+ 0xfa, 0xff, 0x2, 0x0, 0x1, 0x0, 0xfd, 0xff, 0xfe, 0xff, 0x3, 0x0, 0x2,
+ 0x0, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc,
+ 0xff, 0x0, 0x0, 0x1, 0x0, 0x2, 0x0, 0x1, 0x0, 0xfd, 0xff, 0x0, 0x0, 0x1,
+ 0x0, 0x1, 0x0, 0xff, 0xff, 0x2, 0x0, 0x1, 0x0, 0xfc, 0xff, 0xe3, 0xff,
+ 0x3, 0x0, 0x1, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0xfd,
+ 0xff, 0x0, 0x0, 0xfd, 0xff, 0x0, 0x0, 0xff, 0xff, 0x2, 0x0, 0x0, 0x0,
+ 0xfe, 0xff, 0x1, 0x0, 0xe7, 0xff, 0x6, 0x0, 0xfb, 0xff, 0x3, 0x0, 0x3,
+ 0x0, 0xfb, 0xff, 0xfc, 0xff, 0xfb, 0xff, 0x2, 0x0, 0x2, 0x0, 0xfe, 0xff,
+ 0xfe, 0xff, 0x2, 0x0, 0x2, 0x0, 0xff, 0xff, 0xfe, 0xff, 0xe0, 0xff, 0xfd,
+ 0xff, 0x1, 0x0, 0xfb, 0xff, 0xff, 0xff, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0,
+ 0xfc, 0xff, 0xff, 0xff, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
+ 0xfd, 0xff, 0xe5, 0xff, 0x6, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xff,
+ 0xff, 0xff, 0xff, 0xfd, 0xff, 0x1, 0x0, 0xfd, 0xff, 0xff, 0xff, 0xfe,
+ 0xff, 0x3, 0x0, 0x0, 0x0, 0xfd, 0xff, 0x1, 0x0, 0xe6, 0xff, 0x4, 0x0,
+ 0xfb, 0xff, 0x2, 0x0, 0x3, 0x0, 0xfc, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0x1,
+ 0x0, 0x2, 0x0, 0xfe, 0xff, 0xff, 0xff, 0x1, 0x0, 0x2, 0x0, 0x0, 0x0, 0xfd,
+ 0xff, 0xe0, 0xff, 0xfe, 0xff, 0x2, 0x0, 0xfb, 0xff, 0xfe, 0xff, 0x2, 0x0,
+ 0x2, 0x0, 0x1, 0x0, 0xfd, 0xff, 0xfe, 0xff, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0,
+ 0x1, 0x0, 0x0, 0x0, 0xfd, 0xff, 0xe6, 0xff, 0x6, 0x0, 0xff, 0xff, 0x2,
+ 0x0, 0x1, 0x0, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0x1, 0x0, 0xfe, 0xff,
+ 0xff, 0xff, 0xfe, 0xff, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xff, 0x1, 0x0, 0xe5,
+ 0xff, 0x2, 0x0, 0xfd, 0xff, 0x0, 0x0, 0x2, 0x0, 0xfd, 0xff, 0xfe, 0xff,
+ 0xfd, 0xff, 0x0, 0x0, 0x1, 0x0, 0xfe, 0xff, 0xff, 0xff, 0x1, 0x0, 0x2,
+ 0x0, 0x1, 0x0, 0xfc, 0xff, 0xe2, 0xff, 0xff, 0xff, 0x2, 0x0, 0xfc, 0xff,
+ 0xfe, 0xff, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0xfd, 0xff, 0xfe, 0xff, 0x1,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xfe, 0xff, 0xe7, 0xff,
+ 0x6, 0x0, 0xfe, 0xff, 0x2, 0x0, 0x1, 0x0, 0xfd, 0xff, 0xfd, 0xff, 0xfc,
+ 0xff, 0x2, 0x0, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0x2, 0x0, 0x1, 0x0,
+ 0xfe, 0xff, 0x0, 0x0, 0xe4, 0xff, 0x1, 0x0, 0xfd, 0xff, 0x0, 0x0, 0x1,
+ 0x0, 0xfe, 0xff, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0xff, 0xff,
+ 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0xfc, 0xff, 0xe1, 0xff, 0x0, 0x0,
+ 0x2, 0x0, 0xfd, 0xff, 0xfe, 0xff, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xfe,
+ 0xff, 0xfd, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xff, 0xff,
+ 0xff, 0xff, 0xe5, 0xff, 0x5, 0x0, 0xfd, 0xff, 0x3, 0x0, 0x2, 0x0, 0xfc,
+ 0xff, 0xfd, 0xff, 0xfc, 0xff, 0x2, 0x0, 0xff, 0xff, 0xfe, 0xff, 0xfe,
+ 0xff, 0x2, 0x0, 0x1, 0x0, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0xfe, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff,
+ 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0xfd,
+ 0xff, 0xe1, 0xff, 0x1, 0x0, 0x1, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xfe, 0xff, 0x0, 0x0, 0xff, 0xff, 0x1,
+ 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xe4, 0xff, 0x3, 0x0, 0xfe, 0xff,
+ 0x2, 0x0, 0x1, 0x0, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0x2, 0x0, 0x0,
+ 0x0, 0xfe, 0xff, 0xfe, 0xff, 0x1, 0x0, 0x1, 0x0, 0xff, 0xff, 0xff, 0xff,
+ 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0,
+ 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1,
+ 0x0, 0x0, 0x0, 0xfe, 0xff, 0xe4, 0xff, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0xff, 0xff, 0x0, 0x0, 0xfe, 0xff, 0x0, 0x0, 0xfe, 0xff, 0x0, 0x0,
+ 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xe6, 0xff, 0x1,
+ 0x0, 0xfe, 0xff, 0x0, 0x0, 0x1, 0x0, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff,
+ 0x1, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0,
+ 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff,
+ 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x1, 0x0, 0x0, 0x0, 0xff, 0xff, 0xea, 0xff, 0x0, 0x0, 0xff, 0xff,
+ 0x0, 0x0, 0x0, 0x0, 0xfe, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff,
+ 0xed, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xfe, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0,
+ 0x1, 0x0, 0x0, 0x0, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0,
+ 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff,
+ 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xf3,
+ 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0,
+ 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xff, 0xff, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0,
+ 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff}}};
+
+} // namespace sadie
diff --git a/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generated/hrtf_assets.h b/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generated/hrtf_assets.h
new file mode 100644
index 000000000..77bfa225e
--- /dev/null
+++ b/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/generated/hrtf_assets.h
@@ -0,0 +1,45 @@
+/*
+Copyright 2018 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS-IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef THIRD_PARTY_SADIE_HRTF_DATABASE_GENERATED_HRTF_ASSETS_H_
+#define THIRD_PARTY_SADIE_HRTF_DATABASE_GENERATED_HRTF_ASSETS_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace sadie {
+
+// Note this class is automatically generated. Do not modify.
+class HrtfAssets {
+ public:
+ // Lookups and retrieves a file from an asset class.
+ //
+ // @param filename: Filename to be retrieved.
+ // @return std::string with raw file data. In case of an error, a nullptr is
+ // returned. Caller must take over the ownership of the returned data.
+ std::unique_ptr<std::string> GetFile(const std::string& filename) const;
+
+ private:
+ typedef std::unordered_map<std::string, std::vector<unsigned char>>
+ AssetDataMap;
+ static const AssetDataMap kAssetMap;
+};
+
+} // namespace sadie
+
+#endif // THIRD_PARTY_SADIE_HRTF_DATABASE_GENERATED_HRTF_ASSETS_H_
diff --git a/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/hrtf_assets.iad b/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/hrtf_assets.iad
new file mode 100644
index 000000000..d5e546977
--- /dev/null
+++ b/src/3rdparty/resonance-audio/third_party/SADIE_hrtf_database/hrtf_assets.iad
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<IAD name="HrtfAssets">
+ <assets>
+ <file>WAV/Subject_002/SH/sh_hrir_order_1.wav</file>
+ <file>WAV/Subject_002/SH/sh_hrir_order_2.wav</file>
+ <file>WAV/Subject_002/SH/sh_hrir_order_3.wav</file>
+ </assets>
+</IAD>