@@ -448,6 +448,13 @@ void M1PannerAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBloc
448448 postAlert (alert);
449449 }
450450 }
451+
452+ // Initialize memory sharing if in external spatial mixer mode
453+ // This ensures the memory file is created even before audio starts playing
454+ if (external_spatialmixer_active && !m_memoryShareInitialized)
455+ {
456+ initializeMemorySharing ();
457+ }
451458}
452459
453460void M1PannerAudioProcessor::releaseResources ()
@@ -1171,6 +1178,13 @@ void M1PannerAudioProcessor::timerCallback()
11711178 // Added if we need to move the OSC stuff from the processorblock
11721179 pannerOSC->update (); // test for connection
11731180
1181+ // Update memory sharing with current parameters (even when audio isn't playing)
1182+ // This ensures parameter changes are visible to the helper immediately
1183+ if (external_spatialmixer_active && m_memoryShareInitialized && m_memoryShare && m_memoryShare->isValid ())
1184+ {
1185+ updateMemorySharingParametersOnly ();
1186+ }
1187+
11741188 // ON-DEMAND SERVICE INTEGRATION: Periodic health check for helper service
11751189 // Check every ~10 seconds (timer runs every 200ms, so check every 50 calls)
11761190 static int healthCheckCounter = 0 ;
@@ -1593,12 +1607,105 @@ void M1PannerAudioProcessor::updateMemorySharing(const juce::AudioBuffer<float>&
15931607
15941608 // Add state and color info
15951609 parameters.addInt (M1PannerParameterIDs::STATE, pannerSettings.state .load ());
1610+ parameters.addInt (M1PannerParameterIDs::PORT, pannerSettings.port );
1611+
1612+ // Add color info
1613+ parameters.addInt (M1PannerParameterIDs::COLOR_R, static_cast <int32_t >(pannerSettings.color .r .load ()));
1614+ parameters.addInt (M1PannerParameterIDs::COLOR_G, static_cast <int32_t >(pannerSettings.color .g .load ()));
1615+ parameters.addInt (M1PannerParameterIDs::COLOR_B, static_cast <int32_t >(pannerSettings.color .b .load ()));
1616+ parameters.addInt (M1PannerParameterIDs::COLOR_A, static_cast <int32_t >(pannerSettings.color .a .load ()));
1617+
1618+ // Add display name (track name from host) - cache it from track_properties
1619+ // Note: track_properties is updated by host via updateTrackProperties()
1620+ std::string displayName;
1621+ if (track_properties.name .has_value () && !track_properties.name ->isEmpty ())
1622+ {
1623+ displayName = track_properties.name ->toStdString ();
1624+ }
1625+ else
1626+ {
1627+ // Fallback: use a generated name with the port/instance ID
1628+ displayName = " M1-Panner (" + std::to_string (pannerSettings.port ) + " )" ;
1629+ }
1630+ parameters.addString (M1PannerParameterIDs::DISPLAY_NAME, displayName);
15961631
15971632 // Write audio buffer with generic parameters
15981633 // Note: writeAudioBufferWithGenericParameters should be RT-safe internally
15991634 m_memoryShare->writeAudioBufferWithGenericParameters (inputBuffer, parameters, dawTimestamp, playheadPosition, isPlaying, true );
16001635}
16011636
1637+ void M1PannerAudioProcessor::updateMemorySharingParametersOnly ()
1638+ {
1639+ if (!m_memoryShare || !m_memoryShare->isValid ())
1640+ {
1641+ return ;
1642+ }
1643+
1644+ // Get DAW timestamp
1645+ uint64_t dawTimestamp = static_cast <uint64_t >(juce::Time::currentTimeMillis ());
1646+ double playheadPosition = 0.0 ;
1647+ bool isPlaying = false ;
1648+
1649+ // Get playhead info from DAW if available
1650+ if (auto * ph = getPlayHead ())
1651+ {
1652+ juce::AudioPlayHead::CurrentPositionInfo currentPlayHeadInfo;
1653+ if (ph->getCurrentPosition (currentPlayHeadInfo))
1654+ {
1655+ isPlaying = currentPlayHeadInfo.isPlaying ;
1656+ playheadPosition = currentPlayHeadInfo.timeInSeconds ;
1657+ }
1658+ }
1659+
1660+ // Create parameter map with current panner settings
1661+ ParameterMap parameters;
1662+ parameters.addFloat (M1PannerParameterIDs::AZIMUTH, pannerSettings.azimuth .load ());
1663+ parameters.addFloat (M1PannerParameterIDs::ELEVATION, pannerSettings.elevation .load ());
1664+ parameters.addFloat (M1PannerParameterIDs::DIVERGE, pannerSettings.diverge .load ());
1665+ parameters.addFloat (M1PannerParameterIDs::GAIN, pannerSettings.gain .load ());
1666+ parameters.addFloat (M1PannerParameterIDs::STEREO_ORBIT_AZIMUTH, pannerSettings.stereoOrbitAzimuth .load ());
1667+ parameters.addFloat (M1PannerParameterIDs::STEREO_SPREAD, pannerSettings.stereoSpread .load ());
1668+ parameters.addFloat (M1PannerParameterIDs::STEREO_INPUT_BALANCE, pannerSettings.stereoInputBalance .load ());
1669+ parameters.addBool (M1PannerParameterIDs::AUTO_ORBIT, pannerSettings.autoOrbit .load ());
1670+ parameters.addBool (M1PannerParameterIDs::ISOTROPIC_MODE, pannerSettings.isotropicMode .load ());
1671+ parameters.addBool (M1PannerParameterIDs::EQUALPOWER_MODE, pannerSettings.equalpowerMode .load ());
1672+ parameters.addBool (M1PannerParameterIDs::GAIN_COMPENSATION_MODE, pannerSettings.gainCompensationMode .load ());
1673+ parameters.addBool (M1PannerParameterIDs::LOCK_OUTPUT_LAYOUT, pannerSettings.lockOutputLayout .load ());
1674+
1675+ // Add input/output mode info
1676+ {
1677+ std::lock_guard<std::mutex> lock (pannerSettings.processingMutex );
1678+ parameters.addInt (M1PannerParameterIDs::INPUT_MODE, static_cast <int32_t >(pannerSettings.m1Encode .getInputMode ()));
1679+ parameters.addInt (M1PannerParameterIDs::OUTPUT_MODE, static_cast <int32_t >(pannerSettings.m1Encode .getOutputMode ()));
1680+ }
1681+
1682+ // Add state, port, and color info
1683+ parameters.addInt (M1PannerParameterIDs::STATE, pannerSettings.state .load ());
1684+ parameters.addInt (M1PannerParameterIDs::PORT, pannerSettings.port );
1685+ parameters.addInt (M1PannerParameterIDs::COLOR_R, static_cast <int32_t >(pannerSettings.color .r .load ()));
1686+ parameters.addInt (M1PannerParameterIDs::COLOR_G, static_cast <int32_t >(pannerSettings.color .g .load ()));
1687+ parameters.addInt (M1PannerParameterIDs::COLOR_B, static_cast <int32_t >(pannerSettings.color .b .load ()));
1688+ parameters.addInt (M1PannerParameterIDs::COLOR_A, static_cast <int32_t >(pannerSettings.color .a .load ()));
1689+
1690+ // Add display name (track name from host)
1691+ std::string displayName;
1692+ if (track_properties.name .has_value () && !track_properties.name ->isEmpty ())
1693+ {
1694+ displayName = track_properties.name ->toStdString ();
1695+ }
1696+ else
1697+ {
1698+ displayName = " M1-Panner (" + std::to_string (pannerSettings.port ) + " )" ;
1699+ }
1700+ parameters.addString (M1PannerParameterIDs::DISPLAY_NAME, displayName);
1701+
1702+ // Create an empty audio buffer (0 samples) for parameter-only updates
1703+ juce::AudioBuffer<float > emptyBuffer (2 , 0 ); // 2 channels, 0 samples
1704+
1705+ // Write parameters without audio data
1706+ m_memoryShare->writeAudioBufferWithGenericParameters (emptyBuffer, parameters, dawTimestamp, playheadPosition, isPlaying, false );
1707+ }
1708+
16021709// ON-DEMAND SERVICE INTEGRATION: Helper method to check service availability
16031710bool M1PannerAudioProcessor::isHelperServiceAvailable () const
16041711{
0 commit comments