Audio music section
Music and Background Tracks¶
PixelRoot32 includes a complete music sequencing system through the MusicPlayer class. This allows you to create background music, adaptive soundtracks, and complex musical arrangements using the same NES-style audio channels.
🎵 Complete MusicPlayer Guide¶
For comprehensive MusicPlayer documentation with advanced examples, see the MusicPlayer Integration Guide.
Quick Music Example¶
#include "audio/MusicPlayer.h"
#include "audio/AudioMusicTypes.h"
using namespace pixelroot32::audio;
// Define a simple melody
static const MusicNote SIMPLE_MELODY[] = {
makeNote(INSTR_PULSE_LEAD, Note::C, 4, 0.25f), // C4 quarter note
makeNote(INSTR_PULSE_LEAD, Note::E, 4, 0.25f), // E4 quarter note
makeNote(INSTR_PULSE_LEAD, Note::G, 4, 0.25f), // G4 quarter note
makeNote(INSTR_PULSE_LEAD, Note::C, 5, 0.5f), // C5 half note
};
static const MusicTrack SIMPLE_TRACK = {
SIMPLE_MELODY,
sizeof(SIMPLE_MELODY) / sizeof(MusicNote),
true, // Loop enabled
WaveType::PULSE, // Use pulse wave
0.5f // 50% duty cycle
};
// Play the music
void MyScene::init() {
auto& musicPlayer = engine.getMusicPlayer();
musicPlayer.play(SIMPLE_TRACK);
musicPlayer.setTempoFactor(1.0f); // Normal speed
}
Music Integration Patterns¶
Adaptive Music System¶
class GameScene : public Scene {
private:
enum class MusicState { MENU, EXPLORATION, COMBAT, VICTORY };
MusicState currentMusic = MusicState::MENU;
public:
void updateMusic(float threatLevel) {
auto& musicPlayer = engine.getMusicPlayer();
MusicState targetState = MusicState::EXPLORATION;
if (threatLevel > 0.7f) targetState = MusicState::COMBAT;
if (playerWon) targetState = MusicState::VICTORY;
if (currentMusic != targetState) {
switch (targetState) {
case MusicState::COMBAT:
musicPlayer.setTempoFactor(1.3f); // Faster tempo
musicPlayer.play(COMBAT_MUSIC);
break;
case MusicState::VICTORY:
musicPlayer.setTempoFactor(1.0f);
musicPlayer.play(VICTORY_MUSIC);
break;
}
currentMusic = targetState;
}
}
};
Music with Sound Effects Mixing¶
void GameScene::playAttackSound() {
auto& musicPlayer = engine.getMusicPlayer();
auto& audioEngine = engine.getAudioEngine();
// Slight tempo reduction for dramatic effect
float originalTempo = musicPlayer.getTempoFactor();
musicPlayer.setTempoFactor(originalTempo * 0.95f);
// Play attack sound effect
audioEngine.playEvent({
WaveType::NOISE,
200.0f, // Low frequency for impact
0.8f, // High volume
0.1f // Short duration
});
// Restore tempo after delay
delay(100);
musicPlayer.setTempoFactor(originalTempo);
}
Advanced Music Features¶
Tempo Control¶
// Speed up music as difficulty increases
void GameScene::updateDifficulty(int score) {
auto& musicPlayer = engine.getMusicPlayer();
float tempo = 1.0f + (score / 1000.0f) * 0.5f; // 1.0x to 1.5x
musicPlayer.setTempoFactor(tempo);
}
Music Synchronization¶
void RhythmGameScene::update(unsigned long deltaTime) {
// Check if we're on a beat (every 0.5 seconds)
static float beatTimer = 0.0f;
beatTimer += deltaTime * 0.001f;
if (beatTimer >= 0.5f) {
beatTimer = 0.0f;
// Spawn enemy on beat
spawnEnemy();
// Flash screen on beat
screenFlash = 255;
}
}
Music Best Practices¶
- Memory Efficiency: Define music tracks as
static constto store in flash memory - Performance: Keep music tracks reasonably short (under 100 notes) and use looping
- Platform Considerations:
- ESP32: Music timing is sample-accurate, runs on Core 0
- Native: Full SDL2 audio backend with higher quality mixing
- User Experience: Provide volume controls and allow separate music/SFX muting
Common Music Patterns¶
8-Bar Blues Progression¶
static const MusicNote BLUES_PROGRESSION[] = {
// Bar 1-2: C7
makeNote(INSTR_PULSE_LEAD, Note::C, 4, 1.0f),
makeNote(INSTR_PULSE_LEAD, Note::E, 4, 1.0f),
makeNote(INSTR_PULSE_LEAD, Note::G, 4, 1.0f),
makeNote(INSTR_PULSE_LEAD, Note::Bb, 4, 1.0f),
// Continue with F7, G7, etc.
};
Arpeggio Pattern¶
static const MusicNote ARPEGGIO[] = {
makeNote(INSTR_TRIANGLE, Note::C, 4, 0.25f),
makeNote(INSTR_TRIANGLE, Note::E, 4, 0.25f),
makeNote(INSTR_TRIANGLE, Note::G, 4, 0.25f),
makeNote(INSTR_TRIANGLE, Note::C, 5, 0.25f),
// Descending
makeNote(INSTR_TRIANGLE, Note::G, 4, 0.25f),
makeNote(INSTR_TRIANGLE, Note::E, 4, 0.25f),
makeNote(INSTR_TRIANGLE, Note::C, 4, 0.25f),
makeRest(0.25f),
};
MusicPlayer API Reference¶
Key Methods¶
play(const MusicTrack& track)- Start playing a trackstop()- Stop playback and silence the channelpause()- Pause playback (time does not advance)resume()- Resume playback after pauseisPlaying() const- Check if track is currently playingsetTempoFactor(float factor)- Set global tempo (1.0 = normal, 2.0 = double speed)getTempoFactor() const- Get current tempo factor
MusicTrack Structure¶
struct MusicTrack {
const MusicNote* notes; // Array of notes
size_t count; // Number of notes
bool loop; // Whether to loop
WaveType channelType; // Wave type (PULSE, TRIANGLE, NOISE)
float duty; // Duty cycle for pulse waves
};
For complete MusicPlayer documentation, advanced examples, and troubleshooting, see the MusicPlayer Integration Guide.