CRM64Pro GDK v0.80.0
A free cross-platform game development kit built on top of SDL 3.0
Loading...
Searching...
No Matches
Tutorial 09: Audio playback

Overview

This tutorial extends Tutorial 08 with AudioTrack resources. It packages MP3, WAV, FLAC and OGG files into the shared Tutorial.cdc archive, loads them back and mixes looping music with sound effects.

The example also demonstrates track tags, fading, global playback controls and master gain.

Final result

CRM64Pro Tutorial 09 audio controls

Runtime controls

  • 1: Fade in looping music.
  • 2 / 3 / 4: Play the WAV, FLAC or OGG track.
  • P / R: Pause or resume all tracks.
  • S: Stop all tracks.
  • F: Fade out tracks tagged as music.
  • Plus / Minus: Change master gain.
  • D: Show or hide the debug window.
  • Grave key: Show or hide the built-in console.
  • Q or ESC: Exit the tutorial.

Prerequisites

  • CRM64Pro GDK installed and configured with a supported C++17 compiler.
  • Tutorial package downloaded and fully extracted, preserving its folder structure.
  • Run Tutorials 02 and 04 first so Tutorial.cdc contains the background and cursor resources.
  • Included assets available under bin/Base/: music.mp3, music.wav, music.flac, music.ogg.
  • A working audio output device.
  • Write access to the platform output directory.

What you will learn

  • Initialize and close the audio device.
  • Load AudioTrack resources from external files.
  • Keep source bytes temporarily so tracks can be saved.
  • Save and load audio through a CDC archive.
  • Use music, sound-effect and voice tags.
  • Mix looping music with one-shot tracks.
  • Fade, pause, resume and stop playback.
  • Change the mixer master gain.

Step by step

1. Initialize the audio device

The screen is created first, then ConfigMgr opens the mixer before any tracks are loaded or played.

mLog.msg(LL_INFO, " Initialize audio device ... ");
if(!mC64.configMgr().audioInit())
{
logTaskFailed(mLog);
Main::terminate();
return -1;
}
logTaskOk(mLog);

2. Verify the shared archive

The audio resources extend the archive created by earlier tutorials. Source bytes are retained only while the external tracks are being packaged.

mLog.msg(LL_INFO, " Find Tutorial 04 CDC: %s ... ", OUTPUT_CDC);
if(!mC64.tool().fileExists(OUTPUT_CDC))
{
logTaskFailed(mLog);
return false;
}
logTaskOk(mLog);
// Keep raw source bytes only while packaging the tracks into the CDC.
mAudioMgr.setKeepSourceData(true);

3. Save a loaded track

AudioTrack::save() requires the source bytes retained during loading. The helper validates the track before writing it to the archive.

// Save an audio track to the tutorial archive.
static bool saveAudioTrackToCDC(AudioTrackMgr& mAudioMgr, Log& mLog, Sint32 idTrack, const char* szResourceName)
{
AudioTrack* pTrack = mAudioMgr.get(idTrack);
if(idTrack < 0 || pTrack == nullptr)
{
logTaskFailed(mLog, idTrack);
return false;
}
if(pTrack->save(OUTPUT_CDC, szResourceName) < 0)
{
logTaskFailed(mLog);
return false;
}
logTaskOk(mLog);
return true;
}

4. Package tracks by format and tag

Each source file receives a stable CDC name and a playback tag. CDC audio is loaded with preload mode because archive streaming is not supported.

mLog.msg(LL_INFO, " Load music track: %s ... ", RESOURCE_AUDIO_MUSIC_FILE);
Sint32 idMusicFile = mAudioMgr.loadFromFile(RESOURCE_AUDIO_MUSIC_FILE, RESOURCE_AUDIO_MUSIC_CDC_NAME, ATT_MUSIC, ALM_PRELOAD);
if(idMusicFile < 0)
{
logTaskFailed(mLog, idMusicFile);
closeTemporaryAudioTracks(mAudioMgr);
return false;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Save music track to CDC ... ");
if(!saveAudioTrackToCDC(mAudioMgr, mLog, idMusicFile, RESOURCE_AUDIO_MUSIC_CDC_NAME))
{
closeTemporaryAudioTracks(mAudioMgr);
return false;
}
mLog.msg(LL_INFO, " Load OGG voice-style sound: %s ... ", RESOURCE_AUDIO_VOICE_FILE);
Sint32 idVoiceFile = mAudioMgr.loadFromFile(RESOURCE_AUDIO_VOICE_FILE, RESOURCE_AUDIO_VOICE_CDC_NAME, ATT_VOICE, ALM_PRELOAD);
if(idVoiceFile < 0)
{
logTaskFailed(mLog, idVoiceFile);
closeTemporaryAudioTracks(mAudioMgr);
return false;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Save OGG voice-style sound to CDC ... ");
if(!saveAudioTrackToCDC(mAudioMgr, mLog, idVoiceFile, RESOURCE_AUDIO_VOICE_CDC_NAME))
{
closeTemporaryAudioTracks(mAudioMgr);
return false;
}

5. Release temporary source data

Close the packaging tracks and restore the default memory policy before loading runtime copies from the CDC.

// Close the tracks opened for temporary processing.
static void closeTemporaryAudioTracks(AudioTrackMgr& mAudioMgr)
{
mAudioMgr.close(0);
mAudioMgr.setKeepSourceData(false);
}
mLog.msg(LL_INFO, " Close temporary audio tracks ... ");
closeTemporaryAudioTracks(mAudioMgr);
logTaskOk(mLog);

6. Load runtime tracks from CDC

The same helper loads each named resource and confirms that its AudioTrack is available from AudioTrackMgr.

// Load an audio track from the tutorial archive.
static bool loadAudioTrackFromCDC(AudioTrackMgr& mAudioMgr, Log& mLog, Sint32& rIDTrack, const char* szResourceName)
{
rIDTrack = mAudioMgr.load(OUTPUT_CDC, szResourceName);
if(rIDTrack < 0 || mAudioMgr.get(rIDTrack) == nullptr)
{
logTaskFailed(mLog, rIDTrack);
return false;
}
logTaskOk(mLog);
return true;
}

7. Play individual tracks

Music uses AudioTrack::fadeIn() with infinite looping. The other controls call AudioTrack::play() through a small result-checking helper.

// Play a track and record the requested action.
static void playTrack(AudioTrackMgr& mAudioMgr, Log& mLog, TutorialState& rState, Sint32 idTrack, const char* szAction)
{
AudioTrack* pTrack = mAudioMgr.get(idTrack);
if(!pTrack)
{
mLog.msg(LL_ERROR, " Audio track not loaded\n");
setLastAction(rState, "Audio track not loaded");
return;
}
if(pTrack->play() == 0)
{
setLastAction(rState, szAction);
mLog.msg(LL_INFO, " %s\n", szAction);
}
else
{
setLastAction(rState, "Playback failed");
mLog.msg(LL_ERROR, " Playback failed\n");
}
}
else if(event.key.key == SDLK_1)
{
AudioTrack* pMusic = mAudioMgr.get(state.idMusic);
if(pMusic && pMusic->fadeIn(700, -1) == 0)
{
setLastAction(state, "Looping music fading in");
mLog.msg(LL_INFO, " Looping music fading in\n");
}
else
{
setLastAction(state, "Music playback failed");
mLog.msg(LL_ERROR, " Music playback failed\n");
}
}

8. Control tracks globally and by tag

The manager can pause, resume or stop every track. The music tag limits the fade-out request to music and its result prevents a false success message.

else if(event.key.key == SDLK_P)
{
mAudioMgr.pauseAll();
setLastAction(state, "All tracks paused");
mLog.msg(LL_INFO, " All tracks paused\n");
}
else if(event.key.key == SDLK_R)
{
mAudioMgr.resumeAll();
setLastAction(state, "All tracks resumed");
mLog.msg(LL_INFO, " All tracks resumed\n");
}
else if(event.key.key == SDLK_S)
{
mAudioMgr.stopAll();
setLastAction(state, "All tracks stopped");
mLog.msg(LL_INFO, " All tracks stopped\n");
}
else if(event.key.key == SDLK_F)
{
if(mAudioMgr.fadeOutTag(ATT_MUSIC, 1200))
{
setLastAction(state, "Music fading out");
mLog.msg(LL_INFO, " Music fading out\n");
}
else
{
setLastAction(state, "Music fade-out failed");
mLog.msg(LL_ERROR, " Music fade-out failed\n");
}
}

9. Change master gain

The plus and minus controls clamp mixer gain to the documented 0.0-2.0 range.

else if(event.key.key == SDLK_PLUS || event.key.key == SDLK_KP_PLUS)
{
state.fMasterGain += 0.1f;
if(state.fMasterGain > 2.0f) state.fMasterGain = 2.0f;
mC64.configMgr().audioSetMasterGain(state.fMasterGain);
setLastAction(state, "Master gain increased");
mLog.msg(LL_INFO, " Master gain: %.1f\n", state.fMasterGain);
}
else if(event.key.key == SDLK_MINUS || event.key.key == SDLK_KP_MINUS)
{
state.fMasterGain -= 0.1f;
if(state.fMasterGain < 0.0f) state.fMasterGain = 0.0f;
mC64.configMgr().audioSetMasterGain(state.fMasterGain);
setLastAction(state, "Master gain decreased");
mLog.msg(LL_INFO, " Master gain: %.1f\n", state.fMasterGain);
}

10. Finish the loop cleanly

A quit request exits before another state update.

if(!bRunning) break;
state.fMouseX = mC64.cursorMgr().getX();
state.fMouseY = mC64.cursorMgr().getY();
state.iCurrentRenderRate = static_cast<Sint32>(mC64.timer().getCurrentRFR());
state.iCurrentLogicRate = static_cast<Sint32>(mC64.timer().getCurrentLFR());
updateLogic(state);

11. Stop playback before closing audio

Tracks are stopped and closed before the mixer device. Cursor-owned Images are also released before ImageMgr.

mLog.msg(LL_INFO, " Stop and close audio tracks ... ");
mAudioMgr.stopAll();
mAudioMgr.close(0);
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close audio device ... ");
mC64.configMgr().audioClose();
logTaskOk(mLog);

Complete source

The tutorial source is authoritative. The following listing is included directly from validation/source/Tutorial_09_Audio.cpp.

/* ----------------------------------------------------------------------
CRM64Pro GDK - Roberto Prieto
Copyright (C) 2013-2026 MegaStorm Systems
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
------------------------------------------------------------------------
Tutorial 09: Audio
------------------------------------------------------------------------
Overview:
This tutorial extends Tutorial 08 Sprite with audio tracks.
It removes sprite rendering, adds audio files to Tutorial.cdc, loads them
from the CDC, and mixes looping music with sound effects.
Key concepts:
- Initialize the audio device.
- Load AudioTrack resources from files.
- Save AudioTrack resources to a CDC file.
- Load AudioTrack resources from a CDC file.
- Play music and sound effects at the same time.
- Control audio by track and by tag.
Controls:
- 1: Fade in looping music.
- 2: Play WAV sound effect.
- 3: Play FLAC sound effect.
- 4: Play OGG voice-style sound.
- P / R: Pause or resume all tracks.
- S: Stop all tracks.
- F: Fade out music.
- + / -: Change master gain.
- D: Toggle debug window.
- Grave: Toggle console.
- Q / ESC: Quit.
Loop order:
- Process input events.
- Run one fixed logic update.
- Let Main::update() call the screen render callback.
------------------------------------------------------------------------ */
#include "CRM64Pro.h"
#include "RunParameter.h"
using namespace CRM64Pro;
#define BASEDIR "Base/"
#ifdef CRM64PRO_PLATFORM_WINDOWS
#define OUTPUTDIR "Win64/"
#elif defined(CRM64PRO_PLATFORM_LINUX)
#define OUTPUTDIR "Linux/"
#elif defined(CRM64PRO_PLATFORM_MACOS)
#define OUTPUTDIR "macOS/"
#endif
#define RESOURCE_AUDIO_MUSIC_FILE BASEDIR"music.mp3"
#define RESOURCE_AUDIO_SFX_FILE BASEDIR"music.wav"
#define RESOURCE_AUDIO_FLAC_FILE BASEDIR"music.flac"
#define RESOURCE_AUDIO_VOICE_FILE BASEDIR"music.ogg"
#define RESOURCE_BG_CDC_NAME "Tutorial_bg"
#define RESOURCE_CURSOR_CDC_NAME "Tutorial_cursor"
#define RESOURCE_AUDIO_MUSIC_CDC_NAME "Tutorial_audio_music"
#define RESOURCE_AUDIO_SFX_CDC_NAME "Tutorial_audio_sfx"
#define RESOURCE_AUDIO_FLAC_CDC_NAME "Tutorial_audio_flac"
#define RESOURCE_AUDIO_VOICE_CDC_NAME "Tutorial_audio_voice"
#define OUTPUT_CDC OUTPUTDIR"Tutorial.cdc"
// Render rate 0 lets the screen render as often as possible. Logic rate controls counters.
static const Sint32 iLogicRate = 20;
static const Sint32 iRenderRate = 0;
// Window size.
static const Sint32 iScreenW = 960;
static const Sint32 iScreenH = 540;
// Values changed while the tutorial runs.
struct TutorialState
{
Sint32 idBgImage = -1;
Sint32 idCursor = -1;
Sint32 idMusic = -1;
Sint32 idSFX = -1;
Sint32 idFLAC = -1;
Sint32 idVoice = -1;
Sint32 idBuiltInFont = -1;
Sint32 idDebugWindow = -1;
float fMouseX = 0.0f;
float fMouseY = 0.0f;
float fMasterGain = 1.0f;
Sint32 iCurrentRenderRate = 0;
Sint32 iCurrentLogicRate = 0;
Sint32 iLogicFrames = 0;
char szLastAction[128] = "Ready";
};
// Helper functions used by the setup log messages.
static void logTaskOk(Log& mLog)
{
mLog.msg(LL_DEBUG, "OK\n");
}
// Record a failed tutorial task.
static void logTaskFailed(Log& mLog, Sint32 iCode)
{
mLog.msg(LL_ERROR, "FAILED (Code %d)\n", iCode);
}
// Record a failed tutorial task.
static void logTaskFailed(Log& mLog)
{
mLog.msg(LL_ERROR, "FAILED\n");
}
// Store the action shown by the tutorial.
static void setLastAction(TutorialState& rState, const char* szAction)
{
snprintf(rState.szLastAction, sizeof(rState.szLastAction), "%s", szAction);
}
// Update the tutorial state for the current frame.
static void updateLogic(TutorialState& rState)
{
rState.iLogicFrames++;
}
// Draw text at the requested screen position.
static void renderText(Font* pFont, Sint32 iX, Sint32 iY, const char* szText)
{
if(!pFont) return;
pFont->setPosition(iX, iY);
pFont->render(szText);
}
// Draw the tutorial status information.
static void renderHUD(Font* pBuiltInFont, const TutorialState& rState)
{
if(!pBuiltInFont) return;
char szLine[160];
snprintf(szLine, sizeof(szLine), "Render: %d fps Logic: %d fps Master gain: %.1f", rState.iCurrentRenderRate, rState.iCurrentLogicRate, rState.fMasterGain);
renderText(pBuiltInFont, 10, 10, szLine);
renderText(pBuiltInFont, 10, 55, "Audio controls");
renderText(pBuiltInFont, 10, 82, "1 Fade in looping music");
renderText(pBuiltInFont, 10, 106, "2 Play WAV sound effect");
renderText(pBuiltInFont, 10, 130, "3 Play FLAC sound effect");
renderText(pBuiltInFont, 10, 154, "4 Play OGG voice-style sound");
renderText(pBuiltInFont, 10, 178, "P/R Pause or resume all tracks");
renderText(pBuiltInFont, 10, 202, "S Stop all tracks");
renderText(pBuiltInFont, 10, 226, "F Fade out music");
renderText(pBuiltInFont, 10, 250, "+/- Change master gain");
renderText(pBuiltInFont, 10, 274, "D Toggle debug window");
snprintf(szLine, sizeof(szLine), "Last action: %s", rState.szLastAction);
renderText(pBuiltInFont, 10, 330, szLine);
}
// Draw the current frame. CursorMgr draws the selected cursor automatically.
static void renderFrame(Image* pBgImage, Font* pBuiltInFont, const TutorialState& rState)
{
if(pBgImage) pBgImage->render();
renderHUD(pBuiltInFont, rState);
}
// Screen render callback.
// Main::update() calls this when the screen is ready to render.
static Sint32 renderWrapper(Sint32, void* pObj)
{
TutorialState* pState = static_cast<TutorialState*>(pObj);
if(!pState) return 0;
Main& mC64 = Main::instance();
Image* pBgImage = mC64.imageMgr().get(pState->idBgImage);
Font* pBuiltInFont = mC64.fontMgr().get(pState->idBuiltInFont);
renderFrame(pBgImage, pBuiltInFont, *pState);
return 0;
}
// Show or hide the debug window.
static void toggleDebugWindow(DebugWindow* pDebug)
{
if(!pDebug) return;
if(pDebug->baseWidget().status() == GS_SHOWN) pDebug->baseWidget().hide();
else pDebug->baseWidget().show();
}
// Save an audio track to the tutorial archive.
static bool saveAudioTrackToCDC(AudioTrackMgr& mAudioMgr, Log& mLog, Sint32 idTrack, const char* szResourceName)
{
AudioTrack* pTrack = mAudioMgr.get(idTrack);
if(idTrack < 0 || pTrack == nullptr)
{
logTaskFailed(mLog, idTrack);
return false;
}
if(pTrack->save(OUTPUT_CDC, szResourceName) < 0)
{
logTaskFailed(mLog);
return false;
}
logTaskOk(mLog);
return true;
}
// Close the tracks opened for temporary processing.
static void closeTemporaryAudioTracks(AudioTrackMgr& mAudioMgr)
{
mAudioMgr.close(0);
mAudioMgr.setKeepSourceData(false);
}
// Add the audio resources to the tutorial archive.
static bool addAudioToCDC(Main& mC64, Log& mLog)
{
AudioTrackMgr& mAudioMgr = mC64.audioTrackMgr();
mLog.msg(LL_INFO, " Find Tutorial 04 CDC: %s ... ", OUTPUT_CDC);
if(!mC64.tool().fileExists(OUTPUT_CDC))
{
logTaskFailed(mLog);
return false;
}
logTaskOk(mLog);
// Keep raw source bytes only while packaging the tracks into the CDC.
mAudioMgr.setKeepSourceData(true);
mLog.msg(LL_INFO, " Load music track: %s ... ", RESOURCE_AUDIO_MUSIC_FILE);
Sint32 idMusicFile = mAudioMgr.loadFromFile(RESOURCE_AUDIO_MUSIC_FILE, RESOURCE_AUDIO_MUSIC_CDC_NAME, ATT_MUSIC, ALM_PRELOAD);
if(idMusicFile < 0)
{
logTaskFailed(mLog, idMusicFile);
closeTemporaryAudioTracks(mAudioMgr);
return false;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Save music track to CDC ... ");
if(!saveAudioTrackToCDC(mAudioMgr, mLog, idMusicFile, RESOURCE_AUDIO_MUSIC_CDC_NAME))
{
closeTemporaryAudioTracks(mAudioMgr);
return false;
}
mLog.msg(LL_INFO, " Load WAV sound effect: %s ... ", RESOURCE_AUDIO_SFX_FILE);
Sint32 idSFXFile = mAudioMgr.loadFromFile(RESOURCE_AUDIO_SFX_FILE, RESOURCE_AUDIO_SFX_CDC_NAME, ATT_SFX, ALM_PRELOAD);
if(idSFXFile < 0)
{
logTaskFailed(mLog, idSFXFile);
closeTemporaryAudioTracks(mAudioMgr);
return false;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Save WAV sound effect to CDC ... ");
if(!saveAudioTrackToCDC(mAudioMgr, mLog, idSFXFile, RESOURCE_AUDIO_SFX_CDC_NAME))
{
closeTemporaryAudioTracks(mAudioMgr);
return false;
}
mLog.msg(LL_INFO, " Load FLAC sound effect: %s ... ", RESOURCE_AUDIO_FLAC_FILE);
Sint32 idFLACFile = mAudioMgr.loadFromFile(RESOURCE_AUDIO_FLAC_FILE, RESOURCE_AUDIO_FLAC_CDC_NAME, ATT_SFX, ALM_PRELOAD);
if(idFLACFile < 0)
{
logTaskFailed(mLog, idFLACFile);
closeTemporaryAudioTracks(mAudioMgr);
return false;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Save FLAC sound effect to CDC ... ");
if(!saveAudioTrackToCDC(mAudioMgr, mLog, idFLACFile, RESOURCE_AUDIO_FLAC_CDC_NAME))
{
closeTemporaryAudioTracks(mAudioMgr);
return false;
}
mLog.msg(LL_INFO, " Load OGG voice-style sound: %s ... ", RESOURCE_AUDIO_VOICE_FILE);
Sint32 idVoiceFile = mAudioMgr.loadFromFile(RESOURCE_AUDIO_VOICE_FILE, RESOURCE_AUDIO_VOICE_CDC_NAME, ATT_VOICE, ALM_PRELOAD);
if(idVoiceFile < 0)
{
logTaskFailed(mLog, idVoiceFile);
closeTemporaryAudioTracks(mAudioMgr);
return false;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Save OGG voice-style sound to CDC ... ");
if(!saveAudioTrackToCDC(mAudioMgr, mLog, idVoiceFile, RESOURCE_AUDIO_VOICE_CDC_NAME))
{
closeTemporaryAudioTracks(mAudioMgr);
return false;
}
mLog.msg(LL_INFO, " Close temporary audio tracks ... ");
closeTemporaryAudioTracks(mAudioMgr);
logTaskOk(mLog);
return true;
}
// Load an audio track from the tutorial archive.
static bool loadAudioTrackFromCDC(AudioTrackMgr& mAudioMgr, Log& mLog, Sint32& rIDTrack, const char* szResourceName)
{
rIDTrack = mAudioMgr.load(OUTPUT_CDC, szResourceName);
if(rIDTrack < 0 || mAudioMgr.get(rIDTrack) == nullptr)
{
logTaskFailed(mLog, rIDTrack);
return false;
}
logTaskOk(mLog);
return true;
}
// Play a track and record the requested action.
static void playTrack(AudioTrackMgr& mAudioMgr, Log& mLog, TutorialState& rState, Sint32 idTrack, const char* szAction)
{
AudioTrack* pTrack = mAudioMgr.get(idTrack);
if(!pTrack)
{
mLog.msg(LL_ERROR, " Audio track not loaded\n");
setLastAction(rState, "Audio track not loaded");
return;
}
if(pTrack->play() == 0)
{
setLastAction(rState, szAction);
mLog.msg(LL_INFO, " %s\n", szAction);
}
else
{
setLastAction(rState, "Playback failed");
mLog.msg(LL_ERROR, " Playback failed\n");
}
}
// Run the tutorial application and release its resources.
int main(int argc, char* argv[])
{
if(!runParameterReadRender(argc, argv, eRenderer)) return 1;
// Start CRM64Pro memory tracking before creating engine resources.
// atexit() releases memory tracking data when the program exits.
atexit(CMem::destroy);
// Main gives access to the CRM64Pro managers.
Main& mC64 = Main::instance();
Log& mLog = *mC64.logMgr().get();
AudioTrackMgr& mAudioMgr = mC64.audioTrackMgr();
TutorialState state;
// Write log output to stdout and to a file under validation/bin.
mLog.init("Tutorial_09_Audio", LL_DEBUG, LM_FILE | LM_STDOUT, OUTPUTDIR"Tutorial_09_Audio.log");
mLog.msg(LL_INFO, "\nSETUP: screen, timing and audio\n");
// Initialize the timer before calling Main::update().
// setRate() receives render frames per second, then logic frames per second.
mLog.msg(LL_INFO, " Initialize timer ... ");
if(!mC64.timer().init())
{
logTaskFailed(mLog);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Set frame rates (render=%d, logic=%d) ... ", iRenderRate, iLogicRate);
if(!mC64.timer().setRate(iRenderRate, iLogicRate))
{
logTaskFailed(mLog);
return -1;
}
logTaskOk(mLog);
// Add a 1 ms idle delay inside Main::update().
// Configure and show the default screen.
Screen* pScreen = mC64.configMgr().get();
pScreen->setSize(iScreenW, iScreenH);
pScreen->setDriver(eRenderer);
pScreen->setTitle("CRM64Pro - Tutorial 09: Audio");
mLog.msg(LL_INFO, " Create screen (%dx%d) ... ", iScreenW, iScreenH);
if(pScreen->show() < 0)
{
mLog.msg(LL_CRITICAL, "FAILED\n");
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Initialize audio device ... ");
if(!mC64.configMgr().audioInit())
{
logTaskFailed(mLog);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Set render callback ... ");
if(!pScreen->setRenderCallback([&state](Sint32 iMode) { return renderWrapper(iMode, &state); }))
{
logTaskFailed(mLog);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, "\nRESOURCES: add audio and load from CDC\n");
if(!addAudioToCDC(mC64, mLog))
{
return -1;
}
mLog.msg(LL_INFO, " Load background image from CDC ... ");
state.idBgImage = mC64.imageMgr().load(OUTPUT_CDC, RESOURCE_BG_CDC_NAME);
if(state.idBgImage < 0)
{
logTaskFailed(mLog, state.idBgImage);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Load music from CDC ... ");
if(!loadAudioTrackFromCDC(mAudioMgr, mLog, state.idMusic, RESOURCE_AUDIO_MUSIC_CDC_NAME))
{
return -1;
}
mLog.msg(LL_INFO, " Load WAV sound effect from CDC ... ");
if(!loadAudioTrackFromCDC(mAudioMgr, mLog, state.idSFX, RESOURCE_AUDIO_SFX_CDC_NAME))
{
return -1;
}
mLog.msg(LL_INFO, " Load FLAC sound effect from CDC ... ");
if(!loadAudioTrackFromCDC(mAudioMgr, mLog, state.idFLAC, RESOURCE_AUDIO_FLAC_CDC_NAME))
{
return -1;
}
mLog.msg(LL_INFO, " Load OGG voice-style sound from CDC ... ");
if(!loadAudioTrackFromCDC(mAudioMgr, mLog, state.idVoice, RESOURCE_AUDIO_VOICE_CDC_NAME))
{
return -1;
}
mLog.msg(LL_INFO, " Load cursor from CDC ... ");
state.idCursor = mC64.cursorMgr().load(OUTPUT_CDC, RESOURCE_CURSOR_CDC_NAME);
Cursor* pCursor = mC64.cursorMgr().get(state.idCursor);
if(state.idCursor < 0 || pCursor == nullptr)
{
logTaskFailed(mLog, state.idCursor);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Select cursor ... ");
if(mC64.cursorMgr().select(state.idCursor) < 0 || !mC64.cursorMgr().show())
{
logTaskFailed(mLog);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, " Load built-in font ... ");
state.idBuiltInFont = mC64.fontMgr().getBuiltin("CourierNew10White");
Font* pBuiltInFont = mC64.fontMgr().get(state.idBuiltInFont);
if(state.idBuiltInFont < 0 || pBuiltInFont == nullptr)
{
logTaskFailed(mLog, state.idBuiltInFont);
return -1;
}
logTaskOk(mLog);
mLog.msg(LL_INFO, "\nTOOLS: debug window and console\n");
// The DebugWindow is available, but this tutorial starts with it hidden.
mLog.msg(LL_INFO, " Create debug window ... ");
state.idDebugWindow = mC64.guiMgr().create("Debug", CRM64PRO_GUI_DEBUGWINDOW);
DebugWindow* pDebug = mC64.guiMgr().getDebugWindow(state.idDebugWindow);
if(!pDebug)
{
logTaskFailed(mLog, state.idDebugWindow);
return -1;
}
pDebug->addWatch("Mouse X", nullptr, &state.fMouseX);
pDebug->addWatch("Mouse Y", nullptr, &state.fMouseY);
pDebug->addWatch("Master gain", nullptr, &state.fMasterGain);
pDebug->addWatch("Render fps", &state.iCurrentRenderRate);
pDebug->addWatch("Logic fps", &state.iCurrentLogicRate);
pDebug->addWatch("Logic frames", &state.iLogicFrames);
pDebug->baseWidget().hide();
logTaskOk(mLog);
// Main creates the default Console. Log output is also sent there.
Console* pConsole = mC64.guiMgr().getConsole();
if(pConsole) pConsole->print("Tutorial 09: Audio ready. Press ` to toggle the console.\n");
mLog.msg(LL_INFO, "\nCONTROLS\n");
mLog.msg(LL_INFO, " [Q] / [ESC] Quit\n");
mLog.msg(LL_INFO, " [1] Fade in looping music\n");
mLog.msg(LL_INFO, " [2] Play WAV sound effect\n");
mLog.msg(LL_INFO, " [3] Play FLAC sound effect\n");
mLog.msg(LL_INFO, " [4] Play OGG voice-style sound\n");
mLog.msg(LL_INFO, " [P] / [R] Pause or resume all tracks\n");
mLog.msg(LL_INFO, " [S] Stop all tracks\n");
mLog.msg(LL_INFO, " [F] Fade out music\n");
mLog.msg(LL_INFO, " [+] / [-] Change master gain\n");
mLog.msg(LL_INFO, " [D] Toggle debug window\n");
mLog.msg(LL_INFO, " [`] Toggle console\n");
mLog.msg(LL_INFO, "\nRUNNING\n");
SDL_Event event;
bool bRunning = true;
while(bRunning)
{
// Main::update() processes input, runs internal GUI logic and calls the
// screen render callback when a render frame is ready.
while(mC64.update(&event))
{
if(event.type == SDL_EVENT_QUIT)
{
bRunning = false;
}
else if(event.type == SDL_EVENT_KEY_DOWN)
{
if(event.key.key == SDLK_Q || event.key.key == SDLK_ESCAPE)
{
bRunning = false;
}
else if(event.key.key == SDLK_1)
{
AudioTrack* pMusic = mAudioMgr.get(state.idMusic);
if(pMusic && pMusic->fadeIn(700, -1) == 0)
{
setLastAction(state, "Looping music fading in");
mLog.msg(LL_INFO, " Looping music fading in\n");
}
else
{
setLastAction(state, "Music playback failed");
mLog.msg(LL_ERROR, " Music playback failed\n");
}
}
else if(event.key.key == SDLK_2)
{
playTrack(mAudioMgr, mLog, state, state.idSFX, "WAV sound effect played");
}
else if(event.key.key == SDLK_3)
{
playTrack(mAudioMgr, mLog, state, state.idFLAC, "FLAC sound effect played");
}
else if(event.key.key == SDLK_4)
{
playTrack(mAudioMgr, mLog, state, state.idVoice, "OGG voice-style sound played");
}
else if(event.key.key == SDLK_P)
{
mAudioMgr.pauseAll();
setLastAction(state, "All tracks paused");
mLog.msg(LL_INFO, " All tracks paused\n");
}
else if(event.key.key == SDLK_R)
{
mAudioMgr.resumeAll();
setLastAction(state, "All tracks resumed");
mLog.msg(LL_INFO, " All tracks resumed\n");
}
else if(event.key.key == SDLK_S)
{
mAudioMgr.stopAll();
setLastAction(state, "All tracks stopped");
mLog.msg(LL_INFO, " All tracks stopped\n");
}
else if(event.key.key == SDLK_F)
{
if(mAudioMgr.fadeOutTag(ATT_MUSIC, 1200))
{
setLastAction(state, "Music fading out");
mLog.msg(LL_INFO, " Music fading out\n");
}
else
{
setLastAction(state, "Music fade-out failed");
mLog.msg(LL_ERROR, " Music fade-out failed\n");
}
}
else if(event.key.key == SDLK_PLUS || event.key.key == SDLK_KP_PLUS)
{
state.fMasterGain += 0.1f;
if(state.fMasterGain > 2.0f) state.fMasterGain = 2.0f;
mC64.configMgr().audioSetMasterGain(state.fMasterGain);
setLastAction(state, "Master gain increased");
mLog.msg(LL_INFO, " Master gain: %.1f\n", state.fMasterGain);
}
else if(event.key.key == SDLK_MINUS || event.key.key == SDLK_KP_MINUS)
{
state.fMasterGain -= 0.1f;
if(state.fMasterGain < 0.0f) state.fMasterGain = 0.0f;
mC64.configMgr().audioSetMasterGain(state.fMasterGain);
setLastAction(state, "Master gain decreased");
mLog.msg(LL_INFO, " Master gain: %.1f\n", state.fMasterGain);
}
else if(event.key.key == SDLK_D)
{
toggleDebugWindow(pDebug);
}
}
}
if(!bRunning) break;
state.fMouseX = mC64.cursorMgr().getX();
state.fMouseY = mC64.cursorMgr().getY();
state.iCurrentRenderRate = static_cast<Sint32>(mC64.timer().getCurrentRFR());
state.iCurrentLogicRate = static_cast<Sint32>(mC64.timer().getCurrentLFR());
updateLogic(state);
}
// Close resources owned by this tutorial.
mAudioMgr.info();
mLog.msg(LL_INFO, "\nCLEANUP\n");
mLog.msg(LL_INFO, " Stop and close audio tracks ... ");
mAudioMgr.stopAll();
mAudioMgr.close(0);
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close audio device ... ");
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close debug window ... ");
mC64.guiMgr().close(state.idDebugWindow);
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close fonts ... ");
mC64.fontMgr().close(0);
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close cursors ... ");
mC64.cursorMgr().close(0);
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close images ... ");
mC64.imageMgr().close(0);
logTaskOk(mLog);
mLog.msg(LL_INFO, " Close CDC archives ... ");
mC64.archiveMgr().close(0);
logTaskOk(mLog);
return 0;
}
Sint32 close(Sint32 idCDC) override
Close and destroy a CDC object.
Definition CoreArchive.cpp:2476
Audio Track object class.
Definition CRM64Pro.h:2953
Audio Track Manager class.
Definition CRM64Pro.h:3154
bool audioInit(eConfigAudioFreq eAF=CAF_DEFAULT, eConfigAudioSample eAS=CAS_DEFAULT, eConfigAudioMode eAM=CAM_DEFAULT)
Initialize the audio system.
Definition CoreConfig.cpp:1195
bool audioClose()
Close the audio system.
Definition CoreConfig.cpp:1283
Screen * get(Sint32 idScreen=0)
Get a pointer to the screen using its handle.
Definition CoreConfig.cpp:1497
void setMTFriendly(Uint8 iDelayMS)
Set the multithread-friendly scheduler yield.
Definition CoreConfig.cpp:1183
bool audioSetMasterGain(float fGain)
Set Master gain (Volume).
Definition CoreConfig.cpp:1331
Console Object class.
Definition CRM64Pro.h:9678
Cursor Object class.
Definition CRM64Pro.h:3734
float getY() const
Get the mouse cursor Y position.
Definition GraphicsCursor.cpp:1041
Sint32 select(Sint32 idCursor)
Select a mouse cursor.
Definition GraphicsCursor.cpp:968
bool show()
Make visible the selected mouse cursor.
Definition GraphicsCursor.cpp:1001
Sint32 load(const string &sCDCFile, const string &sName)
Load a cursor stored in a CDC file.
Definition GraphicsCursor.cpp:1115
Sint32 close(Sint32 idCursor) override
Close and destroy a cursor.
Definition GraphicsCursor.cpp:897
Cursor * get(Sint32 idCursor)
Get a pointer to the cursor using its handle.
Definition GraphicsCursor.cpp:962
float getX() const
Get the mouse cursor X position.
Definition GraphicsCursor.cpp:1035
DebugWindow Object class.
Definition CRM64Pro.h:9810
Font Object class.
Definition CRM64Pro.h:4741
Sint32 render(const string &sText, bool bShowCursor=false, Sint32 iCurPos=-1, Sint32 idRes=0)
Render a text using this font.
Definition GraphicsFont.cpp:1095
bool setPosition(const Position &rPosX, const Position &rPosY, bool bInterpolate=false)
Set the font position.
Definition GraphicsFont.cpp:1037
Sint32 getBuiltin(const string &sName)
Get a built-in embedded font id using its name.
Definition GraphicsFont.cpp:1436
Sint32 close(Sint32 idFont) override
Close and destroy a font.
Definition GraphicsFont.cpp:1363
Font * get(const string &sName)
Get a pointer to the font using its name.
Definition GraphicsFont.cpp:1235
Image Object class.
Definition CRM64Pro.h:3356
Sint32 render(Sint32 idRes=0, SDL_FRect *pSrc=nullptr, SDL_FRect *pDst=nullptr)
Render the image.
Definition GraphicsImage.cpp:1575
Image * get(Sint32 idImg)
Get a pointer to the image using its handle.
Definition GraphicsImage.cpp:2279
Sint32 load(const string &sCDCFile, const string &sName)
Load an image stored in a CDC file.
Definition GraphicsImage.cpp:2476
Sint32 close(Sint32 idImg) override
Close and destroy an image.
Definition GraphicsImage.cpp:2178
Log Object class.
Definition CRM64Pro.h:771
CRM64Pro GDK Main class.
Definition CRM64Pro.h:502
Screen Object class.
Definition CRM64Pro.h:1055
Sint32 show()
Show the screen.
Definition CoreConfig.cpp:362
Sint32 setDriver(eConfigRendererDriver eCRD)
Set the renderer driver.
Definition CoreConfig.cpp:454
bool setRenderCallback(RenderCallback fnCallback)
Set a callback function for rendering graphics at Render Frame Rate.
Definition CoreConfig.cpp:765
Sint32 setSize(Sint32 iWidth, Sint32 iHeight)
Set the size of the screen.
Definition CoreConfig.cpp:526
bool setTitle(const string &sName)
Set the title.
Definition CoreConfig.cpp:674
Sint32 save(const string &sCDCFile, const string &sResourceName="")
Save the AudioTrack into a CDC file.
Definition AudioTrack.cpp:973
bool pauseAll()
Pause all active audio tracks.
Definition AudioTrack.cpp:1338
bool setKeepSourceData(bool bKeep)
Set the global policy for keeping raw source data in memory.
Definition AudioTrack.cpp:1434
AudioTrack * get(Sint32 idAudio)
Get a pointer to the AudioTrack using its handle.
Definition AudioTrack.cpp:1300
bool fadeOutTag(eAudioTrackTag eTag, Sint32 iTimeMS=1000)
Fade out all tracks with the given audio track tag.
Definition AudioTrack.cpp:1374
bool info(Sint32 iMode=0) override
Request AudioTrack Manager information.
Definition AudioTrack.cpp:1189
Sint32 fadeIn(Sint32 iTimeMS=1000, Sint32 iRepeats=0)
Play the track with a fade-in effect.
Definition AudioTrack.cpp:477
Sint32 close(Sint32 idAudio=0) override
Close and destroy an AudioTrack.
Definition AudioTrack.cpp:1255
bool stopAll()
Immediately stop all active audio tracks.
Definition AudioTrack.cpp:1307
Sint32 load(const string &sCDCFile, const string &sName, bool bForceNew=false)
Load an AudioTrack from a CDC file (by file name).
Definition AudioTrack.cpp:1645
Sint32 play(Sint32 iRepeats=0)
Play the track.
Definition AudioTrack.cpp:471
Sint32 loadFromFile(const string &sFile, const string &sName="", eAudioTrackTag eTag=ATT_SFX, eAudioLoadMode eMode=ALM_AUTO, bool bForceNew=false)
Load an AudioTrack from an external file.
Definition AudioTrack.cpp:1447
bool resumeAll()
Resume all paused audio tracks.
Definition AudioTrack.cpp:1346
@ ATT_MUSIC
Background music track.
Definition CRM64Pro.h:1043
@ ATT_VOICE
Voice/dialogue track.
Definition CRM64Pro.h:1045
@ ATT_SFX
Sound effect track.
Definition CRM64Pro.h:1044
@ ALM_PRELOAD
Preload entire audio into memory (best for SFX).
Definition CRM64Pro.h:2947
void setStatsLevel(eMemStatsLevel eMSL)
Set the memory stats gathering level.
Definition CMemoryManager.cpp:618
void destroy()
Releases the internal heap and displays final stats.
Definition CMemoryManager.cpp:954
@ MSL_NORMAL
Collect allocations per module (CRM64Pro and user). Default for debug builds.
Definition CRM64Pro.h:386
eConfigRendererDriver
Screen renderer drivers.
Definition CRM64Pro.h:1013
@ CRD_SOFTWARE
Screen::setDriver(): software renderer driver. Slow but works on all platforms.
Definition CRM64Pro.h:1015
Sint32 create(const string &sName, Uint32 iType=CRM64PRO_GUI_PANEL) override
Creates a new GUI object.
Definition GUI.cpp:1051
Sint32 addWatch(const string &sName, Sint32 *iIntegerVar, float *fFloatVar=nullptr)
Add a watch.
Definition GUIDebugWindow.cpp:275
#define CRM64PRO_GUI_DEBUGWINDOW
Definition CRM64Pro.h:9195
Widget & baseWidget()
Get the reference to base Widget for this object.
Definition GUIDebugWindow.cpp:269
Sint32 print(const char *format,...)
Print a formatted message to the console.
Definition GUIConsole.cpp:403
Console * getConsole(Sint32 idGUI=CRM64PRO_GUI_CONSOLE)
Get a pointer to a Console using its handle.
Definition GUI.cpp:1366
DebugWindow * getDebugWindow(Sint32 idGUI)
Get a pointer to a DebugWindow using its handle.
Definition GUI.cpp:1375
Sint32 close(Sint32 idGUI) override
Close and destroy a GUI object.
Definition GUI.cpp:1168
eGeneralStatus status() const
Get the widget status.
Definition GUIWidget.cpp:1772
bool hide()
Hide a widget.
Definition GUIWidget.cpp:1729
bool show()
Show a widget.
Definition GUIWidget.cpp:1671
Sint32 setPosition(const Position &rPosX=Position(PH_CENTER), const Position &rPosY=Position(PH_CENTER))
Set the position.
Definition GUIWidget.cpp:1923
Log * get(Sint32 idLog=0)
Get a pointer to the log object.
Definition CoreLog.cpp:921
@ LM_FILE
Log output mode for the 'lm' parameter in CRM64Pro::Log::init(). Output to a file and overwrite exist...
Definition CRM64Pro.h:748
@ LM_STDOUT
Log output mode for the 'lm' parameter in CRM64Pro::Log::init(). Output to standard output (stdout).
Definition CRM64Pro.h:750
@ LL_INFO
Runtime events (startup/shutdown). Messages without timestamp.
Definition CRM64Pro.h:764
@ LL_DEBUG
Detailed information for flow control.
Definition CRM64Pro.h:765
@ LL_CRITICAL
Fatal errors, application crash, or assertions.
Definition CRM64Pro.h:761
@ LL_ERROR
Runtime errors that do not require immediate termination.
Definition CRM64Pro.h:762
GUIMgr & guiMgr()
Get reference to GUI Manager.
Definition CoreMain.cpp:808
Timer & timer()
Get reference to Timer module.
Definition CoreMain.cpp:730
Sint32 update(SDL_Event *evUser=nullptr)
Manage the GDK update cycle with a fixed virtual logic frame rate.
Definition CoreMain.cpp:451
ImageMgr & imageMgr()
Get reference to Image Manager.
Definition CoreMain.cpp:754
CursorMgr & cursorMgr()
Get reference to Cursor Manager.
Definition CoreMain.cpp:760
static void terminate()
Main explicit destructor.
Definition CoreMain.cpp:266
static Main & instance()
Access the Main singleton.
Definition CoreMain.cpp:252
LogMgr & logMgr()
Get reference to Log Manager.
Definition CoreMain.cpp:724
AudioTrackMgr & audioTrackMgr()
Get reference to Audio Track Manager.
Definition CoreMain.cpp:748
Tool & tool()
Get reference to Tool module.
Definition CoreMain.cpp:718
ArchiveMgr & archiveMgr()
Get reference to Archive Manager.
Definition CoreMain.cpp:736
FontMgr & fontMgr()
Get reference to Font Manager.
Definition CoreMain.cpp:778
ConfigMgr & configMgr()
Get reference to Config Manager.
Definition CoreMain.cpp:712
@ GS_SHOWN
General status: enabled and shown.
Definition CRM64Pro.h:300
@ PH_RIGHT
Widget/Font: right with optional offset. Scene: align to map right/end (MapWidth - ViewportWidth)....
Definition CRM64Pro.h:340
@ PH_TOP
Widget/Font: top with optional offset. Scene: align to map top/begin (0.0). Valid for Y axis.
Definition CRM64Pro.h:337
bool init(eTimerState eState=TS_INIT)
Initialize the timer system.
Definition CoreTimer.cpp:543
bool setRate(Uint16 iR, Uint16 iL)
Set preferred Render and Logic Frame Rate.
Definition CoreTimer.cpp:556
bool fileExists(const string &sPath)
Check if a file exists.
Definition CoreTool.cpp:750
CRM64Pro GDK.
Definition AudioTrack.cpp:36
Absolute coordinate or helper-relative position used by layout APIs.
Definition CRM64Pro.h:346

Navigation

Previous: Tutorial 08 - Animated sprites

Back to the tutorials index

Continue with Tutorial 10: Particle effects