02. Coding Sound in c++: the toolchain
April 18, 2026•988 words
Before writing code, it is necessary to understand the software environment that transforms human-readable text into a functioning audio plugin. This collection of software is referred to as the "toolchain." In this course, we utilize a specific set of tools standard in the Windows audio development industry. The process begins with the Source Code, which is the C++ text you write. This text is processed by a Compiler, a program that translates your commands into machine code (binary instructions) that the computer processor understands. On Windows, we use the Microsoft Visual C++ (MSVC) compiler. Finally, to manage these files and the compiler, we use an Integrated Development Environment (IDE), specifically Microsoft Visual Studio.
In addition to the standard C++ tools, we rely on the JUCE Framework. JUCE is a vast collection of pre-written code that handles the complex interaction between your plugin and the operating system. It manages audio device drivers, graphics rendering, and the specific formatting requirements of VST3 plugins so that you can focus entirely on the digital signal processing and synthesizer architecture.
Visual Studio
The first step is setting up the IDE. You must download and install Visual Studio Community (or Professional/Enterprise if you have a license). It is critical to distinguish this from "Visual Studio Code," which is a lightweight text editor and insufficient for the complex build management required in this course. You can find the right installere here: https://visualstudio.microsoft.com/.
During the installation process, the Visual Studio Installer presents a screen titled "Workloads." You must locate the set of tools labeled Desktop development with C++. Selecting this workload instructs the installer to download the MSVC compiler, the Windows SDK, and the necessary linker tools. Without this specific workload selected, Visual Studio will install as a C# or .NET environment and will be unable to compile audio plugins. Once selected, proceed with the installation and allow it to complete.

The First Project
With your IDE ready, you must set up the JUCE library. The best way to obtain the library is using Git to manage the framework versioning efficiently. You can download git for free from here: https://git-scm.com/.
Open your terminal or command prompt and navigate to your development directory. Run the command: git clone https://github.com/juce-framework/JUCE.git. This downloads the entire repository to your machine. It is highly recommended to treat JUCE as a submodule within your specific project repository rather than a global installation in C:\JUCE. This ensures that your project is always linked to the specific version of the framework it was built with, preventing "dependency hell" when you update the library later.
JUCE currently provides two distinct workflows for project management: the traditional Projucer application and native CMake support. The Projucer is a GUI-based tool that served as the entry point for years, generating project files through a visual interface. However, for this guide, we will bypass it entirely in favor of CMake. It offers a powerful meta-build system, ensuring your code remains portable, version-control friendly, and aligned with modern engineering practices.
Create a new folder for your project named CodingSoundsSynth. Inside, create a text file named CMakeLists.txt. This file replaces the old Projucer settings panel. In this file, you will define your project settings. Here is the content of the file:
cmake_minimum_required(VERSION 3.22)
project(CodingSoundsSynth VERSION 0.0.1)
# Add JUCE folder
option(JUCE_BUILD_EXAMPLES "Build JUCE examples" OFF)
option(JUCE_BUILD_EXTRAS "Build JUCE extras" OFF)
add_subdirectory(JUCE)
# Generate executable
juce_add_gui_app(
${PROJECT_NAME}
PRODUCT_NAME ${PROJECT_NAME}
VERSION "0.0.1"
)
target_sources(${PROJECT_NAME} PRIVATE
Source/Main.cpp
)
# Setup compiler
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
target_link_libraries(${PROJECT_NAME} PRIVATE
juce::juce_core
juce::juce_graphics
juce::juce_gui_basics
juce::juce_gui_extra
juce::juce_audio_utils
)
juce_generate_juce_header(${PROJECT_NAME})
Next, we need to write the minimum code necessary to run a bare-bones JUICE app. Create a Source directory in the project root, then add a Main.cpp file containing the following:
++
#include <JuceHeader.h>
// Main application window that inherits from JUCE's DocumentWindow
// Handles window creation, configuration, and lifecycle management
class MainWindow : public juce::DocumentWindow {
public:
// Constructor: Sets up the window with title, color scheme, and button configuration
MainWindow(juce::String name): DocumentWindow(
name,
// Get the default background color from the current look and feel
juce::Desktop::getInstance()
.getDefaultLookAndFeel()
.findColour(juce::ResizableWindow::backgroundColourId),
// Enable minimize, maximize, and close buttons
DocumentWindow::allButtons){
// Use the operating system's native title bar instead of JUCE's custom one
setUsingNativeTitleBar(true);
// Create and set an empty component as the window's content
setContentOwned(new juce::Component(), true);
// Platform-specific window configuration
#if JUCE_IOS || JUCE_ANDROID
// On mobile platforms, use fullscreen mode
setFullScreen(true);
#else
// On desktop platforms, setup a window with specific size and resizability
setResizable(true, false);
centreWithSize(300, 200);
#endif
setVisible(true);
}
// Requests the application to quit gracefully
void closeButtonPressed() override{
juce::JUCEApplication::getInstance()->systemRequestedQuit();
}
private:
// JUCE macro that prevents copying and detects memory leaks in debug builds
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow)
};
// Main application class that manages the application lifecycle
class CodingSoundsSynth : public juce::JUCEApplication {
public:
CodingSoundsSynth() {}
// Application settings
const juce::String getApplicationName() override {
return ProjectInfo::projectName;
}
const juce::String getApplicationVersion() override {
return ProjectInfo::versionString;
}
bool moreThanOneInstanceAllowed() override {
return true;
}
// Called when the application starts
void initialise(const juce::String& commandLine) override{
// Create the main window
mainWindow.reset(new MainWindow(getApplicationName()));
}
// Called when the application is shutting down
void shutdown() override{
mainWindow = nullptr;
}
// Called when the operating system requests the application to quit
void systemRequestedQuit() override{
quit();
}
private:
// Smart pointer to the main window, ensures proper cleanup when destroyed
std::unique_ptr<MainWindow> mainWindow;
};
// JUCE macro that creates the application entry point and initializes the JUCE framework
START_JUCE_APPLICATION(CodingSoundsSynth)
Everything is now set up and ready to go. Select 'CodingSoundsSynth' from the list of executable targets (often found in the top toolbar of your IDE) and hit run.
You will only see an empty window for now, but this is a major milestone. You have just successfully compiled and launched your very first JUCE application!