Aestra follows a consistent C++17 coding style enforced by clang-format (LLVM-based). This guide covers naming conventions, formatting rules, and best practices.
| Element | Convention | Example |
|---|---|---|
| Classes/Structs | PascalCase | AudioEngine, TrackManager |
| Functions/Methods | camelCase | initialize(), setSampleRate() |
| Member variables | camelCase with trailing _ |
sampleRate_, bufferSize_ |
| Constants | SCREAMING_SNAKE_CASE | MAX_CHANNELS, DEFAULT_SAMPLE_RATE |
| Enums | PascalCase values | AudioState::Playing |
| Namespaces | lowercase | Aestra::core, Aestra::audio |
| Files | PascalCase | AudioEngine.h, TrackManager.cpp |
| Private members | Leading underscore or trailing _ |
internalState_ |
Formatting is enforced automatically by clang-format via pre-commit hooks. Run manually:
clang-format -i Source/**/*.cpp AestraAudio/**/*.cpp AestraUI/**/*.cpp
Key rules (from .clang-format):
int* ptr, not int *ptr)// 1. Corresponding header
#include "AudioEngine.h"
// 2. Aestra project headers
#include "AestraCore/Logger.h"
#include "AestraAudio/Types.h"
// 3. Third-party libraries
#include <rtaudio/RtAudio.h>
// 4. Standard library
#include <atomic>
#include <cstdint>
#include <memory>
#include <string>
Use #pragma once:
#pragma once
#include <cstdint>
namespace Aestra {
namespace audio {
class AudioEngine {
// ...
};
} // namespace audio
} // namespace Aestra
Organize class members in this order:
class AudioEngine {
public:
// Types and enums
enum class State { Stopped, Playing, Paused };
// Constructors / destructor
AudioEngine();
~AudioEngine();
// Public interface
bool initialize(const AudioConfig& config);
void start();
void stop();
protected:
// Protected members
private:
// Private methods
void processAudio(float* buffer, int frames);
// Private data members
State state_ = State::Stopped;
uint32_t sampleRate_ = 48000;
};
The audio thread has strict requirements:
new, malloc, std::vector::push_back, or std::string operationsstd::mutex::lock(), use lock-free structures insteadprintf, no networkstd::optional// Good: Lock-free, allocation-free audio callback
void AudioEngine::processAudio(float* buffer, int frames) {
// Read commands from lock-free queue
AudioCommand cmd;
while (commandQueue_.pop(cmd)) {
handleCommand(cmd); // Must also be RT-safe
}
// Process audio (no allocations, no locks)
mixer_.mix(buffer, frames);
}
bool for simple success/failurestd::optional for nullable returnsstd::expected or error codes for detailed errorsstd::optional<AudioDevice> getDefaultDevice();
bool initialize(const AudioConfig& config);
Use /** */ Doxygen-style comments for public APIs:
/**
* @brief Initialize the audio engine with the given configuration.
* @param config Audio configuration (sample rate, buffer size, etc.)
* @return true on success, false if the audio device cannot be opened.
*
* Must be called before start(). If initialization fails, check
* lastError() for details.
*/
bool initialize(const AudioConfig& config);
Follow conventional commits:
type(scope): subject
body (optional)
Closes #issue
Types: feat, fix, docs, style, refactor, test, chore
Examples:
feat(audio): add ASIO exclusive mode support
fix(ui): resolve click-through on dropdown menus
docs: update architecture overview for plugin system
Install hooks to enforce style automatically:
# Windows
pwsh -File scripts/install-hooks.ps1
# Linux
bash scripts/install-hooks.sh
The hooks run:
clang-format on all staged C++ files.clang-format — Full formatting rules.clang-tidy — Static analysis configurationLast updated: March 2026