summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/resonance-audio/resonance_audio
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/resonance-audio/resonance_audio')
-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
267 files changed, 47090 insertions, 0 deletions
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_