Add emulated device ports
Bug: 137005763
Test: boot emulator, make a phone call
Signed-off-by: Roman Kiryanov <rkir@google.com>
Change-Id: Ibbb2574cf2e0d72a0bba6933a2425e7c153e9c38
Merged-In: Ibbb2574cf2e0d72a0bba6933a2425e7c153e9c38
diff --git a/audio/Android.bp b/audio/Android.bp
index b4bea6b..668a1e7 100644
--- a/audio/Android.bp
+++ b/audio/Android.bp
@@ -36,6 +36,7 @@
"android.hardware.audio@6.0",
"android.hardware.audio.common@6.0",
"android.hardware.audio.common@6.0-util",
+ "libaudioutils",
"libbase",
"libcutils",
"libhidlbase",
diff --git a/audio/device_port_sink.cpp b/audio/device_port_sink.cpp
index 4adde3c..8c17b6d 100644
--- a/audio/device_port_sink.cpp
+++ b/audio/device_port_sink.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <log/log.h>
#include "device_port_sink.h"
#include "talsa.h"
#include "util.h"
@@ -67,20 +68,63 @@
}
}
+private:
const uint32_t mSampleRateHz;
util::StreamPosition mPos;
talsa::PcmPtr mPcm;
};
+struct NullSink : public DevicePortSink {
+ explicit NullSink(const AudioConfig &cfg)
+ : mSampleRateHz(cfg.sampleRateHz)
+ , mNChannels(util::countChannels(cfg.channelMask)) {}
+
+ Result getPresentationPosition(uint64_t &frames, TimeSpec &ts) const override {
+ nsecs_t t = 0;
+ mPos.now(mSampleRateHz, frames, t);
+ ts.tvSec = ns2s(t);
+ ts.tvNSec = t - s2ns(ts.tvSec);
+ return Result::OK;
+ }
+
+ int write(const void *, size_t nBytes) override {
+ mPos.addFrames(nBytes / mNChannels / sizeof(int16_t));
+ return nBytes;
+ }
+
+ static std::unique_ptr<NullSink> create(const AudioConfig &cfg) {
+ return std::make_unique<NullSink>(cfg);
+ }
+
+private:
+ const unsigned mSampleRateHz;
+ const unsigned mNChannels;
+ util::StreamPosition mPos;
+};
+
} // namespace
std::unique_ptr<DevicePortSink>
DevicePortSink::create(const DeviceAddress &address,
const AudioConfig &cfg,
const hidl_bitfield<AudioOutputFlag> &flags) {
- (void)address;
(void)flags;
- return TinyalsaSink::create(talsa::kPcmCard, talsa::kPcmDevice, cfg);
+
+ if (cfg.format != AudioFormat::PCM_16_BIT) {
+ ALOGE("%s:%d Only PCM_16_BIT is supported", __func__, __LINE__);
+ return nullptr;
+ }
+
+ switch (address.device) {
+ case AudioDevice::OUT_SPEAKER:
+ return TinyalsaSink::create(talsa::kPcmCard, talsa::kPcmDevice, cfg);
+
+ case AudioDevice::OUT_TELEPHONY_TX:
+ return NullSink::create(cfg);
+
+ default:
+ return nullptr;
+ }
}
} // namespace implementation
diff --git a/audio/device_port_source.cpp b/audio/device_port_source.cpp
index f3350e3..b084db6 100644
--- a/audio/device_port_source.cpp
+++ b/audio/device_port_source.cpp
@@ -14,10 +14,16 @@
* 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 <cmath>
namespace android {
namespace hardware {
@@ -27,31 +33,43 @@
namespace {
-struct TinyalsaSource : public DevicePortSource {
+struct CapturePositionDevicePortSource : public DevicePortSource {
+ explicit CapturePositionDevicePortSource(unsigned sampleRateHz)
+ : mSampleRateHz(sampleRateHz)
+ , mStartNs(systemTime(SYSTEM_TIME_MONOTONIC)) {}
+
+ Result getCapturePosition(uint64_t &frames, uint64_t &time) const override {
+ const nsecs_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC);
+ frames = getNowFrames(nowNs);
+ time = uint64_t(nowNs);
+ return Result::OK;
+ }
+
+ uint64_t getNowFrames(const nsecs_t nowNs) const {
+ return uint64_t(mSampleRateHz) * ns2ms(nowNs - mStartNs) / 1000;
+ }
+
+protected:
+ const unsigned mSampleRateHz;
+ const nsecs_t mStartNs;
+};
+
+struct TinyalsaSource : public CapturePositionDevicePortSource {
TinyalsaSource(unsigned pcmCard, unsigned pcmDevice, const AudioConfig &cfg)
- : sampleRateHz(cfg.sampleRateHz)
+ : CapturePositionDevicePortSource(cfg.sampleRateHz)
, pcm(talsa::pcmOpen(pcmCard, pcmDevice,
util::countChannels(cfg.channelMask),
cfg.sampleRateHz,
cfg.frameCount,
false /* isOut */)) {}
- Result getCapturePosition(uint64_t &frames, uint64_t &time) const override {
- nsecs_t t = 0;
- pos.now(sampleRateHz, frames, t);
- time = t;
- return Result::OK;
- }
-
int read(void *data, size_t toReadBytes) override {
const int res = ::pcm_read(pcm.get(), data, toReadBytes);
if (res < 0) {
return res;
} else if (res == 0) {
- pos.addFrames(::pcm_bytes_to_frames(pcm.get(), toReadBytes));
return toReadBytes;
} else {
- pos.addFrames(::pcm_bytes_to_frames(pcm.get(), res));
return res;
}
}
@@ -67,20 +85,177 @@
}
}
- const uint32_t sampleRateHz;
- util::StreamPosition pos;
+private:
talsa::PcmPtr pcm;
};
+template <class G> struct GeneratedSource : public CapturePositionDevicePortSource {
+ GeneratedSource(const AudioConfig &cfg, G generator)
+ : CapturePositionDevicePortSource(cfg.sampleRateHz)
+ , mNChannels(util::countChannels(cfg.channelMask))
+ , mGenerator(std::move(generator)) {}
+
+ 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:
+ const unsigned mNChannels;
+ 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, G generator) {
+ return std::make_unique<GeneratedSource<G>>(cfg, std::move(generator));
+}
+
} // namespace
std::unique_ptr<DevicePortSource>
DevicePortSource::create(const DeviceAddress &address,
const AudioConfig &cfg,
const hidl_bitfield<AudioOutputFlag> &flags) {
- (void)address;
(void)flags;
- return TinyalsaSource::create(talsa::kPcmCard, talsa::kPcmDevice, cfg);
+
+ if (cfg.format != AudioFormat::PCM_16_BIT) {
+ ALOGE("%s:%d Only PCM_16_BIT is supported", __func__, __LINE__);
+ return nullptr;
+ }
+
+ switch (address.device) {
+ case AudioDevice::IN_BUILTIN_MIC:
+ return TinyalsaSource::create(talsa::kPcmCard, talsa::kPcmDevice, cfg);
+
+ case AudioDevice::IN_TELEPHONY_RX:
+ return createGeneratedSource(cfg, BusySignalGenerator(cfg.sampleRateHz));
+
+ case AudioDevice::IN_FM_TUNER:
+ return createGeneratedSource(
+ cfg, RepeatGenerator(generateSinePattern(cfg.sampleRateHz, 440.0, 1.0)));
+
+ default:
+ return nullptr;
+ }
}
} // namespace implementation
diff --git a/audio/policy/primary_audio_policy_configuration.xml b/audio/policy/primary_audio_policy_configuration.xml
index f9ac2c3..33a47d7 100644
--- a/audio/policy/primary_audio_policy_configuration.xml
+++ b/audio/policy/primary_audio_policy_configuration.xml
@@ -4,6 +4,9 @@
<attachedDevices>
<item>Speaker</item>
<item>Built-In Mic</item>
+ <item>Telephony Tx</item>
+ <item>Telephony Rx</item>
+ <item>FM Tuner</item>
</attachedDevices>
<defaultOutputDevice>Speaker</defaultOutputDevice>
<mixPorts>
@@ -15,18 +18,47 @@
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="44100" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>
+
+ <mixPort name="telephony_tx" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="44100" channelMasks="AUDIO_CHANNEL_OUT_MONO,AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="telephony_rx" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="44100" channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+
+ <mixPort name="fm_tuner" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="44100" channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
</mixPorts>
<devicePorts>
<devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
</devicePort>
+ <devicePort tagName="Telephony Tx" type="AUDIO_DEVICE_OUT_TELEPHONY_TX" role="sink">
+ </devicePort>
<devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
</devicePort>
+ <devicePort tagName="Telephony Rx" type="AUDIO_DEVICE_IN_TELEPHONY_RX" role="source">
+ </devicePort>
+
+ <devicePort tagName="FM Tuner" type="AUDIO_DEVICE_IN_FM_TUNER" role="source">
+ </devicePort>
</devicePorts>
<routes>
<route type="mix" sink="Speaker"
sources="primary output"/>
<route type="mix" sink="primary input"
sources="Built-In Mic"/>
+
+ <route type="mix" sink="telephony_rx"
+ sources="Telephony Rx"/>
+ <route type="mix" sink="Telephony Tx"
+ sources="telephony_tx"/>
+
+ <route type="mix" sink="fm_tuner"
+ sources="FM Tuner"/>
</routes>
</module>