@@ -1851,24 +1851,18 @@ class AudioProcessorGraph::Pimpl
18511851
18521852 nodeStates.setState (settings);
18531853
1854- topologyChanged (UpdateKind::sync );
1854+ topologyChanged (RebuildKind::immediate );
18551855 }
18561856
18571857 void releaseResources ()
18581858 {
18591859 nodeStates.setState (nullopt );
1860- topologyChanged (UpdateKind::sync );
1860+ topologyChanged (RebuildKind::immediate );
18611861 }
18621862
18631863 void rebuild (UpdateKind updateKind)
18641864 {
1865- if (updateKind == UpdateKind::none)
1866- return ;
1867-
1868- if (updateKind == UpdateKind::sync && MessageManager::getInstance ()->isThisTheMessageThread ())
1869- handleAsyncUpdate ();
1870- else
1871- updater.triggerAsyncUpdate ();
1865+ rebuild (getRebuildKind (updateKind));
18721866 }
18731867
18741868 void reset ()
@@ -1918,16 +1912,67 @@ class AudioProcessorGraph::Pimpl
19181912 auto * getAudioThreadState () const { return renderSequenceExchange.getAudioThreadState (); }
19191913
19201914private:
1915+ enum class RebuildKind
1916+ {
1917+ none, // no rebuild
1918+ async, // always async on main thread
1919+ syncIfMainThread, // sync if the rebuild request is on the main thread, async otherwise
1920+ immediate, // synchronous regardless of the thread making the rebuild request
1921+ };
1922+
1923+ static RebuildKind getRebuildKind (UpdateKind kind)
1924+ {
1925+ switch (kind)
1926+ {
1927+ case UpdateKind::async:
1928+ return RebuildKind::async;
1929+
1930+ case UpdateKind::sync:
1931+ return RebuildKind::syncIfMainThread;
1932+
1933+ case UpdateKind::none:
1934+ return RebuildKind::none;
1935+ }
1936+
1937+ jassertfalse;
1938+ return RebuildKind::syncIfMainThread;
1939+ }
1940+
19211941 void setParentGraph (AudioProcessor* p) const
19221942 {
19231943 if (auto * ioProc = dynamic_cast <AudioGraphIOProcessor*> (p))
19241944 ioProc->setParentGraph (owner);
19251945 }
19261946
1927- void topologyChanged (UpdateKind updateKind)
1947+ void topologyChanged (UpdateKind kind)
1948+ {
1949+ topologyChanged (getRebuildKind (kind));
1950+ }
1951+
1952+ void topologyChanged (RebuildKind kind)
19281953 {
19291954 owner->sendChangeMessage ();
1930- rebuild (updateKind);
1955+ rebuild (kind);
1956+ }
1957+
1958+ void rebuild (RebuildKind kind)
1959+ {
1960+ if (kind == RebuildKind::none)
1961+ return ;
1962+
1963+ const auto immediate = kind == RebuildKind::immediate
1964+ || (kind == RebuildKind::syncIfMainThread
1965+ && MessageManager::getInstance ()->isThisTheMessageThread ());
1966+
1967+ if (immediate)
1968+ {
1969+ updater.cancelPendingUpdate ();
1970+ handleAsyncUpdate ();
1971+ }
1972+ else
1973+ {
1974+ updater.triggerAsyncUpdate ();
1975+ }
19311976 }
19321977
19331978 void handleAsyncUpdate ()
@@ -2399,6 +2444,40 @@ class AudioProcessorGraphTests final : public UnitTest
23992444 }
24002445 }
24012446
2447+ beginTest (" graph can be prepared and unprepared from a background thread" );
2448+ {
2449+ using UK = AudioProcessorGraph::UpdateKind;
2450+ AudioProcessorGraph graph;
2451+ auto nodeA = BasicProcessor::make ({}, MidiIn::no, MidiOut::yes);
2452+ auto nodeB = BasicProcessor::make ({}, MidiIn::yes, MidiOut::no);
2453+
2454+ auto * ptrA = nodeA.get ();
2455+ auto * ptrB = nodeB.get ();
2456+
2457+ const auto idA = graph.addNode (std::move (nodeA), {}, UK::none)->nodeID ;
2458+ const auto idB = graph.addNode (std::move (nodeB), {}, UK::none)->nodeID ;
2459+ expect (graph.addConnection ({ { idA, midiChannel }, { idB, midiChannel } }, UK::none));
2460+
2461+ expect (! ptrA->isPrepared ());
2462+ expect (! ptrB->isPrepared ());
2463+
2464+ std::ignore = std::async (std::launch::async, [&]
2465+ {
2466+ expect (! ptrA->isPrepared ());
2467+ expect (! ptrB->isPrepared ());
2468+
2469+ graph.prepareToPlay (44100 , 512 );
2470+
2471+ expect (ptrA->isPrepared ());
2472+ expect (ptrB->isPrepared ());
2473+
2474+ graph.releaseResources ();
2475+
2476+ expect (! ptrA->isPrepared ());
2477+ expect (! ptrB->isPrepared ());
2478+ });
2479+ }
2480+
24022481 beginTest (" large render sequence can be built" );
24032482 {
24042483 AudioProcessorGraph graph;
@@ -2453,8 +2532,8 @@ class AudioProcessorGraphTests final : public UnitTest
24532532 void changeProgramName (int , const String&) override {}
24542533 void getStateInformation (MemoryBlock&) override {}
24552534 void setStateInformation (const void *, int ) override {}
2456- void prepareToPlay (double , int ) override {}
2457- void releaseResources () override {}
2535+ void prepareToPlay (double , int ) override { prepared = true ; }
2536+ void releaseResources () override { prepared = false ; }
24582537 bool supportsDoublePrecisionProcessing () const override { return doublePrecisionSupported; }
24592538 bool isMidiEffect () const override { return {}; }
24602539 void reset () override {}
@@ -2510,11 +2589,14 @@ class AudioProcessorGraphTests final : public UnitTest
25102589
25112590 ProcessingPrecision getLastBlockPrecision () const { return blockPrecision; }
25122591
2592+ bool isPrepared () const { return prepared; }
2593+
25132594 private:
25142595 MidiIn midiIn;
25152596 MidiOut midiOut;
25162597 ProcessingPrecision blockPrecision = ProcessingPrecision (-1 ); // initially invalid
25172598 bool doublePrecisionSupported = true ;
2599+ bool prepared = false ;
25182600 };
25192601};
25202602
0 commit comments