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