110
include/AudioBusHandle.h
Normal file
110
include/AudioBusHandle.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* AudioBusHandle.h - ThreadableJob between PlayHandle and MixerChannel
|
||||
*
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2025 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LMMS_AUDIO_BUS_HANDLE_H
|
||||
#define LMMS_AUDIO_BUS_HANDLE_H
|
||||
|
||||
#include <memory>
|
||||
#include <QString>
|
||||
#include <QMutex>
|
||||
|
||||
#include "PlayHandle.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class EffectChain;
|
||||
class FloatModel;
|
||||
class BoolModel;
|
||||
|
||||
/**
|
||||
@brief Job between @ref PlayHandle and @ref MixerChannel
|
||||
|
||||
A @ref ThreadableJob class located at the exit point of each @ref PlayHandle into a @ref MixerChannel
|
||||
(or into an audio device, in case of @ref AudioJack, but this is not supported in AudioBusHandle yet).
|
||||
It contains an optional @ref EffectChain which is e.g. visualized in the
|
||||
@ref InstrumentTrackWindow or @ref SampleTrackWindow.
|
||||
For processing, it adds all input play handles into an internal buffer,
|
||||
processes the @ref EffectChain (if existing) on that buffer
|
||||
and finally merges the buffer into its @ref MixerChannel.
|
||||
*/
|
||||
class AudioBusHandle : public ThreadableJob
|
||||
{
|
||||
public:
|
||||
AudioBusHandle(const QString& name, bool hasEffectChain = true,
|
||||
FloatModel* volumeModel = nullptr, FloatModel* panningModel = nullptr,
|
||||
BoolModel* mutedModel = nullptr);
|
||||
virtual ~AudioBusHandle();
|
||||
|
||||
SampleFrame* buffer() { return m_buffer; }
|
||||
|
||||
// indicate whether JACK & Co should provide output-buffer at ext. port
|
||||
bool extOutputEnabled() const { return m_extOutputEnabled; }
|
||||
void setExtOutputEnabled(bool enabled);
|
||||
|
||||
// next mixer-channel after this audio-bus-handle
|
||||
// (-1 = none 0 = master)
|
||||
mix_ch_t nextMixerChannel() const { return m_nextMixerChannel; }
|
||||
void setNextMixerChannel(const mix_ch_t chnl) { m_nextMixerChannel = chnl; }
|
||||
|
||||
const QString& name() const { return m_name; }
|
||||
void setName(const QString& newName);
|
||||
|
||||
EffectChain* effects() { return m_effects.get(); }
|
||||
bool processEffects();
|
||||
|
||||
// ThreadableJob stuff
|
||||
void doProcessing() override;
|
||||
bool requiresProcessing() const override { return true; }
|
||||
|
||||
void addPlayHandle(PlayHandle* handle);
|
||||
void removePlayHandle(PlayHandle* handle);
|
||||
|
||||
private:
|
||||
volatile bool m_bufferUsage;
|
||||
|
||||
SampleFrame* const m_buffer;
|
||||
|
||||
bool m_extOutputEnabled;
|
||||
mix_ch_t m_nextMixerChannel;
|
||||
|
||||
QString m_name;
|
||||
|
||||
std::unique_ptr<EffectChain> m_effects;
|
||||
|
||||
PlayHandleList m_playHandles;
|
||||
QMutex m_playHandleLock;
|
||||
|
||||
FloatModel* m_volumeModel;
|
||||
FloatModel* m_panningModel;
|
||||
BoolModel* m_mutedModel;
|
||||
|
||||
friend class AudioEngine;
|
||||
friend class AudioEngineWorkerThread;
|
||||
};
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_AUDIO_BUS_HANDLE_H
|
||||
@@ -36,7 +36,7 @@ namespace lmms
|
||||
{
|
||||
|
||||
class AudioEngine;
|
||||
class AudioPort;
|
||||
class AudioBusHandle;
|
||||
class SampleFrame;
|
||||
|
||||
|
||||
@@ -57,13 +57,13 @@ public:
|
||||
}
|
||||
|
||||
|
||||
// if audio-driver supports ports, classes inherting AudioPort
|
||||
// if audio-driver supports ports, classes inherting AudioBusHandle
|
||||
// (e.g. channel-tracks) can register themselves for making
|
||||
// audio-driver able to collect their individual output and provide
|
||||
// them at a specific port - currently only supported by JACK
|
||||
virtual void registerPort( AudioPort * _port );
|
||||
virtual void unregisterPort( AudioPort * _port );
|
||||
virtual void renamePort( AudioPort * _port );
|
||||
virtual void registerPort(AudioBusHandle* port);
|
||||
virtual void unregisterPort(AudioBusHandle* port);
|
||||
virtual void renamePort(AudioBusHandle* port);
|
||||
|
||||
inline bool supportsCapture() const
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace lmms
|
||||
|
||||
class AudioDevice;
|
||||
class MidiClient;
|
||||
class AudioPort;
|
||||
class AudioBusHandle;
|
||||
class AudioEngineWorkerThread;
|
||||
|
||||
|
||||
@@ -172,15 +172,15 @@ public:
|
||||
}
|
||||
|
||||
|
||||
// audio-port-stuff
|
||||
inline void addAudioPort(AudioPort * port)
|
||||
// audio-bus-handle-stuff
|
||||
inline void addAudioBusHandle(AudioBusHandle* busHandle)
|
||||
{
|
||||
requestChangeInModel();
|
||||
m_audioPorts.push_back(port);
|
||||
m_audioBusHandles.push_back(busHandle);
|
||||
doneChangeInModel();
|
||||
}
|
||||
|
||||
void removeAudioPort(AudioPort * port);
|
||||
void removeAudioBusHandle(AudioBusHandle* busHandle);
|
||||
|
||||
|
||||
// MIDI-client-stuff
|
||||
@@ -366,7 +366,7 @@ private:
|
||||
|
||||
bool m_renderOnly;
|
||||
|
||||
std::vector<AudioPort *> m_audioPorts;
|
||||
std::vector<AudioBusHandle*> m_audioBusHandles;
|
||||
|
||||
fpp_t m_framesPerPeriod;
|
||||
|
||||
|
||||
@@ -36,9 +36,15 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#ifdef AUDIO_BUS_HANDLE_SUPPORT
|
||||
#include <QMap>
|
||||
#endif
|
||||
|
||||
#include "AudioDevice.h"
|
||||
#include "AudioDeviceSetupWidget.h"
|
||||
#ifdef AUDIO_BUS_HANDLE_SUPPORT
|
||||
#include "AudioBusHandle.h"
|
||||
#endif
|
||||
|
||||
class QLineEdit;
|
||||
|
||||
@@ -94,9 +100,9 @@ private:
|
||||
void startProcessing() override;
|
||||
void stopProcessing() override;
|
||||
|
||||
void registerPort(AudioPort* port) override;
|
||||
void unregisterPort(AudioPort* port) override;
|
||||
void renamePort(AudioPort* port) override;
|
||||
void registerPort(AudioBusHandle* port) override;
|
||||
void unregisterPort(AudioBusHandle* port) override;
|
||||
void renamePort(AudioBusHandle* port) override;
|
||||
|
||||
int processCallback(jack_nframes_t nframes);
|
||||
|
||||
@@ -116,13 +122,13 @@ private:
|
||||
f_cnt_t m_framesDoneInCurBuf;
|
||||
f_cnt_t m_framesToDoInCurBuf;
|
||||
|
||||
#ifdef AUDIO_PORT_SUPPORT
|
||||
#ifdef AUDIO_BUS_HANDLE_SUPPORT
|
||||
struct StereoPort
|
||||
{
|
||||
jack_port_t* ports[2];
|
||||
};
|
||||
|
||||
using JackPortMap = QMap<AudioPort*, StereoPort>;
|
||||
using JackPortMap = QMap<AudioBusHandle*, StereoPort>;
|
||||
JackPortMap m_portMap;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* AudioPort.h - base-class for objects providing sound at a port
|
||||
*
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LMMS_AUDIO_PORT_H
|
||||
#define LMMS_AUDIO_PORT_H
|
||||
|
||||
#include <memory>
|
||||
#include <QString>
|
||||
#include <QMutex>
|
||||
|
||||
#include "PlayHandle.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class EffectChain;
|
||||
class FloatModel;
|
||||
class BoolModel;
|
||||
|
||||
class AudioPort : public ThreadableJob
|
||||
{
|
||||
public:
|
||||
AudioPort( const QString & _name, bool _has_effect_chain = true,
|
||||
FloatModel * volumeModel = nullptr, FloatModel * panningModel = nullptr,
|
||||
BoolModel * mutedModel = nullptr );
|
||||
virtual ~AudioPort();
|
||||
|
||||
inline SampleFrame* buffer()
|
||||
{
|
||||
return m_portBuffer;
|
||||
}
|
||||
|
||||
inline void lockBuffer()
|
||||
{
|
||||
m_portBufferLock.lock();
|
||||
}
|
||||
|
||||
inline void unlockBuffer()
|
||||
{
|
||||
m_portBufferLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
// indicate whether JACK & Co should provide output-buffer at ext. port
|
||||
inline bool extOutputEnabled() const
|
||||
{
|
||||
return m_extOutputEnabled;
|
||||
}
|
||||
|
||||
void setExtOutputEnabled( bool _enabled );
|
||||
|
||||
|
||||
// next mixer-channel after this audio-port
|
||||
// (-1 = none 0 = master)
|
||||
inline mix_ch_t nextMixerChannel() const
|
||||
{
|
||||
return m_nextMixerChannel;
|
||||
}
|
||||
|
||||
inline EffectChain * effects()
|
||||
{
|
||||
return m_effects.get();
|
||||
}
|
||||
|
||||
void setNextMixerChannel( const mix_ch_t _chnl )
|
||||
{
|
||||
m_nextMixerChannel = _chnl;
|
||||
}
|
||||
|
||||
|
||||
const QString & name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void setName( const QString & _new_name );
|
||||
|
||||
|
||||
bool processEffects();
|
||||
|
||||
// ThreadableJob stuff
|
||||
void doProcessing() override;
|
||||
bool requiresProcessing() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void addPlayHandle( PlayHandle * handle );
|
||||
void removePlayHandle( PlayHandle * handle );
|
||||
|
||||
private:
|
||||
volatile bool m_bufferUsage;
|
||||
|
||||
SampleFrame* m_portBuffer;
|
||||
QMutex m_portBufferLock;
|
||||
|
||||
bool m_extOutputEnabled;
|
||||
mix_ch_t m_nextMixerChannel;
|
||||
|
||||
QString m_name;
|
||||
|
||||
std::unique_ptr<EffectChain> m_effects;
|
||||
|
||||
PlayHandleList m_playHandles;
|
||||
QMutex m_playHandleLock;
|
||||
|
||||
FloatModel * m_volumeModel;
|
||||
FloatModel * m_panningModel;
|
||||
BoolModel * m_mutedModel;
|
||||
|
||||
friend class AudioEngine;
|
||||
friend class AudioEngineWorkerThread;
|
||||
|
||||
} ;
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_AUDIO_PORT_H
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "AudioPort.h"
|
||||
#include "AudioBusHandle.h"
|
||||
#include "InstrumentFunctions.h"
|
||||
#include "InstrumentSoundShaping.h"
|
||||
#include "Microtuner.h"
|
||||
@@ -144,9 +144,9 @@ public:
|
||||
const Plugin::Descriptor::SubPluginFeatures::Key* key = nullptr,
|
||||
bool keyFromDnd = false);
|
||||
|
||||
AudioPort * audioPort()
|
||||
AudioBusHandle* audioBusHandle()
|
||||
{
|
||||
return &m_audioPort;
|
||||
return &m_audioBusHandle;
|
||||
}
|
||||
|
||||
MidiPort * midiPort()
|
||||
@@ -293,7 +293,7 @@ private:
|
||||
FloatModel m_volumeModel;
|
||||
FloatModel m_panningModel;
|
||||
|
||||
AudioPort m_audioPort;
|
||||
AudioBusHandle m_audioBusHandle;
|
||||
|
||||
FloatModel m_pitchModel;
|
||||
IntModel m_pitchRangeModel;
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace lmms
|
||||
{
|
||||
|
||||
class Track;
|
||||
class AudioPort;
|
||||
class AudioBusHandle;
|
||||
class SampleFrame;
|
||||
|
||||
class LMMS_EXPORT PlayHandle : public ThreadableJob
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
m_offset = p.m_offset;
|
||||
m_affinity = p.m_affinity;
|
||||
m_usesBuffer = p.m_usesBuffer;
|
||||
m_audioPort = p.m_audioPort;
|
||||
m_audioBusHandle = p.m_audioBusHandle;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -134,14 +134,14 @@ public:
|
||||
m_usesBuffer = b;
|
||||
}
|
||||
|
||||
AudioPort * audioPort()
|
||||
AudioBusHandle* audioBusHandle()
|
||||
{
|
||||
return m_audioPort;
|
||||
return m_audioBusHandle;
|
||||
}
|
||||
|
||||
void setAudioPort( AudioPort * port )
|
||||
void setAudioBusHandle(AudioBusHandle* busHandle)
|
||||
{
|
||||
m_audioPort = port;
|
||||
m_audioBusHandle = busHandle;
|
||||
}
|
||||
|
||||
void releaseBuffer();
|
||||
@@ -156,7 +156,7 @@ private:
|
||||
SampleFrame* m_playHandleBuffer;
|
||||
bool m_bufferReleased;
|
||||
bool m_usesBuffer;
|
||||
AudioPort * m_audioPort;
|
||||
AudioBusHandle* m_audioBusHandle;
|
||||
} ;
|
||||
|
||||
using PlayHandleList = QList<PlayHandle*>;
|
||||
|
||||
@@ -38,13 +38,13 @@ namespace lmms
|
||||
class PatternTrack;
|
||||
class SampleClip;
|
||||
class Track;
|
||||
class AudioPort;
|
||||
class AudioBusHandle;
|
||||
|
||||
|
||||
class LMMS_EXPORT SamplePlayHandle : public PlayHandle
|
||||
{
|
||||
public:
|
||||
SamplePlayHandle(Sample* sample, bool ownAudioPort = true);
|
||||
SamplePlayHandle(Sample* sample, bool ownAudioBusHandle = true);
|
||||
SamplePlayHandle( const QString& sampleFile );
|
||||
SamplePlayHandle( SampleClip* clip );
|
||||
~SamplePlayHandle() override;
|
||||
@@ -88,7 +88,7 @@ private:
|
||||
f_cnt_t m_frame;
|
||||
Sample::PlaybackState m_state;
|
||||
|
||||
const bool m_ownAudioPort;
|
||||
const bool m_ownAudioBusHandle;
|
||||
|
||||
FloatModel m_defaultVolumeModel;
|
||||
FloatModel * m_volumeModel;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#ifndef LMMS_SAMPLE_TRACK_H
|
||||
#define LMMS_SAMPLE_TRACK_H
|
||||
|
||||
#include "AudioPort.h"
|
||||
#include "AudioBusHandle.h"
|
||||
#include "Track.h"
|
||||
|
||||
|
||||
@@ -62,9 +62,9 @@ public:
|
||||
return &m_mixerChannelModel;
|
||||
}
|
||||
|
||||
inline AudioPort * audioPort()
|
||||
inline AudioBusHandle* audioBusHandle()
|
||||
{
|
||||
return &m_audioPort;
|
||||
return &m_audioBusHandle;
|
||||
}
|
||||
|
||||
QString nodeName() const override
|
||||
@@ -95,7 +95,7 @@ private:
|
||||
FloatModel m_volumeModel;
|
||||
FloatModel m_panningModel;
|
||||
IntModel m_mixerChannelModel;
|
||||
AudioPort m_audioPort;
|
||||
AudioBusHandle m_audioBusHandle;
|
||||
bool m_isPlaying;
|
||||
|
||||
|
||||
|
||||
254
src/core/AudioBusHandle.cpp
Normal file
254
src/core/AudioBusHandle.cpp
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* AudioBusHandle.cpp - ThreadableJob between PlayHandle and MixerChannel
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2025 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include "AudioBusHandle.h"
|
||||
#include "AudioDevice.h"
|
||||
#include "AudioEngine.h"
|
||||
#include "EffectChain.h"
|
||||
#include "Mixer.h"
|
||||
#include "Engine.h"
|
||||
#include "MixHelpers.h"
|
||||
#include "BufferManager.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
AudioBusHandle::AudioBusHandle(const QString& name, bool hasEffectChain,
|
||||
FloatModel* volumeModel, FloatModel* panningModel,
|
||||
BoolModel* mutedModel) :
|
||||
m_bufferUsage(false),
|
||||
m_buffer(BufferManager::acquire()),
|
||||
m_extOutputEnabled(false),
|
||||
m_nextMixerChannel(0),
|
||||
m_name(name),
|
||||
m_effects(hasEffectChain ? new EffectChain(nullptr) : nullptr),
|
||||
m_volumeModel(volumeModel),
|
||||
m_panningModel(panningModel),
|
||||
m_mutedModel(mutedModel)
|
||||
{
|
||||
Engine::audioEngine()->addAudioBusHandle(this);
|
||||
setExtOutputEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
AudioBusHandle::~AudioBusHandle()
|
||||
{
|
||||
setExtOutputEnabled(false);
|
||||
Engine::audioEngine()->removeAudioBusHandle(this);
|
||||
BufferManager::release(m_buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AudioBusHandle::setExtOutputEnabled(bool enabled)
|
||||
{
|
||||
if (enabled != m_extOutputEnabled)
|
||||
{
|
||||
m_extOutputEnabled = enabled;
|
||||
if (m_extOutputEnabled)
|
||||
{
|
||||
Engine::audioEngine()->audioDev()->registerPort(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Engine::audioEngine()->audioDev()->unregisterPort(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AudioBusHandle::setName(const QString& newName)
|
||||
{
|
||||
m_name = newName;
|
||||
Engine::audioEngine()->audioDev()->renamePort(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool AudioBusHandle::processEffects()
|
||||
{
|
||||
if (m_effects)
|
||||
{
|
||||
bool more = m_effects->processAudioBuffer(m_buffer, Engine::audioEngine()->framesPerPeriod(), m_bufferUsage);
|
||||
return more;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void AudioBusHandle::doProcessing()
|
||||
{
|
||||
if (m_mutedModel && m_mutedModel->value())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const fpp_t fpp = Engine::audioEngine()->framesPerPeriod();
|
||||
|
||||
// clear the buffer
|
||||
zeroSampleFrames(m_buffer, fpp);
|
||||
|
||||
//qDebug( "Playhandles: %d", m_playHandles.size() );
|
||||
for (PlayHandle* ph : m_playHandles) // now we mix all playhandle buffers into our internal buffer
|
||||
{
|
||||
if (ph->buffer())
|
||||
{
|
||||
if (ph->usesBuffer()
|
||||
&& (ph->type() == PlayHandle::Type::NotePlayHandle
|
||||
|| !MixHelpers::isSilent(ph->buffer(), fpp)))
|
||||
{
|
||||
m_bufferUsage = true;
|
||||
MixHelpers::add(m_buffer, ph->buffer(), fpp);
|
||||
}
|
||||
ph->releaseBuffer(); // gets rid of playhandle's buffer and sets
|
||||
// pointer to null, so if it doesn't get re-acquired we know to skip it next time
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bufferUsage)
|
||||
{
|
||||
// handle volume and panning
|
||||
// has both vol and pan models
|
||||
if (m_volumeModel && m_panningModel)
|
||||
{
|
||||
ValueBuffer* volBuf = m_volumeModel->valueBuffer();
|
||||
ValueBuffer* panBuf = m_panningModel->valueBuffer();
|
||||
|
||||
// both vol and pan have s.ex.data:
|
||||
if (volBuf && panBuf)
|
||||
{
|
||||
for (f_cnt_t f = 0; f < fpp; ++f)
|
||||
{
|
||||
float v = volBuf->values()[f] * 0.01f;
|
||||
float p = panBuf->values()[f] * 0.01f;
|
||||
m_buffer[f][0] *= (p <= 0 ? 1.0f : 1.0f - p) * v;
|
||||
m_buffer[f][1] *= (p >= 0 ? 1.0f : 1.0f + p) * v;
|
||||
}
|
||||
}
|
||||
|
||||
// only vol has s.ex.data:
|
||||
else if (volBuf)
|
||||
{
|
||||
float p = m_panningModel->value() * 0.01f;
|
||||
float l = (p <= 0 ? 1.0f : 1.0f - p);
|
||||
float r = (p >= 0 ? 1.0f : 1.0f + p);
|
||||
for (f_cnt_t f = 0; f < fpp; ++f)
|
||||
{
|
||||
float v = volBuf->values()[f] * 0.01f;
|
||||
m_buffer[f][0] *= v * l;
|
||||
m_buffer[f][1] *= v * r;
|
||||
}
|
||||
}
|
||||
|
||||
// only pan has s.ex.data:
|
||||
else if (panBuf)
|
||||
{
|
||||
float v = m_volumeModel->value() * 0.01f;
|
||||
for (f_cnt_t f = 0; f < fpp; ++f)
|
||||
{
|
||||
float p = panBuf->values()[f] * 0.01f;
|
||||
m_buffer[f][0] *= (p <= 0 ? 1.0f : 1.0f - p) * v;
|
||||
m_buffer[f][1] *= (p >= 0 ? 1.0f : 1.0f + p) * v;
|
||||
}
|
||||
}
|
||||
|
||||
// neither has s.ex.data:
|
||||
else
|
||||
{
|
||||
float p = m_panningModel->value() * 0.01f;
|
||||
float v = m_volumeModel->value() * 0.01f;
|
||||
for (f_cnt_t f = 0; f < fpp; ++f)
|
||||
{
|
||||
m_buffer[f][0] *= (p <= 0 ? 1.0f : 1.0f - p) * v;
|
||||
m_buffer[f][1] *= (p >= 0 ? 1.0f : 1.0f + p) * v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// has vol model only
|
||||
else if (m_volumeModel)
|
||||
{
|
||||
ValueBuffer* volBuf = m_volumeModel->valueBuffer();
|
||||
|
||||
if (volBuf)
|
||||
{
|
||||
for (f_cnt_t f = 0; f < fpp; ++f)
|
||||
{
|
||||
float v = volBuf->values()[f] * 0.01f;
|
||||
m_buffer[f][0] *= v;
|
||||
m_buffer[f][1] *= v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float v = m_volumeModel->value() * 0.01f;
|
||||
for (f_cnt_t f = 0; f < fpp; ++f)
|
||||
{
|
||||
m_buffer[f][0] *= v;
|
||||
m_buffer[f][1] *= v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// as of now there's no situation where we only have panning model but no volume model
|
||||
// if we have neither, we don't have to do anything here - just pass the audio as is
|
||||
|
||||
// handle effects
|
||||
const bool anyOutputAfterEffects = processEffects();
|
||||
if (anyOutputAfterEffects || m_bufferUsage)
|
||||
{
|
||||
Engine::mixer()->mixToChannel(m_buffer, m_nextMixerChannel); // send output to mixer
|
||||
// TODO: improve the flow here - convert to pull model
|
||||
m_bufferUsage = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioBusHandle::addPlayHandle(PlayHandle* handle)
|
||||
{
|
||||
QMutexLocker lockGuard(&m_playHandleLock);
|
||||
m_playHandles.append(handle);
|
||||
}
|
||||
|
||||
|
||||
void AudioBusHandle::removePlayHandle(PlayHandle* handle)
|
||||
{
|
||||
QMutexLocker lockGuard(&m_playHandleLock);
|
||||
PlayHandleList::Iterator it = std::find(m_playHandles.begin(), m_playHandles.end(), handle);
|
||||
if (it != m_playHandles.end())
|
||||
{
|
||||
m_playHandles.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lmms
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "lmmsconfig.h"
|
||||
|
||||
#include "AudioEngineWorkerThread.h"
|
||||
#include "AudioPort.h"
|
||||
#include "AudioBusHandle.h"
|
||||
#include "Mixer.h"
|
||||
#include "Song.h"
|
||||
#include "EnvelopeAndLfoParameters.h"
|
||||
@@ -299,13 +299,13 @@ void AudioEngine::renderStageNoteSetup()
|
||||
|
||||
if( it != m_playHandles.end() )
|
||||
{
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::Type::NotePlayHandle )
|
||||
(*it)->audioBusHandle()->removePlayHandle(*it);
|
||||
if((*it)->type() == PlayHandle::Type::NotePlayHandle)
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) *it );
|
||||
NotePlayHandleManager::release((NotePlayHandle*)*it);
|
||||
}
|
||||
else delete *it;
|
||||
m_playHandles.erase( it );
|
||||
m_playHandles.erase(it);
|
||||
}
|
||||
|
||||
it_rem = m_playHandlesToRemove.erase( it_rem );
|
||||
@@ -347,7 +347,7 @@ void AudioEngine::renderStageEffects()
|
||||
AudioEngineProfiler::Probe profilerProbe(m_profiler, AudioEngineProfiler::DetailType::Effects);
|
||||
|
||||
// STAGE 2: process effects of all instrument- and sampletracks
|
||||
AudioEngineWorkerThread::fillJobQueue(m_audioPorts);
|
||||
AudioEngineWorkerThread::fillJobQueue(m_audioBusHandles);
|
||||
AudioEngineWorkerThread::startAndWaitForJobs();
|
||||
|
||||
// removed all play handles which are done
|
||||
@@ -362,13 +362,13 @@ void AudioEngine::renderStageEffects()
|
||||
}
|
||||
if( ( *it )->isFinished() )
|
||||
{
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::Type::NotePlayHandle )
|
||||
(*it)->audioBusHandle()->removePlayHandle(*it);
|
||||
if((*it)->type() == PlayHandle::Type::NotePlayHandle)
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) *it );
|
||||
NotePlayHandleManager::release((NotePlayHandle*)*it);
|
||||
}
|
||||
else delete *it;
|
||||
it = m_playHandles.erase( it );
|
||||
it = m_playHandles.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -556,14 +556,14 @@ void AudioEngine::restoreAudioDevice()
|
||||
|
||||
|
||||
|
||||
void AudioEngine::removeAudioPort(AudioPort * port)
|
||||
void AudioEngine::removeAudioBusHandle(AudioBusHandle* busHandle)
|
||||
{
|
||||
requestChangeInModel();
|
||||
|
||||
auto it = std::find(m_audioPorts.begin(), m_audioPorts.end(), port);
|
||||
if (it != m_audioPorts.end())
|
||||
auto it = std::find(m_audioBusHandles.begin(), m_audioBusHandles.end(), busHandle);
|
||||
if (it != m_audioBusHandles.end())
|
||||
{
|
||||
m_audioPorts.erase(it);
|
||||
m_audioBusHandles.erase(it);
|
||||
}
|
||||
doneChangeInModel();
|
||||
}
|
||||
@@ -577,7 +577,7 @@ bool AudioEngine::addPlayHandle( PlayHandle* handle )
|
||||
if (handle->type() == PlayHandle::Type::InstrumentPlayHandle || !criticalXRuns())
|
||||
{
|
||||
m_newPlayHandles.push( handle );
|
||||
handle->audioPort()->addPlayHandle( handle );
|
||||
handle->audioBusHandle()->addPlayHandle(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -598,7 +598,7 @@ void AudioEngine::removePlayHandle(PlayHandle * ph)
|
||||
// which were created in a thread different than the audio engine thread
|
||||
if (ph->affinityMatters() && ph->affinity() == QThread::currentThread())
|
||||
{
|
||||
ph->audioPort()->removePlayHandle(ph);
|
||||
ph->audioBusHandle()->removePlayHandle(ph);
|
||||
bool removedFromList = false;
|
||||
// Check m_newPlayHandles first because doing it the other way around
|
||||
// creates a race condition
|
||||
@@ -657,13 +657,13 @@ void AudioEngine::removePlayHandlesOfTypes(Track * track, PlayHandle::Types type
|
||||
{
|
||||
if ((*it)->isFromTrack(track) && ((*it)->type() & types))
|
||||
{
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::Type::NotePlayHandle )
|
||||
(*it)->audioBusHandle()->removePlayHandle(*it);
|
||||
if((*it)->type() == PlayHandle::Type::NotePlayHandle)
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) *it );
|
||||
NotePlayHandleManager::release((NotePlayHandle*)*it);
|
||||
}
|
||||
else delete *it;
|
||||
it = m_playHandles.erase( it );
|
||||
it = m_playHandles.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
set(LMMS_SRCS
|
||||
${LMMS_SRCS}
|
||||
|
||||
core/AudioBusHandle.cpp
|
||||
core/AudioEngine.cpp
|
||||
core/AudioEngineProfiler.cpp
|
||||
core/AudioEngineWorkerThread.cpp
|
||||
@@ -100,7 +101,6 @@ set(LMMS_SRCS
|
||||
core/audio/AudioJack.cpp
|
||||
core/audio/AudioOss.cpp
|
||||
core/audio/AudioSndio.cpp
|
||||
core/audio/AudioPort.cpp
|
||||
core/audio/AudioPortAudio.cpp
|
||||
core/audio/AudioSoundIo.cpp
|
||||
core/audio/AudioPulseAudio.cpp
|
||||
|
||||
@@ -37,7 +37,7 @@ InstrumentPlayHandle::InstrumentPlayHandle(Instrument * instrument, InstrumentTr
|
||||
PlayHandle(Type::InstrumentPlayHandle),
|
||||
m_instrument(instrument)
|
||||
{
|
||||
setAudioPort(instrumentTrack->audioPort());
|
||||
setAudioBusHandle(instrumentTrack->audioBusHandle());
|
||||
}
|
||||
|
||||
void InstrumentPlayHandle::play(SampleFrame* working_buffer)
|
||||
|
||||
@@ -114,7 +114,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
setUsesBuffer( false );
|
||||
}
|
||||
|
||||
setAudioPort( instrumentTrack->audioPort() );
|
||||
setAudioBusHandle(instrumentTrack->audioBusHandle());
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file,
|
||||
std::numeric_limits<f_cnt_t>::max() / 2,
|
||||
Note( 0, 0, DefaultKey, 100 ) );
|
||||
|
||||
setAudioPort( s_previewTC->previewInstrumentTrack()->audioPort() );
|
||||
setAudioBusHandle(s_previewTC->previewInstrumentTrack()->audioBusHandle());
|
||||
|
||||
s_previewTC->setPreviewNote( m_previewNote );
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include "SamplePlayHandle.h"
|
||||
#include "AudioEngine.h"
|
||||
#include "AudioPort.h"
|
||||
#include "AudioBusHandle.h"
|
||||
#include "Engine.h"
|
||||
#include "Note.h"
|
||||
#include "PatternTrack.h"
|
||||
@@ -35,20 +35,20 @@ namespace lmms
|
||||
{
|
||||
|
||||
|
||||
SamplePlayHandle::SamplePlayHandle(Sample* sample, bool ownAudioPort) :
|
||||
PlayHandle( Type::SamplePlayHandle ),
|
||||
SamplePlayHandle::SamplePlayHandle(Sample* sample, bool ownAudioBusHandle) :
|
||||
PlayHandle(Type::SamplePlayHandle),
|
||||
m_sample(sample),
|
||||
m_doneMayReturnTrue( true ),
|
||||
m_frame( 0 ),
|
||||
m_ownAudioPort( ownAudioPort ),
|
||||
m_defaultVolumeModel( DefaultVolume, MinVolume, MaxVolume, 1 ),
|
||||
m_volumeModel( &m_defaultVolumeModel ),
|
||||
m_track( nullptr ),
|
||||
m_patternTrack( nullptr )
|
||||
m_doneMayReturnTrue(true),
|
||||
m_frame(0),
|
||||
m_ownAudioBusHandle(ownAudioBusHandle),
|
||||
m_defaultVolumeModel(DefaultVolume, MinVolume, MaxVolume, 1),
|
||||
m_volumeModel(&m_defaultVolumeModel),
|
||||
m_track(nullptr),
|
||||
m_patternTrack(nullptr)
|
||||
{
|
||||
if (ownAudioPort)
|
||||
if (ownAudioBusHandle)
|
||||
{
|
||||
setAudioPort( new AudioPort( "SamplePlayHandle", false ) );
|
||||
setAudioBusHandle(new AudioBusHandle("SamplePlayHandle", false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ SamplePlayHandle::SamplePlayHandle( SampleClip* clip ) :
|
||||
SamplePlayHandle(&clip->sample(), false)
|
||||
{
|
||||
m_track = clip->getTrack();
|
||||
setAudioPort( ( (SampleTrack *)clip->getTrack() )->audioPort() );
|
||||
setAudioBusHandle(((SampleTrack *)clip->getTrack())->audioBusHandle());
|
||||
}
|
||||
|
||||
|
||||
@@ -75,9 +75,9 @@ SamplePlayHandle::SamplePlayHandle( SampleClip* clip ) :
|
||||
|
||||
SamplePlayHandle::~SamplePlayHandle()
|
||||
{
|
||||
if( m_ownAudioPort )
|
||||
if(m_ownAudioBusHandle)
|
||||
{
|
||||
delete audioPort();
|
||||
delete audioBusHandle();
|
||||
delete m_sample;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,21 +109,21 @@ void AudioDevice::stopProcessingThread( QThread * thread )
|
||||
|
||||
|
||||
|
||||
void AudioDevice::registerPort( AudioPort * )
|
||||
void AudioDevice::registerPort(AudioBusHandle*)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AudioDevice::unregisterPort( AudioPort * _port )
|
||||
void AudioDevice::unregisterPort(AudioBusHandle*)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AudioDevice::renamePort( AudioPort * )
|
||||
void AudioDevice::renamePort(AudioBusHandle*)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -172,4 +172,4 @@ void AudioDevice::clearS16Buffer( int_sample_t * _outbuf, const fpp_t _frames )
|
||||
memset( _outbuf, 0, _frames * channels() * BYTES_PER_INT_SAMPLE );
|
||||
}
|
||||
|
||||
} // namespace lmms
|
||||
} // namespace lmms
|
||||
|
||||
@@ -74,7 +74,7 @@ AudioJack::AudioJack(bool& successful, AudioEngine* audioEngineParam)
|
||||
AudioJack::~AudioJack()
|
||||
{
|
||||
AudioJack::stopProcessing();
|
||||
#ifdef AUDIO_PORT_SUPPORT
|
||||
#ifdef AUDIO_BUS_HANDLE_SUPPORT
|
||||
while (m_portMap.size())
|
||||
{
|
||||
unregisterPort(m_portMap.begin().key());
|
||||
@@ -229,9 +229,9 @@ void AudioJack::stopProcessing()
|
||||
m_stopped = true;
|
||||
}
|
||||
|
||||
void AudioJack::registerPort(AudioPort* port)
|
||||
void AudioJack::registerPort(AudioBusHandle* port)
|
||||
{
|
||||
#ifdef AUDIO_PORT_SUPPORT
|
||||
#ifdef AUDIO_BUS_HANDLE_SUPPORT
|
||||
// make sure, port is not already registered
|
||||
unregisterPort(port);
|
||||
const QString name[2] = {port->name() + " L", port->name() + " R"};
|
||||
@@ -249,9 +249,9 @@ void AudioJack::registerPort(AudioPort* port)
|
||||
|
||||
|
||||
|
||||
void AudioJack::unregisterPort(AudioPort* port)
|
||||
void AudioJack::unregisterPort(AudioBusHandle* port)
|
||||
{
|
||||
#ifdef AUDIO_PORT_SUPPORT
|
||||
#ifdef AUDIO_BUS_HANDLE_SUPPORT
|
||||
if (m_portMap.contains(port))
|
||||
{
|
||||
for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch)
|
||||
@@ -265,9 +265,9 @@ void AudioJack::unregisterPort(AudioPort* port)
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioJack::renamePort(AudioPort* port)
|
||||
void AudioJack::renamePort(AudioBusHandle* port)
|
||||
{
|
||||
#ifdef AUDIO_PORT_SUPPORT
|
||||
#ifdef AUDIO_BUS_HANDLE_SUPPORT
|
||||
if (m_portMap.contains(port))
|
||||
{
|
||||
const QString name[2] = {port->name() + " L", port->name() + " R"};
|
||||
@@ -282,7 +282,7 @@ void AudioJack::renamePort(AudioPort* port)
|
||||
}
|
||||
#else
|
||||
(void)port;
|
||||
#endif // AUDIO_PORT_SUPPORT
|
||||
#endif // AUDIO_BUS_HANDLE_SUPPORT
|
||||
}
|
||||
|
||||
|
||||
@@ -304,7 +304,7 @@ int AudioJack::processCallback(jack_nframes_t nframes)
|
||||
m_tempOutBufs[c] = (jack_default_audio_sample_t*)jack_port_get_buffer(m_outputPorts[c], nframes);
|
||||
}
|
||||
|
||||
#ifdef AUDIO_PORT_SUPPORT
|
||||
#ifdef AUDIO_BUS_HANDLE_SUPPORT
|
||||
const int frames = std::min<int>(nframes, audioEngine()->framesPerPeriod());
|
||||
for (JackPortMap::iterator it = m_portMap.begin(); it != m_portMap.end(); ++it)
|
||||
{
|
||||
|
||||
@@ -1,253 +0,0 @@
|
||||
/*
|
||||
* AudioPort.cpp - base-class for objects providing sound at a port
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AudioPort.h"
|
||||
#include "AudioDevice.h"
|
||||
#include "AudioEngine.h"
|
||||
#include "EffectChain.h"
|
||||
#include "Mixer.h"
|
||||
#include "Engine.h"
|
||||
#include "MixHelpers.h"
|
||||
#include "BufferManager.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
AudioPort::AudioPort( const QString & _name, bool _has_effect_chain,
|
||||
FloatModel * volumeModel, FloatModel * panningModel,
|
||||
BoolModel * mutedModel ) :
|
||||
m_bufferUsage( false ),
|
||||
m_portBuffer( BufferManager::acquire() ),
|
||||
m_extOutputEnabled( false ),
|
||||
m_nextMixerChannel( 0 ),
|
||||
m_name( "unnamed port" ),
|
||||
m_effects( _has_effect_chain ? new EffectChain( nullptr ) : nullptr ),
|
||||
m_volumeModel( volumeModel ),
|
||||
m_panningModel( panningModel ),
|
||||
m_mutedModel( mutedModel )
|
||||
{
|
||||
Engine::audioEngine()->addAudioPort( this );
|
||||
setExtOutputEnabled( true );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
AudioPort::~AudioPort()
|
||||
{
|
||||
setExtOutputEnabled( false );
|
||||
Engine::audioEngine()->removeAudioPort( this );
|
||||
BufferManager::release( m_portBuffer );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AudioPort::setExtOutputEnabled( bool _enabled )
|
||||
{
|
||||
if( _enabled != m_extOutputEnabled )
|
||||
{
|
||||
m_extOutputEnabled = _enabled;
|
||||
if( m_extOutputEnabled )
|
||||
{
|
||||
Engine::audioEngine()->audioDev()->registerPort( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
Engine::audioEngine()->audioDev()->unregisterPort( this );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AudioPort::setName( const QString & _name )
|
||||
{
|
||||
m_name = _name;
|
||||
Engine::audioEngine()->audioDev()->renamePort( this );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool AudioPort::processEffects()
|
||||
{
|
||||
if( m_effects )
|
||||
{
|
||||
bool more = m_effects->processAudioBuffer( m_portBuffer, Engine::audioEngine()->framesPerPeriod(), m_bufferUsage );
|
||||
return more;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void AudioPort::doProcessing()
|
||||
{
|
||||
if( m_mutedModel && m_mutedModel->value() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const fpp_t fpp = Engine::audioEngine()->framesPerPeriod();
|
||||
|
||||
// clear the buffer
|
||||
zeroSampleFrames(m_portBuffer, fpp);
|
||||
|
||||
//qDebug( "Playhandles: %d", m_playHandles.size() );
|
||||
for( PlayHandle * ph : m_playHandles ) // now we mix all playhandle buffers into the audioport buffer
|
||||
{
|
||||
if( ph->buffer() )
|
||||
{
|
||||
if( ph->usesBuffer()
|
||||
&& ( ph->type() == PlayHandle::Type::NotePlayHandle
|
||||
|| !MixHelpers::isSilent( ph->buffer(), fpp ) ) )
|
||||
{
|
||||
m_bufferUsage = true;
|
||||
MixHelpers::add( m_portBuffer, ph->buffer(), fpp );
|
||||
}
|
||||
ph->releaseBuffer(); // gets rid of playhandle's buffer and sets
|
||||
// pointer to null, so if it doesn't get re-acquired we know to skip it next time
|
||||
}
|
||||
}
|
||||
|
||||
if( m_bufferUsage )
|
||||
{
|
||||
// handle volume and panning
|
||||
// has both vol and pan models
|
||||
if( m_volumeModel && m_panningModel )
|
||||
{
|
||||
ValueBuffer * volBuf = m_volumeModel->valueBuffer();
|
||||
ValueBuffer * panBuf = m_panningModel->valueBuffer();
|
||||
|
||||
// both vol and pan have s.ex.data:
|
||||
if( volBuf && panBuf )
|
||||
{
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
float v = volBuf->values()[ f ] * 0.01f;
|
||||
float p = panBuf->values()[ f ] * 0.01f;
|
||||
m_portBuffer[f][0] *= ( p <= 0 ? 1.0f : 1.0f - p ) * v;
|
||||
m_portBuffer[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p ) * v;
|
||||
}
|
||||
}
|
||||
|
||||
// only vol has s.ex.data:
|
||||
else if( volBuf )
|
||||
{
|
||||
float p = m_panningModel->value() * 0.01f;
|
||||
float l = ( p <= 0 ? 1.0f : 1.0f - p );
|
||||
float r = ( p >= 0 ? 1.0f : 1.0f + p );
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
float v = volBuf->values()[ f ] * 0.01f;
|
||||
m_portBuffer[f][0] *= v * l;
|
||||
m_portBuffer[f][1] *= v * r;
|
||||
}
|
||||
}
|
||||
|
||||
// only pan has s.ex.data:
|
||||
else if( panBuf )
|
||||
{
|
||||
float v = m_volumeModel->value() * 0.01f;
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
float p = panBuf->values()[ f ] * 0.01f;
|
||||
m_portBuffer[f][0] *= ( p <= 0 ? 1.0f : 1.0f - p ) * v;
|
||||
m_portBuffer[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p ) * v;
|
||||
}
|
||||
}
|
||||
|
||||
// neither has s.ex.data:
|
||||
else
|
||||
{
|
||||
float p = m_panningModel->value() * 0.01f;
|
||||
float v = m_volumeModel->value() * 0.01f;
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
m_portBuffer[f][0] *= ( p <= 0 ? 1.0f : 1.0f - p ) * v;
|
||||
m_portBuffer[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p ) * v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// has vol model only
|
||||
else if( m_volumeModel )
|
||||
{
|
||||
ValueBuffer * volBuf = m_volumeModel->valueBuffer();
|
||||
|
||||
if( volBuf )
|
||||
{
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
float v = volBuf->values()[ f ] * 0.01f;
|
||||
m_portBuffer[f][0] *= v;
|
||||
m_portBuffer[f][1] *= v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float v = m_volumeModel->value() * 0.01f;
|
||||
for( f_cnt_t f = 0; f < fpp; ++f )
|
||||
{
|
||||
m_portBuffer[f][0] *= v;
|
||||
m_portBuffer[f][1] *= v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// as of now there's no situation where we only have panning model but no volume model
|
||||
// if we have neither, we don't have to do anything here - just pass the audio as is
|
||||
|
||||
// handle effects
|
||||
const bool me = processEffects();
|
||||
if( me || m_bufferUsage )
|
||||
{
|
||||
Engine::mixer()->mixToChannel( m_portBuffer, m_nextMixerChannel ); // send output to mixer
|
||||
// TODO: improve the flow here - convert to pull model
|
||||
m_bufferUsage = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AudioPort::addPlayHandle( PlayHandle * handle )
|
||||
{
|
||||
m_playHandleLock.lock();
|
||||
m_playHandles.append( handle );
|
||||
m_playHandleLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
void AudioPort::removePlayHandle( PlayHandle * handle )
|
||||
{
|
||||
m_playHandleLock.lock();
|
||||
PlayHandleList::Iterator it = std::find( m_playHandles.begin(), m_playHandles.end(), handle );
|
||||
if( it != m_playHandles.end() )
|
||||
{
|
||||
m_playHandles.erase( it );
|
||||
}
|
||||
m_playHandleLock.unlock();
|
||||
}
|
||||
|
||||
} // namespace lmms
|
||||
@@ -139,7 +139,7 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) :
|
||||
|
||||
generalSettingsLayout->addLayout(basicControlsLayout);
|
||||
|
||||
m_effectRack = new EffectRackView(tv->model()->audioPort()->effects());
|
||||
m_effectRack = new EffectRackView(tv->model()->audioBusHandle()->effects());
|
||||
m_effectRack->setFixedSize(EffectRackView::DEFAULT_WIDTH, 242);
|
||||
|
||||
vlayout->addWidget(generalSettingsWidget);
|
||||
|
||||
@@ -251,7 +251,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) :
|
||||
m_midiView = new InstrumentMidiIOView(m_tabWidget);
|
||||
|
||||
// FX tab
|
||||
m_effectView = new EffectRackView(m_track->m_audioPort.effects(), m_tabWidget);
|
||||
m_effectView = new EffectRackView(m_track->m_audioBusHandle.effects(), m_tabWidget);
|
||||
|
||||
// Tuning tab
|
||||
m_tuningView = new InstrumentTuningView(m_track, m_tabWidget);
|
||||
@@ -381,11 +381,11 @@ void InstrumentTrackWindow::modelChanged()
|
||||
m_tuningView->microtunerGroupBox()->show();
|
||||
}
|
||||
|
||||
m_ssView->setModel( &m_track->m_soundShaping );
|
||||
m_noteStackingView->setModel( &m_track->m_noteStacking );
|
||||
m_arpeggioView->setModel( &m_track->m_arpeggio );
|
||||
m_midiView->setModel( &m_track->m_midiPort );
|
||||
m_effectView->setModel( m_track->m_audioPort.effects() );
|
||||
m_ssView->setModel(&m_track->m_soundShaping);
|
||||
m_noteStackingView->setModel(&m_track->m_noteStacking);
|
||||
m_arpeggioView->setModel(&m_track->m_arpeggio);
|
||||
m_midiView->setModel(&m_track->m_midiPort);
|
||||
m_effectView->setModel(m_track->m_audioBusHandle.effects());
|
||||
m_tuningView->pitchGroupBox()->setModel(&m_track->m_useMasterPitchModel);
|
||||
m_tuningView->microtunerGroupBox()->setModel(m_track->m_microtuner.enabledModel());
|
||||
m_tuningView->scaleCombo()->setModel(m_track->m_microtuner.scaleModel());
|
||||
|
||||
@@ -46,30 +46,29 @@ namespace lmms
|
||||
{
|
||||
|
||||
|
||||
InstrumentTrack::InstrumentTrack( TrackContainer* tc ) :
|
||||
Track( Track::Type::Instrument, tc ),
|
||||
InstrumentTrack::InstrumentTrack(TrackContainer* tc) :
|
||||
Track(Track::Type::Instrument, tc),
|
||||
MidiEventProcessor(),
|
||||
m_midiPort( tr( "unnamed_track" ), Engine::audioEngine()->midiClient(),
|
||||
this, this ),
|
||||
m_midiPort(tr("unnamed_track"), Engine::audioEngine()->midiClient(), this, this),
|
||||
m_notes(),
|
||||
m_sustainPedalPressed( false ),
|
||||
m_silentBuffersProcessed( false ),
|
||||
m_previewMode( false ),
|
||||
m_sustainPedalPressed(false),
|
||||
m_silentBuffersProcessed(false),
|
||||
m_previewMode(false),
|
||||
m_baseNoteModel(0, 0, NumKeys - 1, this, tr("Base note")),
|
||||
m_firstKeyModel(0, 0, NumKeys - 1, this, tr("First note")),
|
||||
m_lastKeyModel(0, 0, NumKeys - 1, this, tr("Last note")),
|
||||
m_hasAutoMidiDev( false ),
|
||||
m_volumeModel( DefaultVolume, MinVolume, MaxVolume, 0.1f, this, tr( "Volume" ) ),
|
||||
m_panningModel( DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr( "Panning" ) ),
|
||||
m_audioPort( tr( "unnamed_track" ), true, &m_volumeModel, &m_panningModel, &m_mutedModel ),
|
||||
m_pitchModel( 0, MinPitchDefault, MaxPitchDefault, 1, this, tr( "Pitch" ) ),
|
||||
m_pitchRangeModel( 1, 1, 60, this, tr( "Pitch range" ) ),
|
||||
m_mixerChannelModel( 0, 0, 0, this, tr( "Mixer channel" ) ),
|
||||
m_useMasterPitchModel( true, this, tr( "Master pitch") ),
|
||||
m_instrument( nullptr ),
|
||||
m_soundShaping( this ),
|
||||
m_arpeggio( this ),
|
||||
m_noteStacking( this ),
|
||||
m_hasAutoMidiDev(false),
|
||||
m_volumeModel(DefaultVolume, MinVolume, MaxVolume, 0.1f, this, tr("Volume")),
|
||||
m_panningModel(DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr("Panning")),
|
||||
m_audioBusHandle(tr("unnamed_track"), true, &m_volumeModel, &m_panningModel, &m_mutedModel),
|
||||
m_pitchModel(0, MinPitchDefault, MaxPitchDefault, 1, this, tr("Pitch")),
|
||||
m_pitchRangeModel(1, 1, 60, this, tr("Pitch range")),
|
||||
m_mixerChannelModel(0, 0, 0, this, tr("Mixer channel")),
|
||||
m_useMasterPitchModel(true, this, tr("Master pitch")),
|
||||
m_instrument(nullptr),
|
||||
m_soundShaping(this),
|
||||
m_arpeggio(this),
|
||||
m_noteStacking(this),
|
||||
m_piano(this),
|
||||
m_microtuner()
|
||||
{
|
||||
@@ -250,7 +249,7 @@ void InstrumentTrack::processAudioBuffer( SampleFrame* buf, const fpp_t frames,
|
||||
|
||||
// if effects "went to sleep" because there was no input, wake them up
|
||||
// now
|
||||
m_audioPort.effects()->startRunning();
|
||||
m_audioBusHandle.effects()->startRunning();
|
||||
|
||||
// get volume knob data
|
||||
static const float DefaultVolumeRatio = 1.0f / DefaultVolume;
|
||||
@@ -620,11 +619,11 @@ void InstrumentTrack::deleteNotePluginData( NotePlayHandle* n )
|
||||
|
||||
|
||||
|
||||
void InstrumentTrack::setName( const QString & _new_name )
|
||||
void InstrumentTrack::setName(const QString& new_name)
|
||||
{
|
||||
Track::setName( _new_name );
|
||||
m_midiPort.setName( name() );
|
||||
m_audioPort.setName( name() );
|
||||
Track::setName(new_name);
|
||||
m_midiPort.setName(name());
|
||||
m_audioBusHandle.setName(name());
|
||||
}
|
||||
|
||||
|
||||
@@ -672,7 +671,7 @@ void InstrumentTrack::updatePitchRange()
|
||||
|
||||
void InstrumentTrack::updateMixerChannel()
|
||||
{
|
||||
m_audioPort.setNextMixerChannel( m_mixerChannelModel.value() );
|
||||
m_audioBusHandle.setNextMixerChannel(m_mixerChannelModel.value());
|
||||
}
|
||||
|
||||
|
||||
@@ -877,7 +876,7 @@ void InstrumentTrack::saveTrackSpecificSettings(QDomDocument& doc, QDomElement&
|
||||
autoAssignMidiDevice(hasAuto);
|
||||
}
|
||||
|
||||
m_audioPort.effects()->saveState( doc, thisElement );
|
||||
m_audioBusHandle.effects()->saveState(doc, thisElement);
|
||||
}
|
||||
|
||||
|
||||
@@ -909,7 +908,7 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement
|
||||
m_microtuner.loadSettings(thisElement);
|
||||
|
||||
// clear effect-chain just in case we load an old preset without FX-data
|
||||
m_audioPort.effects()->clear();
|
||||
m_audioBusHandle.effects()->clear();
|
||||
|
||||
// We set MIDI CC enable to false so the knobs don't trigger MIDI CC events while
|
||||
// they are being loaded. After all knobs are loaded we load the right value of m_midiCCEnable.
|
||||
@@ -936,9 +935,9 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement
|
||||
{
|
||||
m_midiPort.restoreState( node.toElement() );
|
||||
}
|
||||
else if( m_audioPort.effects()->nodeName() == node.nodeName() )
|
||||
else if (m_audioBusHandle.effects()->nodeName() == node.nodeName())
|
||||
{
|
||||
m_audioPort.effects()->restoreState( node.toElement() );
|
||||
m_audioBusHandle.effects()->restoreState(node.toElement());
|
||||
}
|
||||
else if(node.nodeName() == "instrument")
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ SampleTrack::SampleTrack(TrackContainer* tc) :
|
||||
m_volumeModel(DefaultVolume, MinVolume, MaxVolume, 0.1f, this, tr("Volume")),
|
||||
m_panningModel(DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr("Panning")),
|
||||
m_mixerChannelModel(0, 0, 0, this, tr("Mixer channel")),
|
||||
m_audioPort(tr("Sample track"), true, &m_volumeModel, &m_panningModel, &m_mutedModel),
|
||||
m_audioBusHandle(tr("Sample track"), true, &m_volumeModel, &m_panningModel, &m_mutedModel),
|
||||
m_isPlaying(false)
|
||||
{
|
||||
setName(tr("Sample track"));
|
||||
@@ -73,7 +73,7 @@ SampleTrack::~SampleTrack()
|
||||
bool SampleTrack::play( const TimePos & _start, const fpp_t _frames,
|
||||
const f_cnt_t _offset, int _clip_num )
|
||||
{
|
||||
m_audioPort.effects()->startRunning();
|
||||
m_audioBusHandle.effects()->startRunning();
|
||||
bool played_a_note = false; // will be return variable
|
||||
|
||||
|
||||
@@ -188,36 +188,36 @@ Clip * SampleTrack::createClip(const TimePos & pos)
|
||||
|
||||
|
||||
|
||||
void SampleTrack::saveTrackSpecificSettings(QDomDocument& _doc, QDomElement& _this, bool presetMode)
|
||||
void SampleTrack::saveTrackSpecificSettings(QDomDocument& doc, QDomElement& thisElem, bool /*presetMode*/)
|
||||
{
|
||||
m_audioPort.effects()->saveState( _doc, _this );
|
||||
m_volumeModel.saveSettings( _doc, _this, "vol" );
|
||||
m_panningModel.saveSettings( _doc, _this, "pan" );
|
||||
m_mixerChannelModel.saveSettings( _doc, _this, "mixch" );
|
||||
m_audioBusHandle.effects()->saveState(doc, thisElem);
|
||||
m_volumeModel.saveSettings(doc, thisElem, "vol");
|
||||
m_panningModel.saveSettings(doc, thisElem, "pan");
|
||||
m_mixerChannelModel.saveSettings(doc, thisElem, "mixch");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void SampleTrack::loadTrackSpecificSettings( const QDomElement & _this )
|
||||
void SampleTrack::loadTrackSpecificSettings(const QDomElement & thisElem)
|
||||
{
|
||||
QDomNode node = _this.firstChild();
|
||||
m_audioPort.effects()->clear();
|
||||
while( !node.isNull() )
|
||||
QDomNode node = thisElem.firstChild();
|
||||
m_audioBusHandle.effects()->clear();
|
||||
while(!node.isNull())
|
||||
{
|
||||
if( node.isElement() )
|
||||
if (node.isElement())
|
||||
{
|
||||
if( m_audioPort.effects()->nodeName() == node.nodeName() )
|
||||
if (m_audioBusHandle.effects()->nodeName() == node.nodeName())
|
||||
{
|
||||
m_audioPort.effects()->restoreState( node.toElement() );
|
||||
m_audioBusHandle.effects()->restoreState(node.toElement());
|
||||
}
|
||||
}
|
||||
node = node.nextSibling();
|
||||
}
|
||||
m_volumeModel.loadSettings( _this, "vol" );
|
||||
m_panningModel.loadSettings( _this, "pan" );
|
||||
m_mixerChannelModel.setRange( 0, Engine::mixer()->numChannels() - 1 );
|
||||
m_mixerChannelModel.loadSettings( _this, "mixch" );
|
||||
m_volumeModel.loadSettings(thisElem, "vol");
|
||||
m_panningModel.loadSettings(thisElem, "pan");
|
||||
m_mixerChannelModel.setRange(0, Engine::mixer()->numChannels() - 1);
|
||||
m_mixerChannelModel.loadSettings(thisElem, "mixch");
|
||||
}
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ void SampleTrack::setPlayingClips( bool isPlaying )
|
||||
|
||||
void SampleTrack::updateMixerChannel()
|
||||
{
|
||||
m_audioPort.setNextMixerChannel( m_mixerChannelModel.value() );
|
||||
m_audioBusHandle.setNextMixerChannel(m_mixerChannelModel.value());
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user