Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@olilarkin
Copy link
Member

wip

@olilarkin olilarkin force-pushed the faust-back branch 2 times, most recently from 9fb405f to 1bd90d4 Compare April 21, 2025 19:04
@claude
Copy link

claude bot commented Dec 22, 2025

PR Review: Add FAUST back with interpreter backend, UI

Thank you for bringing FAUST support back to iPlug2! This is a significant feature addition. Here's my detailed review:

Overview

This PR re-introduces FAUST (Functional Audio Stream) DSP integration using the interpreter backend rather than LLVM JIT compilation. The PR includes 100 files with a new example project IPlugFaustDSP and core FAUST integration code in IPlug/Extras/Faust/.


Code Quality & Best Practices

Positive Aspects

  1. Architecture: Clean separation between IPlugFaust (abstract base) and FaustGen (interpreter-based implementation)
  2. Resource Management: Good use of std::unique_ptr for memory safety (IPlugFaustDSP.h:39-40, IPlugFaustGen.h:114)
  3. Thread Safety: Proper mutex usage in Factory class (IPlugFaustGen.cpp:50, 510)
  4. Following iPlug2 conventions: 2-space indentation, member variable naming (mCamelCase)

Issues Identified

1. Commented-Out Code (Medium Priority)

Multiple instances of large commented blocks that should be removed or addressed:

  • IPlugFaustDSP.cpp:12-24: Commented macOS-specific bundle path code
  • IPlugFaustDSP.cpp:38: Commented FAUST_BLOCK macro usage
  • IPlugFaustDSP.cpp:74-79: Commented UI controls
  • IPlugFaustGen.cpp:435-492: Entire CompileCPP() implementation commented out but returns false
  • IPlugFaust.cpp:180: Important comment about not updating IPlug parameters

Recommendation: Either implement these features or remove the commented code. Dead code creates maintenance burden.

2. TODO Comments (Medium Priority)

Several unresolved TODOs in production code:

  • IPlugFaust.cpp:44, 96-97: Hardcoded channel count (2 channels) needs flexibility
  • IPlugFaust.cpp:116: Inefficient sender implementation feeding same value repeatedly
  • IPlugFaustGen.cpp:413: Missing I/O validation handling
  • IPlugFaustGen.cpp:524: No verification of successful JIT compilation

Recommendation: Address these before merging or create follow-up issues.

3. Error Handling (High Priority)

IPlugFaustGen.cpp:98, 391, 409: Multiple assert() calls in production code:

assert(pFactory != nullptr);  // Line 98
assert(mDSP);                 // Line 391
assert((mDSP->getNumInputs() <= mMaxNInputs)...); // Line 409

Problem: Assertions are removed in release builds. Critical error conditions should have proper error handling.

Recommendation:

if (!pFactory) {
  DBGMSG("ERROR: Failed to create interpreter factory\n");
  return nullptr; // or handle gracefully
}

IPlugFaustGen.cpp:332: Assertion with helpful message but will crash in release:

assert(0 && "If you hit this assert it means the faust DSP file...");

4. Potential Memory Issues

FaustCode.hpp:77-78: Raw new/delete in generated code:

static Faust1SIG0* newFaust1SIG0() { return (Faust1SIG0*)new Faust1SIG0(); }
static void deleteFaust1SIG0(Faust1SIG0* dsp) { delete dsp; }

This is FAUST-generated code, but ensure lifecycle is properly managed.

IPlugFaustGen.cpp:138, 261: Raw new for dsp_poly:

dsp_poly* pDspPoly = new mydsp_poly(pMonoDSP, nVoices, true);

Should be managed by smart pointer or documented why raw pointer is necessary.


Potential Bugs

1. Auto-Recompile Timer (High Priority)

IPlugFaustGen.cpp:536: Incorrect this capture in static method:

void FaustGen::SetAutoRecompile(bool enable)
{
  if (enable) {
    if (sTimer == nullptr)
      sTimer = Timer::Create(std::bind(&FaustGen::OnTimer, this, std::placeholders::_1), ...);

Problem: SetAutoRecompile is static but uses this - this will bind the wrong instance or cause undefined behavior.

Fix: The timer needs to iterate over all instances or use a static callback.

2. File Watching Race Condition (Medium Priority)

IPlugFaustGen.cpp:508-520: File modification checking could miss rapid changes:

if (!Equal(newTime, oldTime)) {
  WDL_MutexLock lock(&mMutex);  // Lock AFTER checking, not before
  recompile = true;
  f.second->FreeDSPFactory();

Issue: TOCTOU (Time-of-check-time-of-use) - file could change between stat and lock acquisition.

Recommendation: Acquire lock before stat check, or document this is acceptable.

3. Missing NULL Check (Medium Priority)

IPlugFaustDSP.cpp:41: UI removal without null check:

GetUI()->RemoveAllControls();

Recommendation: Verify GetUI() returns valid pointer before use.


Performance Considerations

1. Realtime Safety Violation (Critical)

IPlugFaust.cpp:116-121: Loop in ProcessBlock doing repeated work:

for (auto s=0; s<nFrames; s++) {
  pSender->ProcessBlock(tmp, 1, mCtrlTagStart + i);
}

Problem: Calling ProcessBlock once per sample with duplicate value. Very inefficient and noted in TODO.

Recommendation: Redesign sender API or batch properly.

2. Lambda Capture Allocation (Medium Priority)

IPlugFaust.cpp:97: Lambda capture in audio callback:

[&](sample** inputs, sample** outputs, int nFrames) //TODO:: badness capture = allocated

Issue: While the TODO notes this, lambda captures in realtime code should be verified as non-allocating.

3. Goto Usage (Low Priority - Style)

IPlugFaustGen.cpp:158, 168, 179: Uses goto end; pattern:

goto end;
...
end:
  assert(pDSP);

Note: While not inherently bad, modern C++ prefers RAII patterns. Consider refactoring to early returns.


Security Concerns

1. Path Injection (Low - Development Only)

IPlugFaustDSP.cpp:32: Hardcoded path concatenation:

filePath.Append("/IPlugFaustDSP.dsp");

Note: Should be safe since this is development/debugging code, but validate paths if user-controllable.

2. DLL Hijacking (Low - Windows)

IPlugFaustGen.cpp:351: Direct DLL directory manipulation:

#ifdef OS_WIN
  SetDllDirectoryA(FAUST_DLL_PATH);
#endif

Recommendation: Ensure FAUST_DLL_PATH is properly validated and points to trusted location.


Test Coverage

Critical Issue: No test files found in this PR.

Recommendations:

  1. Add unit tests for:
    • Parameter mapping/synchronization
    • DSP compilation success/failure paths
    • File watching and auto-recompile
    • Multi-instance factory management
  2. Add integration tests for:
    • Example plugin loading/processing
    • MIDI handling
    • UI generation from FAUST code

Build System

macOS Build (build-faust-mac.sh)

  • ✅ Simple and functional
  • ⚠️ Line 20-22: Commented CMake commands - clarify if intentional
  • ✅ Uses make interp for interpreter backend

Windows Build (build-faust-win.ps1)

  • ⚠️ Very complex - builds LLVM from source
  • ⚠️ Line 38-113: Manual instructions to add LLVM libs - error-prone
  • ❌ Hardcoded path on line 35: C:\Users\oli\Dev\...
  • Issue: Script doesn't match "interpreter backend" claim - it's building LLVM JIT

Recommendation: If using interpreter backend only, simplify Windows build to match macOS approach.


Documentation

README.md Issues

  1. Line 9: "JIT compilation will only work on macOS and Windows" - but PR uses interpreter?
  2. Line 19: References FAUST_COMPILED macro but CompileCPP() is disabled
  3. Line 23-25: References libfaust compiler issues on Windows - still relevant?

Recommendation: Update README to reflect interpreter-based approach.


Summary

Must Fix Before Merge

  1. ❌ Fix static method this capture bug (IPlugFaustGen.cpp:536)
  2. ❌ Replace assertions with proper error handling
  3. ❌ Remove hardcoded paths in Windows build script
  4. ❌ Clarify Windows build approach (LLVM vs interpreter)
  5. ❌ Add basic tests

Should Fix

  1. ⚠️ Address all TODO comments or file issues
  2. ⚠️ Remove commented-out code
  3. ⚠️ Fix ProcessBlock sender inefficiency
  4. ⚠️ Add proper null checks
  5. ⚠️ Update documentation to match implementation

Nice to Have

  1. 💡 Replace goto with modern C++ patterns
  2. 💡 Add comprehensive test coverage
  3. 💡 Document threading model
  4. 💡 Add example of polyphonic FAUST DSP

This is great work bringing FAUST back to iPlug2! The core architecture is solid. Addressing the critical issues above will make this production-ready.

@claude
Copy link

claude bot commented Dec 22, 2025

PR Review: Add FAUST back with interpreter backend, UI

This PR re-introduces FAUST integration to iPlug2 using the interpreter backend instead of LLVM. Here's my comprehensive review:

Overview

The PR adds comprehensive FAUST support with JIT compilation capabilities and a complete example project. The implementation is substantial (87 files changed) and includes build scripts, core integration files, and a full example plugin.


Code Quality & Best Practices

Strengths:

  • Good separation of concerns with IPlugFaust (base), FaustGen (JIT), and IGraphicsFaustUI (UI builder)
  • Factory pattern for sharing DSP instances across multiple plugin instances
  • Proper use of C++17 features (std::unique_ptr, std::function, structured bindings)
  • Follows iPlug2 conventions: 2-space indentation, mMemberVariables, override keyword

Issues:

  1. IPlugFaustDSP.cpp:12-24 - Large block of commented-out macOS bundle path code. Should either remove or document why it's preserved.

  2. IPlugFaustDSP.cpp:37 - Commented CompileCPP() call. Clarify intent or remove.

  3. IPlugFaustDSP.cpp:74-79 - Multiple blocks of commented UI code. Clean up before merging.

  4. IPlugFaustGen.cpp:86-89 - Commented SVG generation code with no explanation. If not needed, remove.

  5. IPlugFaustGen.cpp:118 - Mystery comment: //WHAT IS THIS?. Either resolve or remove.

  6. IPlugFaustGen.cpp:435-492 - Entire CompileCPP() method is commented out but returns false. Either implement or document as unimplemented.


Potential Bugs & Issues

Critical:

  1. IPlugFaustGen.cpp:536 - Race condition in static timer creation:

    sTimer = Timer::Create(std::bind(&FaustGen::OnTimer, this, ...

    Using this in a static context is dangerous. Multiple instances could overwrite the timer with different this pointers.

  2. IPlugFaustGen.cpp:556 - Unsafe buffer clear in error case:

    memset(outputs[0], 0, nFrames * mMaxNOutputs * sizeof(sample));

    Only clears the first channel. Should clear all output channels:

    for (int i = 0; i < mMaxNOutputs; i++)
      memset(outputs[i], 0, nFrames * sizeof(sample));
  3. IPlugFaustGen.cpp:332 - Assert on missing DSP file will crash release builds. Consider graceful error handling.

Medium:

  1. IPlugFaustDSP.cpp:109 - OnIdle() contains only commented code. Either implement or remove override.

  2. IPlugFaustGen.h:98 - CreateFactoryFromSourceCode() returns raw pointer to factory-managed resource, unclear ownership.

  3. build-faust-mac.sh:23 - Hardcoded -j6 for parallel build. Should use $(nproc) or make configurable.

  4. build-faust-win.ps1 - No error handling for git/cmake commands. Add error checks.


Performance Considerations

Good:

  • Mutex protection for DSP recompilation (WDL_MutexLock)
  • Factory pattern avoids duplicate compilation
  • Oversampling support built-in

Concerns:

  1. IPlugFaustGen.cpp:510 - File stat polling every 5 seconds on timer thread. Consider using filesystem watchers on platforms that support them.

  2. IPlugFaustDSP.cpp:104 - Normalized→denormalized conversion on every param change. Could cache if performance-critical.

  3. IPlugFaustGen.cpp:274 - Recompiles all instances when source changes. Could be expensive with many instances.


Security Concerns

  1. IPlugFaustGen.h:77-78 - Default code uses format strings with channel counts. Ensure these are validated (they appear to be from MaxNChannels which should be safe).

  2. build-faust-win.ps1:11 - Uses specific LLVM commit hash (good), but no verification of source authenticity.

  3. IPlugFaustGen.cpp:332 - File path from constructor used directly in file operations. Validate paths to prevent directory traversal (should be okay since it's from bundle resources).

  4. README.md:22 - Notes app sandbox issues on macOS. Document security implications of disabling sandbox.


Test Coverage

Missing:

  • No unit tests for FAUST integration
  • No test coverage for error cases (compilation failures, missing files, invalid DSP)
  • No tests for thread safety of recompilation
  • Should add tests for parameter synchronization

Recommendation: Add at least basic smoke tests for:

  • DSP compilation from file
  • Parameter creation and updates
  • Recompilation on file change
  • Error handling for invalid FAUST code

Architecture & Design

Strengths:

  • Clean separation: IPlugFaust abstract interface works with both JIT and compiled C++
  • FAUST_COMPILED macro allows switching between JIT and ahead-of-time compilation
  • UI builder pattern with customizable control creation functions

Suggestions:

  1. IPlugFaustDSP.cpp:40-44 - Compile callback modifies UI from non-UI thread. Ensure thread-safety with IGraphics.

  2. IPlugFaustGen.h:194 - mOnCompileFunc called without thread safety documentation. Clarify threading model.

  3. FaustCode.hpp - 283-line auto-generated file checked into repo. Consider .gitignoring and generating at build time, or document why it's checked in.


Documentation

Good:

  • README.md explains key concepts
  • Conference paper reference for deep dive
  • Comments explain platform-specific issues

Needs Improvement:

  • No API documentation for SetCompileFunc, SetAutoRecompile
  • Thread safety guarantees not documented
  • Build script usage not documented (macOS/Windows procedures)

Platform-Specific Issues

Windows:

  1. build-faust-win.ps1:38-113 - Manual step requiring editing vcxproj is problematic. Should automate or provide script.
  2. README.md:23 - Notes LLVM issues on Windows but uses interpreter now. Update docs.

macOS:

  1. build-faust-mac.sh:20-22 - CMake commands commented out, only make interp runs. Clarify intent.
  2. Sandbox restrictions documented but no automated solution provided.

Style Compliance (per CLAUDE.md)

✅ 2-space indentation
✅ Member variables: mCamelCase
✅ Pointer args: pCamelCase
✅ C++17 features used appropriately
⚠️ Excessive commented code (should be removed)
✅ Unix line endings (except Windows scripts)


Summary & Recommendations

Before Merge:

  1. Fix critical bug in error-state buffer clearing (IPlugFaustGen.cpp:556)
  2. Fix timer race condition or document thread safety model
  3. Remove commented code blocks or add explanatory comments
  4. Add error handling to build scripts
  5. Document threading model for recompilation callbacks
  6. Update README to reflect interpreter backend (not LLVM)

Post-Merge:

  1. Add unit tests for core functionality
  2. Consider filesystem watchers for better file change detection
  3. Automate Windows build process
  4. Add user documentation for creating FAUST-based plugins

Overall Assessment:
This is a valuable feature addition with solid architecture. The interpreter backend is a good choice over LLVM for complexity reasons. However, several critical bugs need fixing before merge, and the extensive commented code should be cleaned up. The lack of tests is concerning for such a complex feature.

Recommendation: Request changes to address critical bugs and code cleanup before merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants