Rename AudioPort -> AudioBusHandle (#7712)

No functional changes.
This commit is contained in:
Johannes Lorenz
2025-02-28 23:44:58 +01:00
committed by GitHub
parent cf4b492292
commit 3aa1a5dafa
23 changed files with 510 additions and 533 deletions

110
include/AudioBusHandle.h Normal file
View 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

View File

@@ -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
{

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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*>;

View File

@@ -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;

View File

@@ -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
View 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

View File

@@ -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
{

View File

@@ -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

View File

@@ -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)

View File

@@ -114,7 +114,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
setUsesBuffer( false );
}
setAudioPort( instrumentTrack->audioPort() );
setAudioBusHandle(instrumentTrack->audioBusHandle());
unlock();
}

View File

@@ -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 );

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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);

View File

@@ -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());

View File

@@ -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")
{

View File

@@ -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());
}