blob: a82d281f5fc67d9b4b9a35fc052601dcab2c5e58 [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 <cmath>
#include <chrono>
#include <thread>
#include <audio_utils/channels.h>
#include <audio_utils/format.h>
#include <log/log.h>
#include <utils/Timers.h>
#include "device_port_source.h"
#include "talsa.h"
#include "util.h"
#include "debug.h"
namespace android {
namespace hardware {
namespace audio {
namespace V6_0 {
namespace implementation {
namespace {
struct TinyalsaSource : public DevicePortSource {
TinyalsaSource(unsigned pcmCard, unsigned pcmDevice,
const AudioConfig &cfg, uint64_t &frames)
: mFrames(frames)
, mPcm(talsa::pcmOpen(pcmCard, pcmDevice,
util::countChannels(cfg.channelMask),
cfg.sampleRateHz,
cfg.frameCount,
false /* isOut */)) {}
Result getCapturePosition(uint64_t &frames, uint64_t &time) override {
frames = mFrames;
time = systemTime(SYSTEM_TIME_MONOTONIC);
return Result::OK;
}
int read(void *data, size_t toReadBytes) override {
const int res = ::pcm_read(mPcm.get(), data, toReadBytes);
if (res < 0) {
return FAILURE(res);
} else if (res == 0) {
mFrames += ::pcm_bytes_to_frames(mPcm.get(), toReadBytes);
return toReadBytes;
} else {
mFrames += ::pcm_bytes_to_frames(mPcm.get(), res);
return res;
}
}
static std::unique_ptr<TinyalsaSource> create(unsigned pcmCard,
unsigned pcmDevice,
const AudioConfig &cfg,
uint64_t &frames) {
auto src = std::make_unique<TinyalsaSource>(pcmCard, pcmDevice, cfg, frames);
if (src->mPcm) {
return src;
} else {
return FAILURE(nullptr);
}
}
private:
uint64_t &mFrames;
talsa::PcmPtr mPcm;
};
template <class G> struct GeneratedSource : public DevicePortSource {
GeneratedSource(const AudioConfig &cfg, uint64_t &frames, G generator)
: 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 = getNowFrames(nowNs);
mFrames += (nowFrames - mPreviousFrames);
mPreviousFrames = nowFrames;
frames = mFrames;
time = nowNs;
return Result::OK;
}
uint64_t getNowFrames(const nsecs_t nowNs) const {
return uint64_t(mSampleRateHz) * ns2ms(nowNs - mStartNs) / 1000;
}
int read(void *data, size_t toReadBytes) override {
int16_t *samples = static_cast<int16_t *>(data);
const unsigned nChannels = mNChannels;
const unsigned requestedFrames = toReadBytes / nChannels / sizeof(*samples);
unsigned availableFrames;
while (true) {
const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
availableFrames = getNowFrames(nowNs) - mSentFrames;
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;
return sizeBytes;
}
private:
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, uint64_t &frames, G generator) {
return std::make_unique<GeneratedSource<G>>(cfg, frames, std::move(generator));
}
} // namespace
std::unique_ptr<DevicePortSource>
DevicePortSource::create(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, frames);
case AudioDevice::IN_TELEPHONY_RX:
return createGeneratedSource(cfg, frames,
BusySignalGenerator(cfg.sampleRateHz));
case AudioDevice::IN_FM_TUNER:
return createGeneratedSource(
cfg, 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