Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a5d3df2
Copy build artefacts to working executable dir
malisimat Jun 12, 2026
3d1050b
Remove unneeded scripts
malisimat Jun 12, 2026
ab88b5d
Update instructions for building from fresh vcpkg
malisimat Jun 12, 2026
1aeaadb
Add implementation plan
malisimat Jun 16, 2026
96fef05
Complete MIDI automation session 1
malisimat Jun 17, 2026
581524e
Move automation specific methods out of Scene, allow multiple station…
malisimat Jun 17, 2026
7661e60
Updated plan ready for next session
malisimat Jun 17, 2026
c789378
Implement MIDI automation rendering, and update lane management
malisimat Jun 17, 2026
d9cf2a0
Auto add lane on VST editor change, overwrite existing automation on …
malisimat Jun 19, 2026
46c7757
Fx automation issues based on thorough review
malisimat Jun 20, 2026
f824278
Fix after adversarial review of repaired automation recording
malisimat Jun 20, 2026
8d0bcd7
Allow more automation points, use time-based merge
malisimat Jun 20, 2026
2443b37
Delete automation on MIDI loop ditch
malisimat Jun 20, 2026
734bc32
Move automation record arm to INSERT key global capture
malisimat Jun 20, 2026
b0c8c20
Remove dev artefacts
malisimat Jun 20, 2026
3452873
Remove old copilot instructions file
malisimat Jun 20, 2026
259fc82
Add VST3 support for automation recording and playback
malisimat Jun 21, 2026
85ad41d
Align max automation control points with shader uniform, and evict po…
malisimat Jun 21, 2026
b8ae9af
Implement seqlock mechanism for safe concurrent access to automation …
malisimat Jun 21, 2026
0848364
Refactor automation dispatch to separate playback state and ensure im…
malisimat Jun 21, 2026
5c4268b
Remove *ForTest functions in production code
malisimat Jun 21, 2026
7c24155
Move erroneous comment to correct line
malisimat Jun 21, 2026
f9e746a
Clean up duplicate inline helper and move out of header
malisimat Jun 21, 2026
bddadc7
Improve window location/size calcs for multi monitor setups
malisimat Jun 21, 2026
949e5fb
Fix failing test
malisimat Jun 21, 2026
ef7324f
Merge from master (includes GUI changes) to fix merge conflicts
malisimat Jun 21, 2026
176cc2e
Merge conflict resolution
malisimat Jun 21, 2026
6a269ca
Window resizes properly and avoids stretching
malisimat Jun 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions Jamma/Jamma.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,12 @@
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<AdditionalLibraryDirectories>$(ProjectDir)..\lib\vst2sdk\x64\Debug\MD;$(ProjectDir)..\lib\njclient\x64\Debug\MD;$(SolutionDir)vcpkg_installed\x64-windows\debug\lib\manual-link;$(SolutionDir)JammaLib\bin\x64\Debug;$(SolutionDir)vcpkg_installed\x64-windows\debug\lib\vst3sdk;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(ProjectDir)..\lib\vst2sdk\x64\Debug\MD;$(ProjectDir)..\lib\njclient\x64\Debug\MD;$(SolutionDir)vcpkg_installed\x64-windows\debug\lib;$(SolutionDir)vcpkg_installed\x64-windows\debug\lib\manual-link;$(SolutionDir)JammaLib\bin\x64\Debug;$(SolutionDir)vcpkg_installed\x64-windows\debug\lib\vst3sdk;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>vst2sdk.lib;njclient.lib;ogg.lib;vorbis.lib;vorbisenc.lib;vorbisfile.lib;ws2_32.lib;JammaLib.lib;sdk_hosting.lib;sdk.lib;sdk_common.lib;pluginterfaces.lib;base.lib;opengl32.lib;Comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>copy /Y "$(SolutionDir)vcpkg_installed\x64-windows\debug\bin\ogg.dll" "$(OutDir)"&#x0D;&#x0A;copy /Y "$(SolutionDir)vcpkg_installed\x64-windows\debug\bin\vorbis.dll" "$(OutDir)"&#x0D;&#x0A;copy /Y "$(SolutionDir)vcpkg_installed\x64-windows\debug\bin\vorbisenc.dll" "$(OutDir)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
Expand Down Expand Up @@ -180,9 +183,12 @@
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalLibraryDirectories>$(ProjectDir)..\lib\vst2sdk\x64\Release\MD;$(ProjectDir)..\lib\njclient\x64\Release\MD;$(SolutionDir)vcpkg_installed\x64-windows\lib\manual-link;$(SolutionDir)JammaLib\bin\x64\Release;$(ProjectDir)lib\x64;$(SolutionDir)vcpkg_installed\x64-windows\lib\vst3sdk;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(ProjectDir)..\lib\vst2sdk\x64\Release\MD;$(ProjectDir)..\lib\njclient\x64\Release\MD;$(SolutionDir)vcpkg_installed\x64-windows\lib;$(SolutionDir)vcpkg_installed\x64-windows\lib\manual-link;$(SolutionDir)JammaLib\bin\x64\Release;$(ProjectDir)lib\x64;$(SolutionDir)vcpkg_installed\x64-windows\lib\vst3sdk;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>vst2sdk.lib;njclient.lib;ogg.lib;vorbis.lib;vorbisenc.lib;vorbisfile.lib;ws2_32.lib;JammaLib.lib;sdk_hosting.lib;sdk.lib;sdk_common.lib;pluginterfaces.lib;base.lib;opengl32.lib;Comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>copy /Y "$(SolutionDir)vcpkg_installed\x64-windows\bin\ogg.dll" "$(OutDir)"&#x0D;&#x0A;copy /Y "$(SolutionDir)vcpkg_installed\x64-windows\bin\vorbis.dll" "$(OutDir)"&#x0D;&#x0A;copy /Y "$(SolutionDir)vcpkg_installed\x64-windows\bin\vorbisenc.dll" "$(OutDir)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\Main.cpp" />
Expand Down Expand Up @@ -249,6 +255,14 @@
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="resources\shaders\automation.frag">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\shaders\automation.vert">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="resources\shaders\quantisation.frag">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
Expand Down
6 changes: 6 additions & 0 deletions Jamma/Jamma.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="resources\shaders\automation.frag">
<Filter>resources\shaders</Filter>
</Content>
<Content Include="resources\shaders\automation.vert">
<Filter>resources\shaders</Filter>
</Content>
<Content Include="resources\shaders\texture.frag">
<Filter>resources\shaders</Filter>
</Content>
Expand Down
1 change: 1 addition & 0 deletions Jamma/resources/ResourceList.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
2 waveform MVP LoopState LoopHover TextureSampler WaveformSampler WaveformRadius WaveformHeightScale WaveformMinHeight WaveformColorMultiplier WaveformColorScale WaveformUnitMeshRadius
2 vu MVP DX DY NumInstances InstanceOffset
2 midi_note MVP ObjectId Highlight LoopHover DiscAlpha RenderMode
2 automation MVP
2 picker MVP ObjectId WaveformRadius WaveformUnitMeshRadius
2 colour MVP Color
2 quantisation MVP Highlight OverlayAlpha
Expand Down
76 changes: 76 additions & 0 deletions Jamma/resources/shaders/automation.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#version 330 core

in float vT; // loop position 0..1
in float vEdge; // 0 base .. 1 top
in float vHeight; // sampled automation value 0..1

out vec4 ColorOUT;

uniform vec3 LaneColor;
uniform float RecordGlow; // 0..1, lifts brightness while recording
uniform float PlayFrac;
uniform int RenderMode; // 0 curtain, 1 crown, 2 playhead, 3 dot

const int RenderModeCurtain = 0;
const int RenderModeCrown = 1;
const int RenderModePlayhead = 2;
const int RenderModeDot = 3;

// Wrapped distance from the play head in loop space, mapped so the curve is
// brightest at the current play position and fades around the ring (loop time).
float PlayTrail(float t)
{
float d = abs(t - PlayFrac);
d = min(d, 1.0 - d); // shortest way around the circle, 0..0.5
return 1.0 - smoothstep(0.0, 0.5, d);
}

void main()
{
float trail = PlayTrail(vT);

if (RenderMode == RenderModeDot)
{
// Soft glowing play marker with a bright core and falloff halo.
vec2 d = gl_PointCoord - vec2(0.5);
float r = length(d) * 2.0;
if (r > 1.0)
discard;
float core = 1.0 - smoothstep(0.0, 0.35, r);
float halo = 1.0 - smoothstep(0.35, 1.0, r);
vec3 col = mix(LaneColor, vec3(1.0), core * 0.8) + RecordGlow * 0.4;
float a = clamp(core + halo * 0.5, 0.0, 1.0);
ColorOUT = vec4(col, a);
return;
}

if (RenderMode == RenderModePlayhead)
{
// Thin bright vertical line at the play position, fading toward the base.
vec3 col = mix(LaneColor * 1.4, vec3(1.0), 0.5) + RecordGlow * 0.5;
float a = mix(0.15, 0.95, vEdge);
ColorOUT = vec4(col, a);
return;
}

if (RenderMode == RenderModeCrown)
{
// Glowing top ring crown: bright, pulses up while recording.
vec3 col = LaneColor * 1.6 + vec3(0.25) + RecordGlow * 0.6;
float a = clamp(0.45 + 0.55 * trail + RecordGlow * 0.2, 0.0, 1.0);
ColorOUT = vec4(col, a);
return;
}

// Curtain: height-tinted body, brightest near the play head, with a bright
// top edge band and a recording lift.
vec3 low = LaneColor * 0.35;
vec3 body = mix(low, LaneColor, vHeight);
float topBand = smoothstep(0.82, 1.0, vEdge);
vec3 col = body + topBand * (LaneColor * 0.6 + vec3(0.35));
col += RecordGlow * 0.35 * (0.4 + 0.6 * trail);

float alpha = mix(0.10, 0.55, trail);
alpha = clamp(alpha + topBand * 0.4 + RecordGlow * 0.15, 0.0, 0.9);
ColorOUT = vec4(col, alpha);
}
81 changes: 81 additions & 0 deletions Jamma/resources/shaders/automation.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#version 330 core

// Single attribute: x = circumferential position t in [0,1] around the loop,
// y = vertical edge selector (0 = curtain base, 1 = curtain top / crown).
layout(location = 0) in vec2 ParamIN;

out float vT; // loop position 0..1
out float vEdge; // 0 base .. 1 top
out float vHeight; // sampled automation value 0..1

uniform mat4 MVP;

// Sparse control points (frac, value), piecewise-linear in loop space.
const int MaxAutoPoints = 512;
uniform vec2 AutoPoints[MaxAutoPoints];
uniform int AutoPointCount;

uniform float LaneRadius;
uniform float LaneHeight;
uniform float PlayFrac;
uniform int RenderMode; // 0 curtain, 1 crown, 2 playhead, 3 dot

const float TwoPi = 6.28318530718;

const int RenderModeCurtain = 0;
const int RenderModeCrown = 1;
const int RenderModePlayhead = 2;
const int RenderModeDot = 3;

float SampleAutomation(float t)
{
if (AutoPointCount <= 0)
return 0.0;
if (t <= AutoPoints[0].x)
return AutoPoints[0].y;

int last = AutoPointCount - 1;
if (t >= AutoPoints[last].x)
return AutoPoints[last].y;

for (int i = 0; i < MaxAutoPoints - 1; ++i)
{
if (i + 1 > last)
break;

vec2 lo = AutoPoints[i];
vec2 hi = AutoPoints[i + 1];
if (t >= lo.x && t <= hi.x)
{
float span = hi.x - lo.x;
if (span <= 0.0)
return hi.y;
float f = (t - lo.x) / span;
return mix(lo.y, hi.y, f);
}
}
return AutoPoints[last].y;
}

void main()
{
// Playhead and dot are pinned to the current play position; curtain and crown
// sweep the whole loop using their per-vertex t.
float t = (RenderMode >= RenderModePlayhead) ? PlayFrac : ParamIN.x;
float edge = ParamIN.y;

float h = SampleAutomation(t);
vT = t;
vEdge = edge;
vHeight = h;

float angle = TwoPi * t;
float yTop = LaneHeight * h;
float y = mix(0.0, yTop, edge);

vec3 position = vec3(sin(angle) * LaneRadius, y, cos(angle) * LaneRadius);
gl_Position = MVP * vec4(position, 1.0);

if (RenderMode == RenderModeDot)
gl_PointSize = 18.0;
}
26 changes: 26 additions & 0 deletions Jamma/src/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,23 @@ namespace

return DefaultIniPath();
}

bool IsWindowPlacementVisible(const utils::Position2d& position, const utils::Size2d& size)
{
RECT workArea{};
if (!SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0))
return true;

const LONG left = static_cast<LONG>(position.X);
const LONG top = static_cast<LONG>(position.Y);
const LONG right = left + static_cast<LONG>(size.Width);
const LONG bottom = top + static_cast<LONG>(size.Height);

return right > workArea.left
&& bottom > workArea.top
&& left < workArea.right
&& top < workArea.bottom;
}
}

void SetupConsole()
Expand Down Expand Up @@ -318,6 +335,9 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmd
sceneParams.Position = defaults.value().WinPos;
sceneParams.Size = defaults.value().WinSize;

if (!IsWindowPlacementVisible(sceneParams.Position, sceneParams.Size))
sceneParams.Position = Window::Center(sceneParams.Size);

std::stringstream ss;
InitFile::ToStream(defaults.value(), ss);

Expand Down Expand Up @@ -358,6 +378,8 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmd
if (window.Create(hInstance, nCmdShow) != 0)
PostQuitMessage(1);

scene.value()->InitGlobalInsertCapture();

scene.value()->InitAudio();

MSG msg;
Expand All @@ -380,6 +402,10 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmd
if (!active)
break;

actions::KeyAction insertAction;
if (scene.value()->PumpGlobalInsertCapture(insertAction))
window.OnAction(insertAction);

window.Render();
window.Swap();

Expand Down
8 changes: 4 additions & 4 deletions JammaLib/JammaLib.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;NOMINMAX;_LIB;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;__WINDOWS_DS__;__WINDOWS_ASIO__;__WINDOWS_MM__;__STDC_LIB_EXT1__;JAMMA_VST3_ENABLED;JAMMA_VST2_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)..\lib\vst2sdk;$(ProjectDir)..\lib\njclient;$(ProjectDir)..\lib;$(ProjectDir)include;$(ProjectDir)src\base;$(ProjectDir)src\utils;$(ProjectDir)lib\opengl;$(ProjectDir)lib;$(ProjectDir)lib\rtaudio\include;$(VcpkgInstalledDir)$(VcpkgTriplet)\include\vst3sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)..\lib\vst2sdk;$(ProjectDir)..\lib\njclient;$(ProjectDir)..\lib;$(ProjectDir)include;$(ProjectDir)src\base;$(ProjectDir)src\utils;$(ProjectDir)lib\opengl;$(ProjectDir)lib;$(ProjectDir)lib\rtaudio\include;$(VcpkgInstalledDir)$(VcpkgTriplet)\include;$(VcpkgInstalledDir)$(VcpkgTriplet)\include\vst3sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
Expand All @@ -125,7 +125,7 @@
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;NOMINMAX;_LIB;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;__WINDOWS_DS__;__WINDOWS_ASIO__;__WINDOWS_MM__;__STDC_LIB_EXT1__;JAMMA_VST3_ENABLED;JAMMA_VST2_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)..\lib\vst2sdk;$(ProjectDir)..\lib\njclient;$(ProjectDir)..\lib;$(ProjectDir)include;$(ProjectDir)src\base;$(ProjectDir)src\utils;$(ProjectDir)lib\opengl;$(ProjectDir)lib;$(ProjectDir)lib\rtaudio\include;$(VcpkgInstalledDir)$(VcpkgTriplet)\include\vst3sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)..\lib\vst2sdk;$(ProjectDir)..\lib\njclient;$(ProjectDir)..\lib;$(ProjectDir)include;$(ProjectDir)src\base;$(ProjectDir)src\utils;$(ProjectDir)lib\opengl;$(ProjectDir)lib;$(ProjectDir)lib\rtaudio\include;$(VcpkgInstalledDir)$(VcpkgTriplet)\include;$(VcpkgInstalledDir)$(VcpkgTriplet)\include\vst3sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>include\stdafx.h</PrecompiledHeaderFile>
<AdditionalOptions>/FS %(AdditionalOptions)</AdditionalOptions>
Expand Down Expand Up @@ -155,7 +155,7 @@
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;NOMINMAX;_LIB;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;__WINDOWS_DS__;__WINDOWS_ASIO__;__WINDOWS_MM__;__STDC_LIB_EXT1__;JAMMA_VST3_ENABLED;JAMMA_VST2_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)..\lib\vst2sdk;$(ProjectDir)..\lib\njclient;$(ProjectDir)..\lib;$(ProjectDir)include;$(ProjectDir)src\base;$(ProjectDir)src\utils;$(ProjectDir)lib\opengl;$(ProjectDir)lib;$(ProjectDir)lib\rtaudio\include;$(VcpkgInstalledDir)$(VcpkgTriplet)\include\vst3sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)..\lib\vst2sdk;$(ProjectDir)..\lib\njclient;$(ProjectDir)..\lib;$(ProjectDir)include;$(ProjectDir)src\base;$(ProjectDir)src\utils;$(ProjectDir)lib\opengl;$(ProjectDir)lib;$(ProjectDir)lib\rtaudio\include;$(VcpkgInstalledDir)$(VcpkgTriplet)\include;$(VcpkgInstalledDir)$(VcpkgTriplet)\include\vst3sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>include\stdafx.h</PrecompiledHeaderFile>
<AdditionalOptions>/FS %(AdditionalOptions)</AdditionalOptions>
Expand Down Expand Up @@ -186,7 +186,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;NOMINMAX;_LIB;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;__WINDOWS_DS__;__WINDOWS_ASIO__;__WINDOWS_MM__;__STDC_LIB_EXT1__;JAMMA_VST3_ENABLED;JAMMA_VST2_ENABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)..\lib\vst2sdk;$(ProjectDir)..\lib\njclient;$(ProjectDir)..\lib;$(ProjectDir)include;$(ProjectDir)src\base;$(ProjectDir)src\utils;$(ProjectDir)lib\opengl;$(ProjectDir)lib;$(ProjectDir)lib\rtaudio\include;$(VcpkgInstalledDir)$(VcpkgTriplet)\include\vst3sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)..\lib\vst2sdk;$(ProjectDir)..\lib\njclient;$(ProjectDir)..\lib;$(ProjectDir)include;$(ProjectDir)src\base;$(ProjectDir)src\utils;$(ProjectDir)lib\opengl;$(ProjectDir)lib;$(ProjectDir)lib\rtaudio\include;$(VcpkgInstalledDir)$(VcpkgTriplet)\include;$(VcpkgInstalledDir)$(VcpkgTriplet)\include\vst3sdk;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>include\stdafx.h</PrecompiledHeaderFile>
<AdditionalOptions>%(AdditionalOptions)</AdditionalOptions>
Expand Down
11 changes: 10 additions & 1 deletion JammaLib/src/engine/LoopTake.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1338,7 +1338,14 @@ void LoopTake::Play(unsigned long index,
{
if (midiLoop->State() == midi::MidiLoopState::Recording)
{
midiLoop->EndRecord(midiLoopLength);
// Compute the global sample that corresponds to loop-relative position 0 so
// automation frac calculations stay in phase with _midiVisualPlayIndex.
// _midiVisualPlayIndex was just set to P0 = InitialMidiPlayIndex(...) above.
// At global sample `index`, the play cursor is at P0, so position 0 maps to
// global sample (index - P0). uint32_t wraps correctly.
const auto phaseAnchor = static_cast<std::uint32_t>(index)
- static_cast<std::uint32_t>(_midiVisualPlayIndex);
midiLoop->EndRecord(midiLoopLength, phaseAnchor);
midiLoop->QueueModelUpdateFromEvents(midiLoopLength, true);
}
}
Expand Down Expand Up @@ -2638,6 +2645,8 @@ void LoopTake::SetSampleRate(float sampleRate)
loop->SetSampleRate(sampleRate);
for (auto& loop : _backLoops)
loop->SetSampleRate(sampleRate);
for (auto& midiLoop : _midiLoops)
if (midiLoop) midiLoop->SetSampleRate(sampleRate);
}

void LoopTake::SetParentVisualScale(float scale) noexcept
Expand Down
9 changes: 9 additions & 0 deletions JammaLib/src/engine/LoopTake.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ namespace engine
std::shared_ptr<vst::IVstPlugin> GetVstPlugin(size_t index) const;
std::vector<io::JamFile::VstEntry> VstEntries() const;

// True if plugin is hosted by this take's VST chain. Identity comparison
// only; non-RT (reads the published chain). Used to resolve the owner of
// an editor-driven automation event.
bool OwnsPlugin(const vst::IVstPlugin* plugin) const noexcept
{
auto chain = _vstChain.load(std::memory_order_acquire);
return chain && chain->ContainsPlugin(plugin);
}

void Record(std::vector<unsigned int> channels,
std::string stationName,
std::vector<unsigned int> midiChannels = {},
Expand Down
Loading
Loading