#include "third_party/blink/renderer/platform/audio/stereo_panner.h"
#include <algorithm>
#include <memory>
#include "base/compiler_specific.h"
#include "base/memory/ptr_util.h"
#include "third_party/blink/renderer/platform/audio/audio_bus.h"
#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
#include "third_party/fdlibm/ieee754.h"
namespace blink {
StereoPanner::StereoPanner(float sample_rate) {}
void StereoPanner::PanWithSampleAccurateValues(const AudioBus* input_bus,
AudioBus* output_bus,
const float* pan_values,
uint32_t frames_to_process) {
DCHECK(input_bus);
DCHECK_LE(frames_to_process, input_bus->length());
DCHECK_GE(input_bus->NumberOfChannels(), 1u);
DCHECK_LE(input_bus->NumberOfChannels(), 2u);
unsigned number_of_input_channels = input_bus->NumberOfChannels();
DCHECK(output_bus);
DCHECK_EQ(output_bus->NumberOfChannels(), 2u);
DCHECK_LE(frames_to_process, output_bus->length());
const float* source_l = input_bus->Channel(0)->Data();
const float* source_r =
number_of_input_channels > 1 ? input_bus->Channel(1)->Data() : source_l;
float* destination_l =
output_bus->ChannelByType(AudioBus::kChannelLeft)->MutableData();
float* destination_r =
output_bus->ChannelByType(AudioBus::kChannelRight)->MutableData();
if (!source_l || !source_r || !destination_l || !destination_r) {
return;
}
double gain_l, gain_r, pan_radian;
int n = frames_to_process;
if (number_of_input_channels == 1) {
while (n--) {
float input_l = *UNSAFE_TODO(source_l++);
double pan = ClampTo(*UNSAFE_TODO(pan_values++), -1.0, 1.0);
pan_radian = (pan * 0.5 + 0.5) * kPiOverTwoDouble;
gain_l = fdlibm::cos(pan_radian);
gain_r = fdlibm::sin(pan_radian);
*UNSAFE_TODO(destination_l++) = static_cast<float>(input_l * gain_l);
*UNSAFE_TODO(destination_r++) = static_cast<float>(input_l * gain_r);
}
} else {
while (n--) {
float input_l = *UNSAFE_TODO(source_l++);
float input_r = *UNSAFE_TODO(source_r++);
double pan = ClampTo(*UNSAFE_TODO(pan_values++), -1.0, 1.0);
pan_radian = (pan <= 0 ? pan + 1 : pan) * kPiOverTwoDouble;
gain_l = fdlibm::cos(pan_radian);
gain_r = fdlibm::sin(pan_radian);
if (pan <= 0) {
*UNSAFE_TODO(destination_l++) =
static_cast<float>(input_l + input_r * gain_l);
*UNSAFE_TODO(destination_r++) = static_cast<float>(input_r * gain_r);
} else {
*UNSAFE_TODO(destination_l++) = static_cast<float>(input_l * gain_l);
*UNSAFE_TODO(destination_r++) =
static_cast<float>(input_r + input_l * gain_r);
}
}
}
}
void StereoPanner::PanToTargetValue(const AudioBus* input_bus,
AudioBus* output_bus,
float pan_value,
uint32_t frames_to_process) {
DCHECK(input_bus);
DCHECK_LE(frames_to_process, input_bus->length());
DCHECK_GE(input_bus->NumberOfChannels(), 1u);
DCHECK_LE(input_bus->NumberOfChannels(), 2u);
unsigned number_of_input_channels = input_bus->NumberOfChannels();
DCHECK(output_bus);
DCHECK_EQ(output_bus->NumberOfChannels(), 2u);
DCHECK_LE(frames_to_process, output_bus->length());
const float* source_l = input_bus->Channel(0)->Data();
const float* source_r =
number_of_input_channels > 1 ? input_bus->Channel(1)->Data() : source_l;
float* destination_l =
output_bus->ChannelByType(AudioBus::kChannelLeft)->MutableData();
float* destination_r =
output_bus->ChannelByType(AudioBus::kChannelRight)->MutableData();
if (!source_l || !source_r || !destination_l || !destination_r) {
return;
}
float target_pan = ClampTo(pan_value, -1.0, 1.0);
int n = frames_to_process;
if (number_of_input_channels == 1) {
double pan_radian = (target_pan * 0.5 + 0.5) * kPiOverTwoDouble;
double gain_l = fdlibm::cos(pan_radian);
double gain_r = fdlibm::sin(pan_radian);
while (n--) {
float input_l = *UNSAFE_TODO(source_l++);
*UNSAFE_TODO(destination_l++) = static_cast<float>(input_l * gain_l);
*UNSAFE_TODO(destination_r++) = static_cast<float>(input_l * gain_r);
}
} else {
double pan_radian =
(target_pan <= 0 ? target_pan + 1 : target_pan) * kPiOverTwoDouble;
double gain_l = fdlibm::cos(pan_radian);
double gain_r = fdlibm::sin(pan_radian);
while (n--) {
float input_l = *UNSAFE_TODO(source_l++);
float input_r = *UNSAFE_TODO(source_r++);
if (target_pan <= 0) {
*UNSAFE_TODO(destination_l++) =
static_cast<float>(input_l + input_r * gain_l);
*UNSAFE_TODO(destination_r++) = static_cast<float>(input_r * gain_r);
} else {
*UNSAFE_TODO(destination_l++) = static_cast<float>(input_l * gain_l);
*UNSAFE_TODO(destination_r++) =
static_cast<float>(input_r + input_l * gain_r);
}
}
}
}
}