blob: 3f7c842b70dde27fcb490555abb8fedfbd61a5f7 [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AudioEffect.h"
#include <logging_macros.h>
#include <climits>
#include <cstring>
#include <audio_common.h>
/*
* Mixing Audio in integer domain to avoid FP calculation
* (FG * ( MixFactor * 16 ) + BG * ( (1.0f-MixFactor) * 16 )) / 16
*/
static const int32_t kFloatToIntMapFactor = 128;
AudioMixer::AudioMixer() :
AudioFormat(48000, 2, oboe::AudioFormat::I16) {
busy_ = false;
bgMixFactorInt_ = (int32_t)
(bgMixFactor_ * kFloatToIntMapFactor + 0.5f);
fgMixFactorInt_ = kFloatToIntMapFactor - bgMixFactorInt_;
}
/**
* destructor: release memory for audio samples
*/
AudioMixer::~AudioMixer() {
}
/**
* Set mix factor for the 2 streams( background and forground );
* blending:
* recordedAudio * fgMix +
* backgroundMusic * ( 1.0f - fgMix )
* @param fgMix is background music mixer
*/
void AudioMixer::setBackgroundMixer(float mixer) {
if (mixer >= 0.0f && mixer <= 1.0f) {
bgMixFactor_ = mixer;
bgMixFactorInt_ = (int32_t)
(bgMixFactor_ * kFloatToIntMapFactor + 0.5f);
fgMixFactorInt_ = kFloatToIntMapFactor - bgMixFactorInt_;
}
}
/**
* Insert a raw PCM audio buffer to blend with live audio
*
* @param samples points to PCM audio buffer
* @param sampleCount is total samples pointed by samples
* @param channelCount channels for PCM audio pointed by samples
* @param freq is PCM audio frequency (48000hz for this sample)
*/
void AudioMixer::addStream(std::unique_ptr<int16_t[]>samples, size_t sampleCount,
int32_t sampleRate, int32_t channelCount, oboe::AudioFormat format){
if (busy_) {
LOGW("filtering in progress, filter configuration is IGNORED");
return;
}
bgAudio_ = std::move(samples);
bgAudioSampleCount_ = sampleCount;
sampleRate_ = sampleRate;
format_ = format;
channelCount_ = channelCount;
curPosition_ = 0;
}
/**
* Adding audio processing into the live audio
* @param liveAudio is recorded audio
* @param samplesPerFrame is same as channelCount.
* @param numFrames represents frames pointed by liveAudio
* total samples = numFrames * samplesPerFrame
*/
void AudioMixer::process(int16_t *liveAudio, int32_t channelCount,
int32_t numFrames) {
assert(bgAudio_ && liveAudio);
if (numFrames > bgAudioSampleCount_ || channelCount != channelCount_ ||
bgMixFactorInt_ == 0) {
return;
}
busy_ = true;
int32_t curSample;
for (int i = 0; i < (numFrames * channelCount); i++) {
curSample = liveAudio[i];
curSample = curSample * fgMixFactorInt_ +
bgAudio_[curPosition_] * bgMixFactorInt_;
curSample /= kFloatToIntMapFactor;
curSample = (curSample > SHRT_MAX ? SHRT_MAX : curSample);
liveAudio[i] = (int16_t)(curSample < SHRT_MIN ? SHRT_MIN : curSample);
curPosition_ = (curPosition_ + 1 ) % bgAudioSampleCount_;
}
busy_ = false;
}
/**
* query for audio format supportability
*/
bool AudioMixer::AudioFormatSupported(int32_t frequency,
int32_t channels, oboe::AudioFormat format) const {
return (frequency == sampleRate_ &&
channels == channelCount_ &&
format == format_);
}
/**
* Constructor for AudioDelay
* @param sampleRate
* @param channelCount
* @param format
* @param delay
* @param decay
*/
AudioDelay::AudioDelay(int32_t sampleRate,
int32_t channelCount,
oboe::AudioFormat format,
float delay,
float decay) :
AudioFormat(sampleRate, channelCount, format),
delay_(delay), decay_(decay) {
feedbackFactor_ = static_cast<int32_t>(decay_ * kFloatToIntMapFactor);
liveAudioFactor_ = kFloatToIntMapFactor - feedbackFactor_;
allocateBuffer();
}
/**
* Destructor
*/
AudioDelay::~AudioDelay() {
if(buffer_) delete static_cast<uint8_t*>(buffer_);
}
/**
* Configure for delay time ( in miliseconds ). It is possible to dynamically
* adjust the value
* @param delay in seconds
* @return true if delay time is set successfully
*/
bool AudioDelay::setDelay(float delay) {
float delta = delay - delay_;
if ( delta > -0.022f && delta < 0.022f) {
return true;
}
std::lock_guard<std::mutex> lock(lock_);
if(buffer_) {
delete static_cast<uint8_t*>(buffer_);
buffer_ = nullptr;
}
delay_ = delay;
allocateBuffer();
return buffer_ != nullptr;
}
/**
* Internal helper function to allocate buffer for the delay
* - calculate the buffer size for the delay time
* - allocate and zero out buffer (0 means silent audio)
* - configure bufSize_ to be size of audioFrames
*/
void AudioDelay::allocateBuffer(void) {
float fNumFrames = sampleRate_ * delay_;
size_t sampleCount = static_cast<uint32_t>(fNumFrames + 0.5f) * channelCount_;
uint32_t bytePerSample = SampleFormatToBpp(format_) / 8;
assert(bytePerSample <= 4);
uint32_t bytePerFrame = channelCount_ * bytePerSample;
// get bufCapacity in bytes
bufCapacity_ = sampleCount * bytePerSample;
bufCapacity_ = ((bufCapacity_ + bytePerFrame - 1) / bytePerFrame) * bytePerFrame;
buffer_ = new uint8_t[bufCapacity_];
assert(buffer_);
memset(buffer_, 0, bufCapacity_);
curPos_ = 0;
// bufSize_ is in Frames ( not samples, not bytes )
bufSize_ = bufCapacity_ / bytePerFrame;
}
float AudioDelay::getDelay(void) const {
return delay_;
}
/**
* SetFeedbackRatio(): set the decay factor
* ratio: value of 0.0 -- 1.0f;
*
* the calculation is in integer ( not in float )
* for performance purpose
*/
void AudioDelay::setDecay(float decay) {
if (decay > 0.0f && decay < 1.0f) {
float feedback = (decay * kFloatToIntMapFactor + 0.5f);
feedbackFactor_ = static_cast<int32_t>(feedback);
liveAudioFactor_ = kFloatToIntMapFactor - feedbackFactor_;
}
}
float AudioDelay::getDecay(float) const {
return decay_;
}
/**
* process() filter live audio with "echo" effect:
* delay time is run-time adjustable
* decay time could also be adjustable, but not used
* in this sample, hardcoded to .5
*
* @param liveAudio is recorded audio stream
* @param channelCount for liveAudio, must be 2 for stereo
* @param numFrames is length of liveAudio in Frames ( not in byte )
*/
void AudioDelay::process(int16_t *liveAudio,
int32_t channelCount,
int32_t numFrames) {
if (feedbackFactor_ == 0 ||
channelCount != channelCount_ ||
bufSize_ < numFrames) {
return;
}
if(!lock_.try_lock()) {
return;
}
if (numFrames + curPos_ > bufSize_) {
curPos_ = 0;
}
// process every sample
int32_t sampleCount = channelCount * numFrames;
int16_t* samples = & static_cast<int16_t*>(buffer_)[curPos_];
int32_t curSample;
for (size_t idx = 0; idx < sampleCount; idx++) {
#if 1
curSample = (samples[idx] * feedbackFactor_ +
liveAudio[idx] * liveAudioFactor_) / kFloatToIntMapFactor;
#else
curSample = (samples[idx] * feedbackFactor_) / kFloatToIntMapFactor +
liveAudio[idx];
#endif
if(curSample > SHRT_MAX)
curSample = SHRT_MAX;
else if (curSample < SHRT_MIN)
curSample = SHRT_MAX;
samples[idx] = static_cast<int16_t>(curSample);
liveAudio[idx] = samples[idx];
}
curPos_ += numFrames;
lock_.unlock();
}