/* * 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 #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> gAudioSources; } // namespace PlatformAudio::PlatformAudio() {} PlatformAudio::~PlatformAudio() {} void PlatformAudio::init() { // TODO: Implement this. } void audioSourceCallback(void *cookie) { auto *audioSource = static_cast(cookie); auto &dataEvent = audioSource->dataEvent; Nanoseconds samplingTime = AudioUtil::getDurationFromSampleCountAndRate( audioSource->numSamples, static_cast(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(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(dataEvent.samplesS16), static_cast(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(source->audioInfo.samplerate); audioSource->minBufferDuration = source->minBufferDuration.toRawNanoseconds(); audioSource->maxBufferDuration = source->maxBufferDuration.toRawNanoseconds(); audioSource->format = source->dataEvent.format; } return success; } void PlatformAudioBase::addAudioSource(UniquePtr &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(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(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(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(gAudioSources.size()); source->dataEvent.sampleRate = static_cast(source->audioInfo.samplerate); gAudioSources.push_back(std::move(source)); } } // namespace chre