| /* | 
 |  * 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 <cmath> | 
 | #include <chrono> | 
 | #include <thread> | 
 | #include <unistd.h> | 
 | #include <audio_utils/channels.h> | 
 | #include <audio_utils/format.h> | 
 | #include <log/log.h> | 
 | #include <utils/ThreadDefs.h> | 
 | #include <utils/Timers.h> | 
 | #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<int16_t *>(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<uint8_t> 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<TinyalsaSource> create(unsigned pcmCard, | 
 |                                                   unsigned pcmDevice, | 
 |                                                   const AudioConfig &cfg, | 
 |                                                   size_t writerBufferSizeHint, | 
 |                                                   uint64_t &frames) { | 
 |         (void)writerBufferSizeHint; | 
 |  | 
 |         auto src = std::make_unique<TinyalsaSource>(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<uint32_t> mFramesLost = 0; | 
 |     RingBuffer mRingBuffer; | 
 |     talsa::Mixer mMixer; | 
 |     talsa::PcmPtr mPcm; | 
 |     std::thread mProduceThread; | 
 |     std::atomic<bool> mProduceThreadRunning = true; | 
 | }; | 
 |  | 
 | template <class G> 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<int16_t> 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<int16_t> convertFloatsToInt16(const std::vector<float> &pcmFloat) { | 
 |     std::vector<int16_t> 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<float> 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<int16_t> mValues; | 
 |     size_t mI = 0; | 
 | }; | 
 |  | 
 | struct RepeatGenerator { | 
 |     explicit RepeatGenerator(const std::vector<float> &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<int16_t> mValues; | 
 |     size_t mI = 0; | 
 | }; | 
 |  | 
 | std::vector<float> generateSinePattern(uint32_t sampleRateHz, | 
 |                                        double freq, | 
 |                                        double amp) { | 
 |     std::vector<float> 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 <class G> std::unique_ptr<GeneratedSource<G>> | 
 | createGeneratedSource(const AudioConfig &cfg, | 
 |                       size_t writerBufferSizeHint, | 
 |                       uint64_t &frames, | 
 |                       G generator) { | 
 |     return std::make_unique<GeneratedSource<G>>(cfg, | 
 |                                                 writerBufferSizeHint, | 
 |                                                 frames, | 
 |                                                 std::move(generator)); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | std::unique_ptr<DevicePortSource> | 
 | DevicePortSource::create(size_t writerBufferSizeHint, | 
 |                          const DeviceAddress &address, | 
 |                          const AudioConfig &cfg, | 
 |                          const hidl_bitfield<AudioOutputFlag> &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 |