blob: bf133ff9b8d9472474d3dad9160ad7500c563f9a [file] [log] [blame]
/*
* Copyright (C) 2017 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/platform_audio.h"
#include <cinttypes>
#include "chre/core/audio_util.h"
#include "chre/core/event_loop_manager.h"
#include "chre/platform/fatal_error.h"
#include "chre/platform/log.h"
#include "chre/platform/system_time.h"
#include "chre/util/dynamic_vector.h"
namespace chre {
namespace {
//! The list of audio sources provided by the simulator.
DynamicVector<UniquePtr<AudioSource>> gAudioSources;
} // namespace
PlatformAudio::PlatformAudio() {}
PlatformAudio::~PlatformAudio() {}
void PlatformAudio::init() {
// TODO: Implement this.
}
void audioSourceCallback(void *cookie) {
auto *audioSource = static_cast<AudioSource *>(cookie);
auto &dataEvent = audioSource->dataEvent;
Nanoseconds samplingTime = AudioUtil::getDurationFromSampleCountAndRate(
audioSource->numSamples,
static_cast<uint32_t>(audioSource->audioInfo.samplerate));
dataEvent.timestamp =
(SystemTime::getMonotonicTime() - samplingTime).toRawNanoseconds();
dataEvent.sampleCount = audioSource->numSamples;
if (dataEvent.format == CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM) {
uint32_t intervalNumSamples = AudioUtil::getSampleCountFromRateAndDuration(
static_cast<uint32_t>(audioSource->audioInfo.samplerate),
audioSource->eventDelay);
if (intervalNumSamples > audioSource->numSamples) {
sf_count_t seekAmount = intervalNumSamples - audioSource->numSamples;
sf_seek(audioSource->audioFile, -seekAmount, SEEK_CUR);
}
sf_count_t readCount = sf_read_short(
audioSource->audioFile, const_cast<int16_t *>(dataEvent.samplesS16),
static_cast<sf_count_t>(dataEvent.sampleCount));
if (readCount != dataEvent.sampleCount) {
LOGI("TODO: File done, suspend the source");
} else {
EventLoopManagerSingleton::get()
->getAudioRequestManager()
.handleAudioDataEvent(&audioSource->dataEvent);
}
} else {
FATAL_ERROR("Unimplemented data format");
}
}
void PlatformAudio::setHandleEnabled(uint32_t handle, bool enabled) {
// TODO: Implement this.
}
bool PlatformAudio::requestAudioDataEvent(uint32_t handle, uint32_t numSamples,
Nanoseconds eventDelay) {
LOGD("Request for audio data made for handle %" PRIu32 " with %" PRIu32
" samples and %" PRIu64 " delivery interval",
handle, numSamples, eventDelay.toRawNanoseconds());
auto &source = gAudioSources[handle];
source->numSamples = numSamples;
source->eventDelay = eventDelay;
return source->timer.set(audioSourceCallback, source.get(), eventDelay);
}
void PlatformAudio::cancelAudioDataEventRequest(uint32_t handle) {
LOGD("Cancelling audio request for handle %" PRIu32, handle);
auto &source = gAudioSources[handle];
source->timer.cancel();
}
void PlatformAudio::releaseAudioDataEvent(struct chreAudioDataEvent *event) {
// TODO(P1-41459d): Implement this API in terms of libsndfile.
}
size_t PlatformAudio::getSourceCount() {
return gAudioSources.size();
}
bool PlatformAudio::getAudioSource(uint32_t handle,
chreAudioSource *audioSource) const {
bool success = (handle < gAudioSources.size());
if (success) {
const auto &source = gAudioSources[handle];
// TODO(P1-b9ff35): Ensure that name never exceeds 40 bytes in length.
audioSource->name = source->audioFilename.c_str();
audioSource->sampleRate =
static_cast<uint32_t>(source->audioInfo.samplerate);
audioSource->minBufferDuration =
source->minBufferDuration.toRawNanoseconds();
audioSource->maxBufferDuration =
source->maxBufferDuration.toRawNanoseconds();
audioSource->format = source->dataEvent.format;
}
return success;
}
void PlatformAudioBase::addAudioSource(UniquePtr<AudioSource> &source) {
LOGI("Adding audio source - filename: %s, min buf size: %" PRIu64
"ms, max buf size: %" PRIu64 "ms",
source->audioFilename.c_str(),
Milliseconds(source->minBufferDuration).getMilliseconds(),
Milliseconds(source->maxBufferDuration).getMilliseconds());
auto &audioInfo = source->audioInfo;
source->audioFile =
sf_open(source->audioFilename.c_str(), SFM_READ, &audioInfo);
auto sampleCount = AudioUtil::getSampleCountFromRateAndDuration(
static_cast<uint32_t>(source->audioInfo.samplerate),
source->maxBufferDuration);
if (source->audioFile == nullptr) {
FATAL_ERROR("Failed to open provided audio file %s",
source->audioFilename.c_str());
} else if ((audioInfo.format & SF_FORMAT_ULAW) == SF_FORMAT_ULAW) {
source->dataEvent.format = CHRE_AUDIO_DATA_FORMAT_8_BIT_U_LAW;
source->dataEvent.samplesULaw8 =
static_cast<uint8_t *>(malloc(sizeof(uint8_t) * sampleCount));
} else if ((audioInfo.format & SF_FORMAT_PCM_16) == SF_FORMAT_PCM_16) {
source->dataEvent.format = CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM;
source->dataEvent.samplesS16 =
static_cast<int16_t *>(malloc(sizeof(uint16_t) * sampleCount));
} else {
FATAL_ERROR("Invalid format 0x%08x", audioInfo.format);
}
source->dataEvent.version = CHRE_AUDIO_DATA_EVENT_VERSION;
memset(source->dataEvent.reserved, 0, sizeof(source->dataEvent.reserved));
source->dataEvent.handle = static_cast<uint32_t>(gAudioSources.size());
source->dataEvent.sampleRate =
static_cast<uint32_t>(source->audioInfo.samplerate);
gAudioSources.push_back(std::move(source));
}
} // namespace chre