Merge pull request #17206 from RomanPudashkin/aux_channels

aux_channels
This commit is contained in:
RomanPudashkin 2023-04-12 17:32:53 +03:00 committed by GitHub
commit 96addbf260
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 213 additions and 68 deletions

View file

@ -45,6 +45,7 @@ using volume_db_t = float;
using volume_dbfs_t = float;
using gain_t = float;
using balance_t = float;
using audio_signal_amount_t = float;
using TrackSequenceId = int32_t;
using TrackSequenceIdList = std::vector<TrackSequenceId>;
@ -255,17 +256,31 @@ struct AudioFxParams {
using AudioFxChain = std::map<AudioFxChainOrder, AudioFxParams>;
struct AuxSendParams {
audio_signal_amount_t signalAmount = 0.f; // [0; 1]
bool active = false;
bool operator ==(const AuxSendParams& other) const
{
return RealIsEqual(signalAmount, other.signalAmount) && active == other.active;
}
};
using AuxSendsParams = std::map<TrackId, AuxSendParams>;
struct AudioOutputParams {
AudioFxChain fxChain;
volume_db_t volume = 0.f;
balance_t balance = 0.f;
AuxSendsParams auxSends;
bool muted = false;
bool operator ==(const AudioOutputParams& other) const
{
return fxChain == other.fxChain
&& volume == other.volume
&& balance == other.balance
&& RealIsEqual(volume, other.volume)
&& RealIsEqual(balance, other.balance)
&& auxSends == other.auxSends
&& muted == other.muted;
}
};

View file

@ -35,7 +35,7 @@ public:
virtual ~IGetTracks() = default;
virtual TrackPtr track(const TrackId id) const = 0;
virtual TracksMap allTracks() const = 0;
virtual const TracksMap& allTracks() const = 0;
virtual async::Channel<TrackPtr> trackAboutToBeAdded() const = 0;
virtual async::Channel<TrackPtr> trackAboutToBeRemoved() const = 0;

View file

@ -45,6 +45,9 @@ public:
virtual RetVal2<TrackId, AudioParams> addTrack(const std::string& trackName, io::IODevice* device,
const AudioParams& requiredParams) = 0;
virtual RetVal2<TrackId, AudioOutputParams> addAuxTrack(const std::string& trackName,
const AudioOutputParams& requiredOutputParams) = 0;
virtual TrackName trackName(const TrackId id) const = 0;
virtual TrackIdList trackIdList() const = 0;

View file

@ -73,14 +73,34 @@ RetVal<MixerChannelPtr> Mixer::addChannel(const TrackId trackId, IAudioSourcePtr
return result;
}
Ret Mixer::removeChannel(const TrackId id)
RetVal<MixerChannelPtr> Mixer::addAuxChannel(const TrackId trackId)
{
ONLY_AUDIO_WORKER_THREAD;
auto search = m_trackChannels.find(id);
m_auxChannels.emplace(trackId, std::make_shared<MixerChannel>(trackId, m_sampleRate));
RetVal<MixerChannelPtr> result;
result.val = m_auxChannels[trackId];
result.ret = make_ret(Ret::Code::Ok);
return result;
}
Ret Mixer::removeChannel(const TrackId trackId)
{
ONLY_AUDIO_WORKER_THREAD;
auto search = m_trackChannels.find(trackId);
if (search != m_trackChannels.end() && search->second) {
m_trackChannels.erase(id);
m_trackChannels.erase(trackId);
return make_ret(Ret::Code::Ok);
}
search = m_auxChannels.find(trackId);
if (search != m_auxChannels.end() && search->second) {
m_auxChannels.erase(trackId);
return make_ret(Ret::Code::Ok);
}
@ -135,7 +155,7 @@ samples_t Mixer::process(float* outBuffer, samples_t samplesPerChannel)
for (const auto& pair : m_trackChannels) {
MixerChannelPtr channel = pair.second;
std::future<std::vector<float> > future = TaskScheduler::instance()->submit([this, outBufferSize, samplesPerChannel,
std::future<std::vector<float> > future = TaskScheduler::instance()->submit([outBufferSize, samplesPerChannel,
channel]() -> std::vector<float> {
thread_local std::vector<float> buffer(outBufferSize, 0.f);
thread_local std::vector<float> silent_buffer(outBufferSize, 0.f);

View file

@ -46,7 +46,8 @@ public:
IAudioSourcePtr mixedSource();
RetVal<MixerChannelPtr> addChannel(const TrackId trackId, IAudioSourcePtr source);
Ret removeChannel(const TrackId id);
RetVal<MixerChannelPtr> addAuxChannel(const TrackId trackId);
Ret removeChannel(const TrackId trackId);
void setAudioChannelsCount(const audioch_t count);
@ -78,6 +79,8 @@ private:
std::vector<IFxProcessorPtr> m_masterFxProcessors = {};
std::map<TrackId, MixerChannelPtr> m_trackChannels = {};
std::map<TrackId, MixerChannelPtr> m_auxChannels = {};
dsp::LimiterPtr m_limiter = nullptr;
std::set<IClockPtr> m_clocks;

View file

@ -43,6 +43,12 @@ MixerChannel::MixerChannel(const TrackId trackId, IAudioSourcePtr source, const
setSampleRate(sampleRate);
}
MixerChannel::MixerChannel(const TrackId trackId, const unsigned int sampleRate)
: MixerChannel(trackId, nullptr, sampleRate)
{
ONLY_AUDIO_WORKER_THREAD;
}
const AudioOutputParams& MixerChannel::outputParams() const
{
return m_params;
@ -111,34 +117,26 @@ bool MixerChannel::isActive() const
{
ONLY_AUDIO_WORKER_THREAD;
IF_ASSERT_FAILED(m_audioSource) {
return false;
}
return m_audioSource->isActive();
return m_audioSource ? m_audioSource->isActive() : true;
}
void MixerChannel::setIsActive(bool arg)
{
ONLY_AUDIO_WORKER_THREAD;
IF_ASSERT_FAILED(m_audioSource) {
return;
if (m_audioSource) {
m_audioSource->setIsActive(arg);
}
m_audioSource->setIsActive(arg);
}
void MixerChannel::setSampleRate(unsigned int sampleRate)
{
ONLY_AUDIO_WORKER_THREAD;
IF_ASSERT_FAILED(m_audioSource) {
return;
if (m_audioSource) {
m_audioSource->setSampleRate(sampleRate);
}
m_audioSource->setSampleRate(sampleRate);
for (IFxProcessorPtr fx : m_fxProcessors) {
fx->setSampleRate(sampleRate);
}
@ -148,33 +146,25 @@ unsigned int MixerChannel::audioChannelsCount() const
{
ONLY_AUDIO_WORKER_THREAD;
IF_ASSERT_FAILED(m_audioSource) {
return 0;
}
return m_audioSource->audioChannelsCount();
return m_audioSource ? m_audioSource->audioChannelsCount() : 0;
}
async::Channel<unsigned int> MixerChannel::audioChannelsCountChanged() const
{
ONLY_AUDIO_WORKER_THREAD;
IF_ASSERT_FAILED(m_audioSource) {
return {};
}
return m_audioSource->audioChannelsCountChanged();
return m_audioSource ? m_audioSource->audioChannelsCountChanged() : async::Channel<unsigned int>();
}
samples_t MixerChannel::process(float* buffer, samples_t samplesPerChannel)
{
ONLY_AUDIO_WORKER_THREAD;
IF_ASSERT_FAILED(m_audioSource) {
return 0;
}
samples_t processedSamplesCount = samplesPerChannel;
samples_t processedSamplesCount = m_audioSource->process(buffer, samplesPerChannel);
if (m_audioSource) {
processedSamplesCount = m_audioSource->process(buffer, samplesPerChannel);
}
if (processedSamplesCount == 0 || m_params.muted) {
unsigned int channelsCount = audioChannelsCount();

View file

@ -38,6 +38,7 @@ class MixerChannel : public ITrackAudioOutput, public async::Asyncable
public:
explicit MixerChannel(const TrackId trackId, IAudioSourcePtr source, const unsigned int sampleRate);
explicit MixerChannel(const TrackId trackId, const unsigned int sampleRate);
const AudioOutputParams& outputParams() const override;
void applyOutputParams(const AudioOutputParams& requiredParams) override;

View file

@ -34,15 +34,11 @@ SequencePlayer::SequencePlayer(IGetTracks* getTracks, IClockPtr clock)
: m_getTracks(getTracks), m_clock(clock)
{
m_clock->seekOccurred().onNotify(this, [this]() {
for (auto& pair : tracks()) {
pair.second->inputHandler->seek(m_clock->currentTime());
}
seekAllTracks(m_clock->currentTime());
});
m_clock->statusChanged().onReceive(this, [this](const PlaybackStatus status) {
for (auto& pair : tracks()) {
pair.second->inputHandler->setIsActive(status == PlaybackStatus::Running);
}
setAllTracksActive(status == PlaybackStatus::Running);
});
}
@ -51,21 +47,16 @@ void SequencePlayer::play()
ONLY_AUDIO_WORKER_THREAD;
m_clock->start();
for (auto& pair : tracks()) {
pair.second->inputHandler->setIsActive(true);
}
setAllTracksActive(true);
}
void SequencePlayer::seek(const msecs_t newPositionMsecs)
{
ONLY_AUDIO_WORKER_THREAD;
m_clock->seek(newPositionMsecs * 1000);
for (auto& pair : tracks()) {
pair.second->inputHandler->seek(newPositionMsecs * 1000);
}
msecs_t newPos = newPositionMsecs * 1000;
m_clock->seek(newPos);
seekAllTracks(newPos);
}
void SequencePlayer::stop()
@ -73,10 +64,7 @@ void SequencePlayer::stop()
ONLY_AUDIO_WORKER_THREAD;
m_clock->stop();
for (auto& pair : tracks()) {
pair.second->inputHandler->setIsActive(false);
}
setAllTracksActive(false);
}
void SequencePlayer::pause()
@ -84,10 +72,7 @@ void SequencePlayer::pause()
ONLY_AUDIO_WORKER_THREAD;
m_clock->pause();
for (auto& pair : tracks()) {
pair.second->inputHandler->setIsActive(false);
}
setAllTracksActive(false);
}
void SequencePlayer::resume()
@ -95,10 +80,7 @@ void SequencePlayer::resume()
ONLY_AUDIO_WORKER_THREAD;
m_clock->resume();
for (auto& pair : tracks()) {
pair.second->inputHandler->setIsActive(true);
}
setAllTracksActive(true);
}
msecs_t SequencePlayer::duration() const
@ -147,11 +129,28 @@ Channel<PlaybackStatus> SequencePlayer::playbackStatusChanged() const
return m_clock->statusChanged();
}
TracksMap SequencePlayer::tracks() const
void SequencePlayer::setAllTracksActive(bool active)
{
IF_ASSERT_FAILED(m_getTracks) {
return {};
return;
}
return m_getTracks->allTracks();
for (const auto& pair : m_getTracks->allTracks()) {
if (pair.second->inputHandler) {
pair.second->inputHandler->setIsActive(active);
}
}
}
void SequencePlayer::seekAllTracks(const msecs_t newPositionMsecs)
{
IF_ASSERT_FAILED(m_getTracks) {
return;
}
for (const auto& pair : m_getTracks->allTracks()) {
if (pair.second->inputHandler) {
pair.second->inputHandler->seek(newPositionMsecs);
}
}
}

View file

@ -50,7 +50,8 @@ public:
async::Channel<PlaybackStatus> playbackStatusChanged() const override;
private:
TracksMap tracks() const;
void setAllTracksActive(bool active);
void seekAllTracks(const msecs_t newPositionMsecs);
IGetTracks* m_getTracks = nullptr;
IClockPtr m_clock = nullptr;

View file

@ -140,6 +140,37 @@ RetVal2<TrackId, AudioParams> TrackSequence::addTrack(const std::string& trackNa
return result;
}
RetVal2<TrackId, AudioOutputParams> TrackSequence::addAuxTrack(const std::string& trackName, const AudioOutputParams& requiredOutputParams)
{
ONLY_AUDIO_WORKER_THREAD;
RetVal2<TrackId, AudioOutputParams> result;
result.val1 = -1;
IF_ASSERT_FAILED(mixer()) {
result.ret = make_ret(Err::Undefined);
return result;
}
TrackId newId = newTrackId();
EventTrackPtr trackPtr = std::make_shared<EventTrack>();
trackPtr->id = newId;
trackPtr->name = trackName;
trackPtr->outputHandler = mixer()->addAuxChannel(newId).val;
trackPtr->setOutputParams(requiredOutputParams);
m_trackAboutToBeAdded.send(trackPtr);
m_tracks.emplace(newId, trackPtr);
m_trackAdded.send(newId);
result.ret = make_ret(Err::NoError);
result.val1 = newId;
result.val2 = trackPtr->outputParams();
return result;
}
TrackName TrackSequence::trackName(const TrackId id) const
{
TrackPtr trackPtr = track(id);
@ -232,7 +263,7 @@ TrackPtr TrackSequence::track(const TrackId id) const
return nullptr;
}
TracksMap TrackSequence::allTracks() const
const TracksMap& TrackSequence::allTracks() const
{
ONLY_AUDIO_WORKER_THREAD;

View file

@ -46,6 +46,8 @@ public:
const AudioParams& requiredParams) override;
RetVal2<TrackId, AudioParams> addTrack(const std::string& trackName, io::IODevice* device, const AudioParams& requiredParams) override;
RetVal2<TrackId, AudioOutputParams> addAuxTrack(const std::string& trackName, const AudioOutputParams& requiredOutputParams) override;
TrackName trackName(const TrackId id) const override;
TrackIdList trackIdList() const override;
@ -60,7 +62,7 @@ public:
// IGetTracks
TrackPtr track(const TrackId id) const override;
TracksMap allTracks() const override;
const TracksMap& allTracks() const override;
async::Channel<TrackPtr> trackAboutToBeAdded() const override;
async::Channel<TrackPtr> trackAboutToBeRemoved() const override;

View file

@ -118,6 +118,28 @@ Promise<TrackId, AudioParams> TracksHandler::addTrack(const TrackSequenceId sequ
}, AudioThread::ID);
}
Promise<TrackId, AudioOutputParams> TracksHandler::addAuxTrack(const TrackSequenceId sequenceId, const std::string& trackName,
const AudioOutputParams& outputParams)
{
return Promise<TrackId, AudioOutputParams>([this, sequenceId, trackName, outputParams](auto resolve, auto reject) {
ONLY_AUDIO_WORKER_THREAD;
ITrackSequencePtr s = sequence(sequenceId);
if (!s) {
return reject(static_cast<int>(Err::InvalidSequenceId), "invalid sequence id");
}
RetVal2<TrackId, AudioOutputParams> result = s->addAuxTrack(trackName, outputParams);
if (!result.ret) {
return reject(result.ret.code(), result.ret.text());
}
return resolve(result.val1, result.val2);
}, AudioThread::ID);
}
void TracksHandler::removeTrack(const TrackSequenceId sequenceId, const TrackId trackId)
{
Async::call(this, [this, sequenceId, trackId]() {

View file

@ -40,10 +40,16 @@ public:
async::Promise<TrackIdList> trackIdList(const TrackSequenceId sequenceId) const override;
async::Promise<TrackName> trackName(const TrackSequenceId sequenceId, const TrackId trackId) const override;
async::Promise<TrackId, AudioParams> addTrack(const TrackSequenceId sequenceId, const std::string& trackName,
io::IODevice* playbackData, AudioParams&& params) override;
async::Promise<TrackId, AudioParams> addTrack(const TrackSequenceId sequenceId, const std::string& trackName,
const mpe::PlaybackData& playbackData, AudioParams&& params) override;
async::Promise<TrackId, AudioOutputParams> addAuxTrack(const TrackSequenceId sequenceId, const std::string& trackName,
const AudioOutputParams& outputParams) override;
void removeTrack(const TrackSequenceId sequenceId, const TrackId trackId) override;
void removeAllTracks(const TrackSequenceId sequenceId) override;

View file

@ -40,10 +40,15 @@ public:
virtual async::Promise<TrackIdList> trackIdList(const TrackSequenceId sequenceId) const = 0;
virtual async::Promise<TrackName> trackName(const TrackSequenceId sequenceId, const TrackId trackId) const = 0;
virtual async::Promise<TrackId, AudioParams> addTrack(const TrackSequenceId sequenceId, const std::string& trackName,
io::IODevice* playbackData, AudioParams&& params) = 0;
virtual async::Promise<TrackId, AudioParams> addTrack(const TrackSequenceId sequenceId, const std::string& trackName,
const mpe::PlaybackData& playbackData, AudioParams&& params) = 0;
virtual async::Promise<TrackId, AudioOutputParams> addAuxTrack(const TrackSequenceId sequenceId, const std::string& trackName,
const AudioOutputParams& outputParams) = 0;
virtual void removeTrack(const TrackSequenceId sequenceId, const TrackId trackId) = 0;
virtual void removeAllTracks(const TrackSequenceId sequenceId) = 0;

View file

@ -266,6 +266,7 @@ AudioOutputParams ProjectAudioSettings::outputParamsFromJson(const QJsonObject&
result.fxChain = fxChainFromJson(object.value("fxChain").toObject());
result.balance = object.value("balance").toVariant().toFloat();
result.volume = object.value("volumeDb").toVariant().toFloat();
result.auxSends = auxSendsFromJson(object.value("auxSends").toObject());
return result;
}
@ -302,6 +303,27 @@ AudioFxParams ProjectAudioSettings::fxParamsFromJson(const QJsonObject& object)
return result;
}
AuxSendsParams ProjectAudioSettings::auxSendsFromJson(const QJsonObject& object) const
{
AuxSendsParams result;
for (const QString& key : object.keys()) {
AuxSendParams params = auxSendParamsFromJson(object.value(key).toObject());
result.emplace(static_cast<TrackId>(key.toInt()), std::move(params));
}
return result;
}
AuxSendParams ProjectAudioSettings::auxSendParamsFromJson(const QJsonObject& object) const
{
AuxSendParams result;
result.signalAmount = object.value("signalAmount").toVariant().toFloat();
result.active = object.value("active").toBool();
return result;
}
AudioResourceMeta ProjectAudioSettings::resourceMetaFromJson(const QJsonObject& object) const
{
AudioResourceMeta result;
@ -352,6 +374,7 @@ QJsonObject ProjectAudioSettings::outputParamsToJson(const audio::AudioOutputPar
result.insert("fxChain", fxChainToJson(params.fxChain));
result.insert("balance", params.balance);
result.insert("volumeDb", params.volume);
result.insert("auxSends", auxSendsToJson(params.auxSends));
return result;
}
@ -376,6 +399,26 @@ QJsonObject ProjectAudioSettings::fxChainToJson(const audio::AudioFxChain& fxCha
return result;
}
QJsonObject ProjectAudioSettings::auxSendsToJson(const audio::AuxSendsParams& auxSends) const
{
QJsonObject result;
for (const auto& pair : auxSends) {
result.insert(QString::number(static_cast<int>(pair.first)), auxSendParamsToJson(pair.second));
}
return result;
}
QJsonObject ProjectAudioSettings::auxSendParamsToJson(const audio::AuxSendParams& auxParams) const
{
QJsonObject result;
result.insert("active", auxParams.active);
result.insert("signalAmount", auxParams.signalAmount);
return result;
}
QJsonObject ProjectAudioSettings::fxParamsToJson(const audio::AudioFxParams& fxParams) const
{
QJsonObject result;

View file

@ -74,6 +74,8 @@ private:
SoloMuteState soloMuteStateFromJson(const QJsonObject& object) const;
audio::AudioFxChain fxChainFromJson(const QJsonObject& fxChainObject) const;
audio::AudioFxParams fxParamsFromJson(const QJsonObject& object) const;
audio::AuxSendsParams auxSendsFromJson(const QJsonObject& object) const;
audio::AuxSendParams auxSendParamsFromJson(const QJsonObject& object) const;
audio::AudioResourceMeta resourceMetaFromJson(const QJsonObject& object) const;
audio::AudioUnitConfig unitConfigFromJson(const QJsonObject& object) const;
audio::AudioResourceAttributes attributesFromJson(const QJsonObject& object) const;
@ -83,6 +85,8 @@ private:
QJsonObject soloMuteStateToJson(const SoloMuteState& state) const;
QJsonObject fxChainToJson(const audio::AudioFxChain& fxChain) const;
QJsonObject fxParamsToJson(const audio::AudioFxParams& fxParams) const;
QJsonObject auxSendsToJson(const audio::AuxSendsParams& auxSends) const;
QJsonObject auxSendParamsToJson(const audio::AuxSendParams& auxParams) const;
QJsonObject resourceMetaToJson(const audio::AudioResourceMeta& meta) const;
QJsonObject unitConfigToJson(const audio::AudioUnitConfig& config) const;
QJsonObject attributesToJson(const audio::AudioResourceAttributes& attributes) const;