Skip to content

Commit 30b2ac0

Browse files
committed
WIP: Add audio clip audio context
1 parent b39c018 commit 30b2ac0

7 files changed

Lines changed: 316 additions & 2 deletions

File tree

src/libs/application/dspxmodel/src/AudioClip.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace dspx {
1414

1515
class AudioClipPrivate;
1616

17-
struct AudioPathInfo {
17+
struct DSPX_MODEL_EXPORT AudioPathInfo {
1818
Q_GADGET
1919
Q_PROPERTY(QString absoluteDir MEMBER absoluteDir)
2020
Q_PROPERTY(QString relativeDir MEMBER relativeDir)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include "AudioClipAudioContext.h"
2+
#include "AudioClipAudioContext_p.h"
3+
4+
#include <QHash>
5+
6+
#include <TalcsCore/BufferingAudioSource.h>
7+
#include <TalcsCore/PositionableAudioSource.h>
8+
#include <TalcsCore/PositionableMixerAudioSource.h>
9+
#include <TalcsDspx/DspxAudioClipContext.h>
10+
#include <TalcsDspx/DspxTrackContext.h>
11+
12+
#include <coreplugin/ProjectWindowInterface.h>
13+
14+
#include <dspxmodel/AudioClip.h>
15+
16+
namespace Audio {
17+
18+
static QHash<dspx::AudioClip *, AudioClipAudioContext *> s_audioClipAudioContexts;
19+
20+
static quintptr audioClipId(dspx::AudioClip *clip) {
21+
return reinterpret_cast<quintptr>(clip);
22+
}
23+
24+
AudioClipAudioContext::AudioClipAudioContext(Core::ProjectWindowInterface *windowHandle, dspx::AudioClip *clip, talcs::DspxTrackContext *trackContext)
25+
: QObject(windowHandle), d_ptr(new AudioClipAudioContextPrivate) {
26+
Q_D(AudioClipAudioContext);
27+
d->q_ptr = this;
28+
d->windowHandle = windowHandle;
29+
d->clip = clip;
30+
d->trackContext = trackContext;
31+
d->clipContext = trackContext->addAudioClip(audioClipId(clip));
32+
s_audioClipAudioContexts.insert(clip, this);
33+
}
34+
35+
AudioClipAudioContext::~AudioClipAudioContext() {
36+
Q_D(AudioClipAudioContext);
37+
s_audioClipAudioContexts.remove(d->clip);
38+
d->trackContext->removeAudioClip(audioClipId(d->clip));
39+
}
40+
41+
AudioClipAudioContext *AudioClipAudioContext::of(dspx::AudioClip *clip) {
42+
return s_audioClipAudioContexts.value(clip);
43+
}
44+
45+
Core::ProjectWindowInterface *AudioClipAudioContext::windowHandle() const {
46+
Q_D(const AudioClipAudioContext);
47+
return d->windowHandle;
48+
}
49+
50+
dspx::AudioClip *AudioClipAudioContext::clip() const {
51+
Q_D(const AudioClipAudioContext);
52+
return d->clip;
53+
}
54+
55+
talcs::PositionableMixerAudioSource *AudioClipAudioContext::controlMixer() const {
56+
Q_D(const AudioClipAudioContext);
57+
return d->clipContext->controlMixer();
58+
}
59+
60+
talcs::PositionableMixerAudioSource *AudioClipAudioContext::clipMixer() const {
61+
Q_D(const AudioClipAudioContext);
62+
return d->clipContext->clipMixer();
63+
}
64+
65+
talcs::PositionableAudioSource *AudioClipAudioContext::contentSource() const {
66+
Q_D(const AudioClipAudioContext);
67+
return d->clipContext->contentSource();
68+
}
69+
70+
AudioClipAudioContext *AudioClipAudioContextPrivate::create(Core::ProjectWindowInterface *windowHandle, dspx::AudioClip *clip, talcs::DspxTrackContext *trackContext) {
71+
return new AudioClipAudioContext(windowHandle, clip, trackContext);
72+
}
73+
74+
AudioClipAudioContextPrivate *AudioClipAudioContextPrivate::of(AudioClipAudioContext *q) {
75+
return q->d_func();
76+
}
77+
78+
}
79+
80+
#include "moc_AudioClipAudioContext.cpp"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#ifndef DIFFSCOPE_AUDIO_AUDIOCLIPAUDIOCONTEXT_H
2+
#define DIFFSCOPE_AUDIO_AUDIOCLIPAUDIOCONTEXT_H
3+
4+
#include <QObject>
5+
6+
namespace Core {
7+
class ProjectWindowInterface;
8+
}
9+
10+
namespace dspx {
11+
class AudioClip;
12+
}
13+
14+
namespace talcs {
15+
class DspxTrackContext;
16+
class PositionableAudioSource;
17+
class PositionableMixerAudioSource;
18+
}
19+
20+
namespace Audio {
21+
22+
namespace Internal {
23+
class ProjectAudioAddOn;
24+
}
25+
26+
class AudioClipAudioContextPrivate;
27+
28+
class AudioClipAudioContext : public QObject {
29+
Q_OBJECT
30+
Q_DECLARE_PRIVATE(AudioClipAudioContext)
31+
Q_PROPERTY(Core::ProjectWindowInterface *windowHandle READ windowHandle CONSTANT)
32+
Q_PROPERTY(dspx::AudioClip *clip READ clip CONSTANT)
33+
34+
public:
35+
~AudioClipAudioContext() override;
36+
37+
static AudioClipAudioContext *of(dspx::AudioClip *clip);
38+
39+
Core::ProjectWindowInterface *windowHandle() const;
40+
dspx::AudioClip *clip() const;
41+
42+
talcs::PositionableMixerAudioSource *controlMixer() const;
43+
talcs::PositionableMixerAudioSource *clipMixer() const;
44+
talcs::PositionableAudioSource *contentSource() const;
45+
46+
private:
47+
friend class AudioClipAudioContextPrivate;
48+
friend class Internal::ProjectAudioAddOn;
49+
explicit AudioClipAudioContext(Core::ProjectWindowInterface *windowHandle, dspx::AudioClip *clip, talcs::DspxTrackContext *trackContext);
50+
51+
QScopedPointer<AudioClipAudioContextPrivate> d_ptr;
52+
};
53+
54+
}
55+
56+
#endif // DIFFSCOPE_AUDIO_AUDIOCLIPAUDIOCONTEXT_H
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef DIFFSCOPE_AUDIO_AUDIOCLIPAUDIOCONTEXT_P_H
2+
#define DIFFSCOPE_AUDIO_AUDIOCLIPAUDIOCONTEXT_P_H
3+
4+
#include <audio/AudioClipAudioContext.h>
5+
6+
namespace talcs {
7+
class DspxAudioClipContext;
8+
class DspxTrackContext;
9+
}
10+
11+
namespace Audio {
12+
13+
class AudioClipAudioContextPrivate {
14+
Q_DECLARE_PUBLIC(AudioClipAudioContext)
15+
public:
16+
AudioClipAudioContext *q_ptr{};
17+
Core::ProjectWindowInterface *windowHandle{};
18+
dspx::AudioClip *clip{};
19+
talcs::DspxTrackContext *trackContext{};
20+
talcs::DspxAudioClipContext *clipContext{};
21+
22+
static AudioClipAudioContext *create(Core::ProjectWindowInterface *windowHandle, dspx::AudioClip *clip, talcs::DspxTrackContext *trackContext);
23+
static AudioClipAudioContextPrivate *of(AudioClipAudioContext *q);
24+
};
25+
26+
}
27+
28+
#endif // DIFFSCOPE_AUDIO_AUDIOCLIPAUDIOCONTEXT_P_H

src/plugins/audio/internal/addon/ProjectAudioAddOn.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
#include "ProjectAudioAddOn.h"
22

3+
#include <QDir>
4+
35
#include <TalcsCore/MixerAudioSource.h>
46
#include <TalcsCore/PositionableMixerAudioSource.h>
7+
#include <TalcsDspx/DspxAudioClipContext.h>
58
#include <TalcsDspx/DspxProjectContext.h>
9+
#include <TalcsFormat/AbstractAudioFormatIO.h>
10+
#include <TalcsFormat/FormatManager.h>
611

712
#include <SVSCraftCore/MusicTime.h>
813
#include <SVSCraftCore/MusicTimeline.h>
914

15+
#include <dspxmodel/AudioClip.h>
1016
#include <dspxmodel/BusControl.h>
17+
#include <dspxmodel/Clip.h>
18+
#include <dspxmodel/ClipSequence.h>
19+
#include <dspxmodel/ClipTime.h>
1120
#include <dspxmodel/Master.h>
1221
#include <dspxmodel/Model.h>
1322
#include <dspxmodel/Track.h>
1423
#include <dspxmodel/TrackControl.h>
1524
#include <dspxmodel/TrackList.h>
1625

26+
#include <audio/AudioClipAudioContext.h>
27+
#include <audio/private/AudioClipAudioContext_p.h>
1728
#include <audio/GlobalAudioContext.h>
1829
#include <audio/ProjectAudioContext.h>
1930
#include <audio/private/ProjectAudioContext_p.h>
@@ -35,9 +46,14 @@ namespace Audio::Internal {
3546
GlobalAudioContext::preMixer()->removeSource(m_context->preMixer());
3647
const auto tracks = windowHandle()->cast<Core::ProjectWindowInterface>()->projectDocumentContext()->document()->model()->tracks()->items();
3748
for (auto track : tracks) {
49+
const auto clips = track->clips()->slice(0, track->clips()->size());
50+
for (auto clip : clips) {
51+
removeClip(clip);
52+
}
3853
delete TrackAudioContext::of(track);
3954
}
4055
}
56+
qDeleteAll(m_audioClipCache);
4157
}
4258

4359
void ProjectAudioAddOn::initialize() {
@@ -82,17 +98,33 @@ namespace Audio::Internal {
8298
return windowHandle->getFirstObject<ProjectAudioAddOn>();
8399
}
84100

101+
void ProjectAudioAddOn::addAudioClipCache(dspx::AudioClip *clip, talcs::AbstractAudioFormatIO *io) {
102+
delete m_audioClipCache.take(clip);
103+
if (io) {
104+
m_audioClipCache.insert(clip, io);
105+
}
106+
}
107+
108+
talcs::AbstractAudioFormatIO *ProjectAudioAddOn::takeAudioClipCache(dspx::AudioClip *clip) {
109+
return m_audioClipCache.take(clip);
110+
}
111+
85112
void ProjectAudioAddOn::addTrack(int index, dspx::Track *track) {
86113
Q_ASSERT(TrackAudioContext::of(track) == nullptr);
87114
auto projectAudioContext = ProjectAudioContextPrivate::of(m_context)->projectContext.get();
88115
auto context = TrackAudioContextPrivate::create(windowHandle()->cast<Core::ProjectWindowInterface>(), track, projectAudioContext, index);
89116
syncTrackControl(track, context);
117+
syncTrackClips(track, context);
90118
}
91119

92120
void ProjectAudioAddOn::removeTrack(int index, dspx::Track *track) {
93121
Q_UNUSED(index)
94122
auto context = TrackAudioContext::of(track);
95123
Q_ASSERT(context);
124+
const auto clips = track->clips()->slice(0, track->clips()->size());
125+
for (auto clip : clips) {
126+
removeClip(clip);
127+
}
96128
delete context;
97129
}
98130

@@ -141,4 +173,103 @@ namespace Audio::Internal {
141173
});
142174
}
143175

176+
void ProjectAudioAddOn::syncTrackClips(dspx::Track *track, TrackAudioContext *context) {
177+
Q_UNUSED(context)
178+
const auto clips = track->clips()->slice(0, track->clips()->size());
179+
for (auto clip : clips) {
180+
addClip(clip);
181+
}
182+
connect(track->clips(), &dspx::ClipSequence::itemInserted, this, [this](dspx::Clip *clip) {
183+
addClip(clip);
184+
});
185+
connect(track->clips(), &dspx::ClipSequence::itemRemoved, this, [this](dspx::Clip *clip) {
186+
removeClip(clip);
187+
});
188+
}
189+
190+
void ProjectAudioAddOn::addClip(dspx::Clip *clip) {
191+
if (!clip || clip->type() != dspx::Clip::Audio) {
192+
return;
193+
}
194+
auto audioClip = static_cast<dspx::AudioClip *>(clip);
195+
Q_ASSERT(AudioClipAudioContext::of(audioClip) == nullptr);
196+
auto trackContext = TrackAudioContext::of(clip->clipSequence()->track());
197+
Q_ASSERT(trackContext);
198+
auto context = AudioClipAudioContextPrivate::create(windowHandle()->cast<Core::ProjectWindowInterface>(), audioClip, TrackAudioContextPrivate::of(trackContext)->trackContext);
199+
syncAudioClip(audioClip, context);
200+
}
201+
202+
void ProjectAudioAddOn::removeClip(dspx::Clip *clip) {
203+
if (!clip || clip->type() != dspx::Clip::Audio) {
204+
return;
205+
}
206+
auto context = AudioClipAudioContext::of(static_cast<dspx::AudioClip *>(clip));
207+
if (context) {
208+
delete context;
209+
}
210+
}
211+
212+
void ProjectAudioAddOn::syncAudioClip(dspx::AudioClip *clip, AudioClipAudioContext *context) {
213+
auto control = clip->control();
214+
auto controlMixer = context->controlMixer();
215+
216+
controlMixer->setGain(static_cast<float>(control->gain()));
217+
controlMixer->setPan(static_cast<float>(control->pan()));
218+
controlMixer->setSilentFlags(control->mute() ? -1 : 0);
219+
220+
connect(control, &dspx::BusControl::gainChanged, controlMixer, &talcs::PositionableMixerAudioSource::setGain);
221+
connect(control, &dspx::BusControl::panChanged, controlMixer, &talcs::PositionableMixerAudioSource::setPan);
222+
connect(control, &dspx::BusControl::muteChanged, controlMixer, [controlMixer](bool mute) {
223+
controlMixer->setSilentFlags(mute ? -1 : 0);
224+
});
225+
226+
auto clipContext = AudioClipAudioContextPrivate::of(context)->clipContext;
227+
auto time = clip->time();
228+
clipContext->setStart(time->start());
229+
clipContext->setClipStart(time->clipStart());
230+
clipContext->setClipLen(time->clipLen());
231+
232+
connect(time, &dspx::ClipTime::startChanged, clipContext, &talcs::DspxAudioClipContext::setStart);
233+
connect(time, &dspx::ClipTime::clipStartChanged, clipContext, &talcs::DspxAudioClipContext::setClipStart);
234+
connect(time, &dspx::ClipTime::clipLenChanged, clipContext, &talcs::DspxAudioClipContext::setClipLen);
235+
connect(GlobalAudioContext::instance(), &GlobalAudioContext::sampleRateChanged, clipContext, [clipContext] {
236+
clipContext->updatePosition();
237+
});
238+
connect(windowHandle()->cast<Core::ProjectWindowInterface>()->projectTimeline()->musicTimeline(), &SVS::MusicTimeline::tempiChanged, clipContext, [clipContext] {
239+
clipContext->updatePosition();
240+
});
241+
242+
loadAudioClip(clip, context);
243+
clipContext->updatePosition();
244+
245+
connect(clip, &dspx::AudioClip::pathChanged, context, [this, clip, context] {
246+
reloadAudioClip(clip, context);
247+
});
248+
}
249+
250+
void ProjectAudioAddOn::loadAudioClip(dspx::AudioClip *clip, AudioClipAudioContext *context) {
251+
auto io = takeAudioClipCache(clip);
252+
if (!io) {
253+
const auto path = clip->path();
254+
const auto filePath = QDir(path.absoluteDir).filePath(path.fileName);
255+
io = GlobalAudioContext::formatManager()->getFormatLoad(filePath, path.userData, path.formatEntryClassName);
256+
}
257+
258+
if (!io) {
259+
// TODO: Report audio clip load failure.
260+
return;
261+
}
262+
263+
AudioClipAudioContextPrivate::of(context)->clipContext->loadAudio(io);
264+
}
265+
266+
void ProjectAudioAddOn::reloadAudioClip(dspx::AudioClip *clip, AudioClipAudioContext *context) {
267+
auto clipContext = AudioClipAudioContextPrivate::of(context)->clipContext;
268+
if (context->contentSource()) {
269+
delete clipContext->takeAudio();
270+
}
271+
loadAudioClip(clip, context);
272+
clipContext->updatePosition();
273+
}
274+
144275
}

0 commit comments

Comments
 (0)