#pragma once #include "analytics.h" #include "audioclip.h" #include "biquad.h" #include "concurrent/bag.h" #include "limiter.h" #include "meter.h" #include "mipmapworker.h" #include "timeline.h" #include "track.h" #include #include EMSCRIPTEN_DECLARE_VAL_TYPE(DSPContextPromise); EMSCRIPTEN_DECLARE_VAL_TYPE(AudioContextValType); struct TimelineDestroyRequest { TimelineDestroyRequest(std::shared_ptr timeline) : timeline(std::move(timeline)), next(nullptr) {} std::shared_ptr timeline; TimelineDestroyRequest *next; }; struct LoopStateGuard { std::shared_ptr tl; bool prev; explicit LoopStateGuard(const std::shared_ptr &t) : tl(t), prev(t ? t->isLoopEnabled() : false) { if (tl) { tl->setLoopEnabled(false); } } ~LoopStateGuard() { if (tl) { tl->setLoopEnabled(prev); } } }; class Bouncer final { private: std::shared_ptr outputBuffer; std::shared_ptr timeline; LoopStateGuard loopGuard; double end; public: Bouncer(const std::shared_ptr &timeline, double start, double end, int channelCount, int bufferSize); std::shared_ptr bounceNext(); }; class SumOnlyBouncer final { private: std::shared_ptr mixdownBuffer, outputBuffer; std::shared_ptr limiter; size_t framesProduced; size_t tailFramesProduced; public: SumOnlyBouncer(int bufferSize, int channelCount, int sampleRate); void sumIntoMixdownBuffer(const std::shared_ptr &buffer); std::shared_ptr bounceNext(bool inputFinished); }; class DSPContext final { private: struct ContinueDSPContextCreate { std::string name; float vuAlpha; int channelCount; std::shared_ptr analyticsObserver; emscripten::val resolve; emscripten::val reject; }; struct DSPContextRoot { std::shared_ptr instance; }; static int audioContextSampleRate; static EMSCRIPTEN_WEBAUDIO_T audioContext; static bool asyncInitialized; static void AudioThreadInitialized(EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData); static void CreateProcessorAsync(ContinueDSPContextCreate *continuation); static void AudioWorkletProcessorCreated(EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData); static bool RunWorklet(int numInputs, const AudioSampleFrame *inputs, int numOutputs, AudioSampleFrame *outputs, int numParams, const AudioParamFrame *params, void *userData); bool run(int numInputs, const AudioSampleFrame *inputs, int numOutputs, AudioSampleFrame *outputs); TimelineDestroyRequest *sync_newPlayingTimeline; TimelineDestroyRequest *rt_playingTimeline; IntrusiveBag timelineDestroyRequests; void resolvePendingTimelineSwap(); void flushDestroyRequests(); const float vuAlpha; const int channelCount; const int sampleRate; const bool isOffline; std::shared_ptr limiter; std::shared_ptr timelineSharedState; public: DSPContext(float vuAlpha, int channelCount, int sampleRate, bool isOffline, bool enableLimiter, const std::shared_ptr &analyticsObserver); ~DSPContext(); static DSPContextPromise create(const std::string &name, float vuAlpha, const std::shared_ptr &analyticsObserver, std::optional channelCount); static DSPContextPromise createOffline(const std::string &name, float vuAlpha, int channelCount, int sampleRate, bool enableLimiter); std::shared_ptr createTimeline( const std::shared_ptr &timing, const std::vector> &tracks); std::shared_ptr createTrack(float gain, float pan, bool muted, const std::shared_ptr &meter, const std::shared_ptr &filterChain, const std::vector> &clips); std::shared_ptr createAudioClip( std::shared_ptr underlyingReadable, std::shared_ptr> warpMap, float gain, double timelineStartBeats, double timelineEndBeats, double loopStartBeats, double loopEndBeats, double readStartBeats, double fadeInBeats, double fadeInExponent, double fadeOutBeats, double fadeOutExponent, float transposition, double warpedContentBps, bool warpEnabled, const std::string &clipId); std::shared_ptr createMeter(); std::shared_ptr createFilterChain(int filterCount); void swapLiveTimeline(const std::shared_ptr &newTimeline); std::shared_ptr createBouncer(double start, double end, int bufferSize); std::shared_ptr createSumOnlyBouncer(int bufferSize); AudioContextValType getAudioContext(); std::vector>> assignClipsToGroups(const std::vector> &allClips); const std::shared_ptr meter; const std::shared_ptr analyticsObserver; const std::shared_ptr mipMapWorker; void setLimiterParams(float lookaheadSeconds, float attackSeconds, float releaseSeconds, float preGainDb, bool bypass, float stereoLink); };