Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 67

Real-Time Audio Processing Constraints

Recommended resources

Hello! Welcome back.

In our last lesson, we built a digital compressor and a look-ahead limiter. We saw that features like "look-ahead" introduce a deliberate delay (latency) and that the entire algorithm has a certain computational cost. These are not just implementation details; they are fundamental challenges in real-time signal processing.

This lesson builds directly on that foundation. Our goal is to describe the constraints of real-time audio processing, including buffer size, latency, and computational load. We will formalize the "rules of the road" that govern how audio effects are written, moving from the what (the algorithm) to the how (the practical constraints of running it). Your background in random processes and control systems, which involves analyzing system behavior over time, will provide a strong basis for understanding the trade-offs we'll discuss.

1. The Nature of the Real-Time Challenge

Real-time audio processing is a fascinating intersection of general-purpose computing and embedded systems-style constraints. To understand it, we first need to distinguish between two key aspects of performance: latency and throughput.

What is Low Latency C++? (Part 1) - Timur Doumler - CppNow 2023

The video 'What is Low Latency C++?' by Timur Doumler provides an excellent framework for understanding performance. This first segment defines latency and throughput and introduces the concept of a 'hot path,' which is central to our discussion.

Please watch from 06:14 to 14:30. Focus on: The distinction between latency and throughput. The concept of the audio processing 'hot path' and its deadline-driven nature. The explanation of how an audio callback works.

As the video explains, for audio processing, we are not primarily optimizing for throughput (how much total data we can process over a long time) but for latency (how quickly we can process a small chunk of data). Specifically, we are operating under a real-time constraint: we must finish processing a buffer of audio before the next one is due.

Hard, Firm, and Soft Real-Time Systems

This deadline-driven environment is common in many fields. Systems are often categorized by the consequence of missing a deadline:

  • Hard Real-Time: A missed deadline is a catastrophic system failure (e.g., an airplane's flight control system).
  • Firm Real-Time: A missed deadline makes the result for that interval useless, but the system can recover. The output quality is degraded.
  • Soft Real-Time: A missed deadline reduces the value of the result, but it may still be useful.

What is Low Latency C++? (Part 1) - Timur Doumler - CppNow 2023

This next segment from the same video defines these categories and explains why missing a deadline in audio results in an audible glitch.

Watch from 20:42 to 24:30. Pay attention to the definition of hard, firm, and soft real-time, and the physical reason for audio glitches (signal discontinuity).

Real-time audio on a consumer operating system (like Windows, macOS, or Linux) is a firm real-time problem. If our process function takes too long, the audio driver won't have new data. It might play a buffer of zeros or repeat the last buffer, creating a sharp discontinuity in the waveform. Your experience with signal processing tells you that a sharp discontinuity is rich in high-frequency components, which we perceive as an undesirable click or pop.


2. The Core Trade-Off: Buffer Size and Latency

The most fundamental parameter you can control that dictates the real-time schedule is the buffer size. This is the number of samples delivered by the host application (like a Digital Audio Workstation, or DAW) in a single processing callback.

Fixed vs. Variable Buffer Processing in Real-Time Audio ...

The article 'Fixed vs. Variable Buffer Processing' provides a superb, detailed analysis of the relationship between buffer size, latency, and performance. We'll start with the sections that quantify this relationship.

First, read the introductory section (up to 'Real-Time Safety Constraints') which frames the problem. Then, skip down and read the 'Latency Analysis' section. Focus on the simple formula provided.

As the article makes clear, the latency introduced by the audio buffer is directly proportional to its size and inversely proportional to the sample rate:

For example, at a standard sample rate of 48 kHz:

  • A buffer of 1024 samples yields ms of latency.
  • A buffer of 128 samples yields ms of latency.
  • A buffer of 32 samples yields ms of latency.

This reveals the central trade-off:

  • Small Buffers: Result in low latency, which is critical for live monitoring and virtual instruments. However, they give the CPU very little time to complete its work and increase the overhead, as the processing callback is invoked more frequently.
  • Large Buffers: Result in high latency, which is acceptable for mixing and mastering but not for real-time performance. They give the CPU a longer, more relaxed deadline and are more efficient, as the overhead of calling the function is smaller relative to the amount of work done.
Test your understanding!

You are designing a virtual instrument. A musician reports that it feels "sluggish" to play. They are using a sample rate of 44.1 kHz and a buffer size of 512 samples.

  1. What is the current latency just from the audio buffer?
  2. To make it feel more responsive, you advise them to switch to a buffer size of 64 samples. What is the new latency?
  3. What is the potential downside of making this change?
Show answer
  1. The current latency is ms. This is generally considered too high for a responsive feel when playing an instrument.
  2. The new latency is ms. This is excellent for live performance.
  3. The downside is a significantly increased computational load on the CPU. The processing callback will now be called times as often. If the virtual instrument's algorithm is complex, the CPU may not be able to finish its work within the new, much shorter deadline, leading to audio glitches.

3. Computational Load: The Golden Rules of the Audio Thread

To guarantee that we meet our deadline every single time—even under heavy load—we must treat the audio processing callback as a sacred, protected space. Any operation with an unpredictable or potentially long execution time is forbidden.

The operating system scheduler does not provide the hard guarantees a true real-time OS would. Therefore, the responsibility falls on us, the developers, to ensure our code is deterministic. This means we must be able to reason about its worst-case execution time (WCET).

Fixed vs. Variable Buffer Processing in Real-Time Audio ...

The same article, 'Fixed vs. Variable Buffer Processing', contains an excellent, concise list of these real-time safety constraints. It's a foundational set of rules for any real-time audio developer.

Please read the section titled 'Real-Time Safety Constraints'. For each prohibition, make sure you understand why it is forbidden.

To summarize these crucial rules, within the audio processing callback, you must NEVER:

  • Allocate or deallocate memory: new, delete, malloc, free, or any operation that might cause a standard library container like std::vector to reallocate. Heap operations can involve searching for memory blocks and may trigger system calls, making their execution time unbounded.
  • Acquire a blocking lock (mutex): If another thread holds the lock, the audio thread will be forced to wait for an unknown amount of time, guaranteeing a missed deadline. This can also lead to priority inversion.
  • Perform I/O: No file reading/writing, network requests, or console logging. These involve the operating system and hardware and have extremely high and variable latency.
  • Interact with the GUI: UI updates are handled on a separate, lower-priority thread.
  • Use exceptions: The process of stack unwinding to handle an exception is computationally expensive and its timing is not deterministic.
  • Use unbounded loops: Any loop must have a clear, predictable exit condition so you can calculate its worst-case execution time. while(true) loops are only acceptable if you can mathematically prove they will terminate within the deadline.

All "heavy lifting"—like allocating memory for delay lines, pre-calculating FFTs, or loading files—must be done outside the real-time thread, for example, during initialization (prepareToPlay) or on separate background threads.


4. A Deeper Dive: Safe Inter-Thread Communication

The "no blocking locks" rule raises a critical question: How do we safely pass data from other threads (like the GUI thread changing a knob) to the real-time audio thread?

This is one of the most complex problems in audio programming. The solutions involve specialized lock-free and wait-free data structures and algorithms. Because of your interest in deeper mathematical treatments, let's explore the problem space. We won't implement the solutions, but we will understand the principles that make them necessary.

Thread Synchronisation in Real-Time Audio Processing With RCU (Read-Copy-Update) - Timur Doumler ADC

The talk 'Thread Synchronisation in Real-Time Audio Processing' by Timur Doumler is a masterclass on this topic. It's aimed at C++ developers, but the core concepts are universal. The initial sections perfectly frame the problem and dismiss naive solutions.

Watch from 00:45 to 07:39. This segment establishes: The setup: A real-time thread reading values, while other threads write them. Why a simple std::atomic works for single values but fails for larger objects (like filter coefficients). The critical flaw in using a standard mutex with try_lock—the unlock() call is not real-time safe!

The video confirms and deepens what we've learned: standard synchronization primitives like mutexes are fundamentally unsuitable because they can cause the real-time thread to perform a system call or block.

So, what are the alternatives? The video goes on to discuss several advanced strategies. Here is a high-level summary of the principles:

  • Lock-Free FIFOs: Special queues can be used to stream data (like MIDI messages or automation events) from a writer thread to the reader thread without using locks.
  • Pointer Swapping: A common technique is to have the GUI thread prepare a new version of the data (e.g., a new set of filter coefficients). It then atomically swaps a pointer to point to this new data. The audio thread only ever reads via this atomic pointer.
  • Deferred Reclamation: The pointer swap creates a problem: when can you safely delete the old data? The audio thread might still be using it. The solution is called deferred reclamation. The old data is put aside, and only deleted later, once it's certain no real-time thread is still accessing it. Algorithms like Read-Copy-Update (RCU), originally from the Linux kernel, provide a formal, high-performance way to manage this.

These techniques are complex, but they all share one goal: to ensure the audio thread's read operation is wait-free (guaranteed to complete in a bounded number of steps) and involves no blocking or non-deterministic operations. The complexity is pushed onto the non-real-time writer threads, where it is safe to do more work.


Conclusion

In this lesson, we have defined the strict boundaries within which real-time audio processing must operate. The need for low latency and glitch-free performance imposes a set of constraints that transform general-purpose programming into a deterministic, deadline-driven discipline.

Key Takeaways:

  • Real-time audio is a firm real-time problem where missed deadlines cause audible glitches.
  • Latency is directly tied to buffer size; reducing one often comes at the cost of the other and increased CPU load.
  • The audio processing thread is a critical, real-time context where operations with unpredictable timing are forbidden. This includes memory allocation, blocking locks, and I/O.
  • Computational load must be managed by ensuring all algorithms have a predictable worst-case execution time (WCET).
  • Communication between the real-time audio thread and other threads must be handled with care, using lock-free and wait-free techniques to avoid blocking.

Preview of the Next Lesson:

We now have a solid grasp of the rules. In our next module, we'll dive into one of the most powerful—and computationally demanding—tools in all of signal processing: the Discrete Fourier Transform (DFT). We'll begin by tracing the historical development of the Fast Fourier Transform (FFT), from Gauss to Cooley and Tukey, understanding the innovations that made real-time spectral analysis possible. We will see how these powerful algorithms must be carefully implemented to respect the very constraints we've discussed today.