/* * 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 #include #include #include #include #include #include #include #include #include "device_port_source.h" #include "talsa.h" #include "ring_buffer.h" #include "audio_ops.h" #include "util.h" #include "debug.h" namespace android { namespace hardware { namespace audio { namespace V6_0 { namespace implementation { namespace { constexpr int kMaxJitterUs = 3000; // Enforced by CTS, should be <= 6ms struct TinyalsaSource : public DevicePortSource { TinyalsaSource(unsigned pcmCard, unsigned pcmDevice, const AudioConfig &cfg, uint64_t &frames) : mStartNs(systemTime(SYSTEM_TIME_MONOTONIC)) , mSampleRateHz(cfg.sampleRateHz) , mFrameSize(util::countChannels(cfg.channelMask) * sizeof(int16_t)) , mReadSizeFrames(cfg.frameCount) , mFrames(frames) , mRingBuffer(mFrameSize * cfg.frameCount * 3) , mMixer(pcmCard) , mPcm(talsa::pcmOpen(pcmCard, pcmDevice, util::countChannels(cfg.channelMask), cfg.sampleRateHz, cfg.frameCount, false /* isOut */)) { mProduceThread = std::thread(&TinyalsaSource::producerThread, this); } ~TinyalsaSource() { mProduceThreadRunning = false; mProduceThread.join(); } Result getCapturePosition(uint64_t &frames, uint64_t &time) override { const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC); const uint64_t nowFrames = getCaptureFrames(nowNs); mFrames += (nowFrames - mPreviousFrames); mPreviousFrames = nowFrames; frames = mFrames; time = nowNs; return Result::OK; } uint64_t getCaptureFrames(const nsecs_t nowNs) const { return uint64_t(mSampleRateHz) * ns2us(nowNs - mStartNs) / 1000000; } uint64_t getAvailableFrames(const nsecs_t nowNs) const { return getCaptureFrames(nowNs) - mSentFrames; } uint64_t getAvailableFramesNow() const { return getAvailableFrames(systemTime(SYSTEM_TIME_MONOTONIC)); } size_t getWaitFramesNow(const size_t requestedFrames) const { const size_t availableFrames = getAvailableFramesNow(); return (requestedFrames > availableFrames) ? (requestedFrames - availableFrames) : 0; } size_t read(float volume, size_t bytesToRead, IWriter &writer) override { const size_t waitFrames = getWaitFramesNow(bytesToRead / mFrameSize); const auto blockUntil = std::chrono::high_resolution_clock::now() + + std::chrono::microseconds(waitFrames * 1000000 / mSampleRateHz); while (bytesToRead > 0) { if (mRingBuffer.waitForConsumeAvailable(blockUntil + std::chrono::microseconds(kMaxJitterUs))) { if (mRingBuffer.availableToConsume() >= bytesToRead) { // Since the ring buffer has all bytes we need, make sure we // are not too early here: tinyalsa is jittery, we don't // want to go faster than SYSTEM_TIME_MONOTONIC std::this_thread::sleep_until(blockUntil); } auto chunk = mRingBuffer.getConsumeChunk(); const size_t writeBufSzBytes = std::min(chunk.size, bytesToRead); aops::multiplyByVolume(volume, static_cast(chunk.data), writeBufSzBytes / sizeof(int16_t)); writer(chunk.data, writeBufSzBytes); LOG_ALWAYS_FATAL_IF(mRingBuffer.consume(chunk, writeBufSzBytes) < writeBufSzBytes); bytesToRead -= writeBufSzBytes; mSentFrames += writeBufSzBytes / mFrameSize; } else { ALOGW("TinyalsaSource::%s:%d pcm_read was late delivering " "frames, inserting %zu us of silence", __func__, __LINE__, size_t(1000000 * bytesToRead / mFrameSize / mSampleRateHz)); static const uint8_t zeroes[256] = {0}; while (bytesToRead > 0) { const size_t nZeroFrames = std::min(bytesToRead, sizeof(zeroes)) / mFrameSize; const size_t nZeroBytes = nZeroFrames * mFrameSize; writer(zeroes, nZeroBytes); mSentFrames += nZeroFrames; bytesToRead -= nZeroBytes; } break; } } return mFramesLost.exchange(0); } void producerThread() { util::setThreadPriority(PRIORITY_URGENT_AUDIO); std::vector readBuf(mReadSizeFrames * mFrameSize); while (mProduceThreadRunning) { const size_t bytesLost = mRingBuffer.makeRoomForProduce(readBuf.size()); mFramesLost += bytesLost / mFrameSize; auto produceChunk = mRingBuffer.getProduceChunk(); if (produceChunk.size < readBuf.size()) { const size_t sz = doRead(readBuf.data(), readBuf.size()); if (sz > 0) { LOG_ALWAYS_FATAL_IF(mRingBuffer.produce(readBuf.data(), sz) < sz); } } else { const size_t sz = doRead(produceChunk.data, readBuf.size()); if (sz > 0) { LOG_ALWAYS_FATAL_IF(mRingBuffer.produce(readBuf.size()) < sz); } } } } size_t doRead(void *dst, size_t sz) { const int res = ::pcm_read(mPcm.get(), dst, sz); if (res < 0) { ALOGW("TinyalsaSource::%s:%d pcm_read failed with res=%d", __func__, __LINE__, res); return 0; } return sz; } static std::unique_ptr create(unsigned pcmCard, unsigned pcmDevice, const AudioConfig &cfg, size_t writerBufferSizeHint, uint64_t &frames) { (void)writerBufferSizeHint; auto src = std::make_unique(pcmCard, pcmDevice, cfg, frames); if (src->mMixer && src->mPcm) { return src; } else { return FAILURE(nullptr); } } private: const nsecs_t mStartNs; const unsigned mSampleRateHz; const unsigned mFrameSize; const unsigned mReadSizeFrames; uint64_t &mFrames; uint64_t mPreviousFrames = 0; uint64_t mSentFrames = 0; std::atomic mFramesLost = 0; RingBuffer mRingBuffer; talsa::Mixer mMixer; talsa::PcmPtr mPcm; std::thread mProduceThread; std::atomic mProduceThreadRunning = true; }; template struct GeneratedSource : public DevicePortSource { GeneratedSource(const AudioConfig &cfg, size_t writerBufferSizeHint, uint64_t &frames, G generator) : mWriteBuffer(writerBufferSizeHint / sizeof(int16_t)) , mFrames(frames) , mStartNs(systemTime(SYSTEM_TIME_MONOTONIC)) , mSampleRateHz(cfg.sampleRateHz) , mNChannels(util::countChannels(cfg.channelMask)) , mGenerator(std::move(generator)) {} Result getCapturePosition(uint64_t &frames, uint64_t &time) override { const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC); const uint64_t nowFrames = getCaptureFrames(nowNs); mFrames += (nowFrames - mPreviousFrames); mPreviousFrames = nowFrames; frames = mFrames; time = nowNs; return Result::OK; } uint64_t getCaptureFrames(const nsecs_t nowNs) const { return uint64_t(mSampleRateHz) * ns2us(nowNs - mStartNs) / 1000000; } uint64_t getAvailableFrames(const nsecs_t nowNs) const { return getCaptureFrames(nowNs) - mSentFrames; } size_t read(float volume, size_t bytesToRead, IWriter &writer) override { mWriteBuffer.resize(bytesToRead / sizeof(int16_t)); int16_t *samples = mWriteBuffer.data(); const unsigned nChannels = mNChannels; const unsigned requestedFrames = bytesToRead / nChannels / sizeof(*samples); unsigned availableFrames; while (true) { const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC); availableFrames = getAvailableFrames(nowNs); if (availableFrames < requestedFrames / 2) { const unsigned neededMoreFrames = requestedFrames / 2 - availableFrames; using namespace std::chrono_literals; std::this_thread::sleep_for(1s * neededMoreFrames / mSampleRateHz); } else { break; } } const unsigned nFrames = std::min(requestedFrames, availableFrames); mGenerator(samples, nFrames); const size_t sizeBytes = nFrames * nChannels * sizeof(*samples); if (nChannels > 1) { adjust_channels(samples, 1, samples, nChannels, sizeof(*samples), sizeBytes); } mSentFrames += nFrames; aops::multiplyByVolume(volume, mWriteBuffer.data(), sizeBytes / sizeof(int16_t)); writer(mWriteBuffer.data(), sizeBytes); return 0; } private: std::vector mWriteBuffer; uint64_t &mFrames; const nsecs_t mStartNs; const unsigned mSampleRateHz; const unsigned mNChannels; uint64_t mPreviousFrames = 0; uint64_t mSentFrames = 0; G mGenerator; }; std::vector convertFloatsToInt16(const std::vector &pcmFloat) { std::vector pcmI16(pcmFloat.size()); memcpy_by_audio_format(pcmI16.data(), AUDIO_FORMAT_PCM_16_BIT, pcmFloat.data(), AUDIO_FORMAT_PCM_FLOAT, pcmFloat.size()); return pcmI16; } // https://en.wikipedia.org/wiki/Busy_signal struct BusySignalGenerator { explicit BusySignalGenerator(const uint32_t sampleRateHz) : mSampleRateHz(sampleRateHz) { // 24/480 = 31/620, mValues must contain 50ms of audio samples const size_t sz = sampleRateHz / 20; std::vector pcm(sz); for (unsigned i = 0; i < sz; ++i) { const double a = double(i) * M_PI * 2 / sampleRateHz; pcm[i] = .5 * (sin(480 * a) + sin(620 * a)); } mValues = convertFloatsToInt16(pcm); } void operator()(int16_t* s, size_t n) { const unsigned rate = mSampleRateHz; const unsigned rateHalf = rate / 2; const int16_t *const vals = mValues.data(); const size_t valsSz = mValues.size(); size_t i = mI; while (n > 0) { size_t len; if (i < rateHalf) { const size_t valsOff = i % valsSz; len = std::min(n, std::min(rateHalf - i, valsSz - valsOff)); memcpy(s, vals + valsOff, len * sizeof(*s)); } else { len = std::min(n, rate - i); memset(s, 0, len * sizeof(*s)); } s += len; i = (i + len) % rate; n -= len; } mI = i; } private: const unsigned mSampleRateHz; std::vector mValues; size_t mI = 0; }; struct RepeatGenerator { explicit RepeatGenerator(const std::vector &pcm) : mValues(convertFloatsToInt16(pcm)) {} void operator()(int16_t* s, size_t n) { const int16_t *const vals = mValues.data(); const size_t valsSz = mValues.size(); size_t i = mI; while (n > 0) { const size_t len = std::min(n, valsSz - i); memcpy(s, vals + i, len * sizeof(*s)); s += len; i = (i + len) % valsSz; n -= len; } mI = i; } private: const std::vector mValues; size_t mI = 0; }; std::vector generateSinePattern(uint32_t sampleRateHz, double freq, double amp) { std::vector result(3 * sampleRateHz / freq + .5); for (size_t i = 0; i < result.size(); ++i) { const double a = double(i) * M_PI * 2 / sampleRateHz; result[i] = amp * sin(a * freq); } return result; } template std::unique_ptr> createGeneratedSource(const AudioConfig &cfg, size_t writerBufferSizeHint, uint64_t &frames, G generator) { return std::make_unique>(cfg, writerBufferSizeHint, frames, std::move(generator)); } } // namespace std::unique_ptr DevicePortSource::create(size_t writerBufferSizeHint, const DeviceAddress &address, const AudioConfig &cfg, const hidl_bitfield &flags, uint64_t &frames) { (void)flags; if (cfg.format != AudioFormat::PCM_16_BIT) { ALOGE("%s:%d Only PCM_16_BIT is supported", __func__, __LINE__); return FAILURE(nullptr); } switch (address.device) { case AudioDevice::IN_BUILTIN_MIC: return TinyalsaSource::create(talsa::kPcmCard, talsa::kPcmDevice, cfg, writerBufferSizeHint, frames); case AudioDevice::IN_TELEPHONY_RX: return createGeneratedSource(cfg, writerBufferSizeHint, frames, BusySignalGenerator(cfg.sampleRateHz)); case AudioDevice::IN_FM_TUNER: return createGeneratedSource( cfg, writerBufferSizeHint, frames, RepeatGenerator(generateSinePattern(cfg.sampleRateHz, 440.0, 1.0))); default: ALOGE("%s:%d unsupported device: %x", __func__, __LINE__, address.device); return FAILURE(nullptr); } } } // namespace implementation } // namespace V6_0 } // namespace audio } // namespace hardware } // namespace android