blob: 0c2f5401ee93981b00894e489c2ab783279bad31 [file] [log] [blame]
/*
* Copyright (C) 2020 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 "chre/platform/aoc/audio_filter.h"
#include "chre/core/event_loop_manager.h"
#include "chre/platform/assert.h"
#include "chre/platform/log.h"
#include "chre/platform/system_time.h"
#include "chre/util/time.h"
#include "efw/include/ring_buffer_ipc.h"
namespace chre {
AudioFilter::AudioFilter()
: Filter("CHRE_FLT" /* name */, EFWObject::Root() /* parent */,
tskIDLE_PRIORITY + 5 /* priority */, 512 /* stack size words */,
1 /* command depth */, 1 /* Num Filter inputs */,
1 /* Num Filter Outputs */, 1 /* Num Ring Buffers */) {
LOGV("Filter Created");
TaskSpawn();
}
int AudioFilter::StartCallback() {
mRingBufferHandle = ring_[kRingIndex]->ReaderRegister();
CHRE_ASSERT(mRingBufferHandle != nullptr);
return 0;
}
bool AudioFilter::StopCallback() {
ring_[kRingIndex]->ReaderUnregister(mRingBufferHandle);
mRingBufferHandle = nullptr;
return true;
}
bool AudioFilter::SignalProcessor(int index) {
switch (index) {
case kBufferReleasedSignalIndex:
OnBufferReleased();
break;
default:
break;
}
return true;
}
void AudioFilter::InputProcessor(int /*pin*/, void *message, size_t size) {
CHRE_ASSERT(message != nullptr);
if (!mBufferInUse) {
// TODO resync and synchronization, check metadata format etc.
auto *metadata = static_cast<struct AudioInputMetadata *>(message);
if (metadata->need_resync) {
LOGW("Resync request received, logic not implemented yet");
}
constexpr size_t kBytesPerAocSample = sizeof(uint32_t);
constexpr size_t kNumSamplesAoc = 160;
constexpr size_t kRingSize = kNumSamplesAoc * kBytesPerAocSample *
1; // 1 channel of u32 samples per 10 ms
uint32_t buffer[kNumSamplesAoc];
uint32_t nBytes =
ring_[kRingIndex]->Read(mRingBufferHandle, &buffer, kRingSize);
CHRE_ASSERT_LOG(nBytes != 0, "Got data pipe notif, but no data in ring");
size_t nSamples = nBytes / kBytesPerAocSample;
if (mSampleCount == 0) {
// TODO: Get a better estimate of the timestamp of the first sample in
// the frame. Since the pipe notification arrives every 10ms, we could
// subtract 10ms from now(), and maybe even keep track of the embedded
// timestamp of the first sample of the current frame, and the last
// sample of the previous frame.
mDataEvent.timestamp = SystemTime::getMonotonicTime().toRawNanoseconds();
}
// TODO: For the initial implementation/testing, we assume that the
// frame might not fit to a T in our current buffer, but that is the
// expectation. With the current logic, we could end up dropping a frame,
// revisit this to make sure we capture the exact number of samples,
// while continuing to buffer samples either via a ping-pong scheme, or
// a small extra scratch space.
if ((nSamples + mSampleCount) > kSamplesPer2Sec) {
mDataEvent.sampleCount = mSampleCount;
mDataEvent.samplesS16 = mSampleBuffer;
mSampleCount = 0;
mBufferInUse = true;
EventLoopManagerSingleton::get()
->getAudioRequestManager()
.handleAudioDataEvent(&mDataEvent);
} else {
for (size_t i = 0; i < nSamples; ++i, ++mSampleCount) {
// The zeroth byte of the received sample is a timestamp, which we
// strip out.
// TODO: Verify that there's no scaling on the remaining data, in
// which case casting without descaling won't be the right thing to
// do.
mSampleBuffer[mSampleCount] =
static_cast<int16_t>((buffer[i] >> 8) & 0xff);
}
}
} else {
LOGW(
"Received an audio notification before the previous event was "
"released!");
}
}
void AudioFilter::OnBufferReleased() {
mBufferInUse = false;
}
} // namespace chre