Files
Horizon-OC/Source/hoc-clk/overlay/lib/libultrahand/libultra/include/audio.hpp
2026-04-01 16:00:54 -04:00

126 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/********************************************************************************
* File: audio.hpp
* Author: ppkantorski
* Description:
* This header defines the Audio class and related structures used for
* handling sound playback within the Ultrahand Overlay. It provides interfaces
* for loading, caching, and playing WAV audio through libnx's audout service,
* along with basic sound type management and synchronization support.
*
* For the latest updates and contributions, visit the project's GitHub repository.
* (GitHub Repository: https://github.com/ppkantorski/Ultrahand-Overlay)
*
* Note: Please be aware that this notice cannot be altered or removed. It is a part
* of the project's documentation and must remain intact.
*
* Licensed under both GPLv2 and CC-BY-4.0
* Copyright (c) 2025-2026 ppkantorski
********************************************************************************/
#pragma once
#include <switch.h>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <atomic>
#include <cstring>
#include <mutex>
#include "tsl_utils.hpp"
namespace ult {
class Audio {
public:
enum class SoundType : uint8_t {
Navigate,
Enter,
Exit,
Wall,
On,
Off,
Settings,
Move,
Notification,
Count
};
struct CachedSound {
// Compact raw PCM — native channel count, 16-bit, no volume applied.
// This is the only per-sound allocation. No pre-baked stereo copy is kept.
void* rawBuf = nullptr;
uint32_t rawSize = 0; // actual data bytes
uint32_t rawCap = 0; // allocated (aligned) bytes
uint32_t sampleRate = 48000; // native rate read from WAV header
bool isMono = false;
};
static bool initialize();
static void exit();
static inline bool allSoundsExist() {
for (const auto& path : m_soundPaths)
if (!isFile(path)) return false;
return true;
}
static void playSound(SoundType type);
static inline void playNavigateSound() { playSound(SoundType::Navigate); }
static inline void playEnterSound() { playSound(SoundType::Enter); }
static inline void playExitSound() { playSound(SoundType::Exit); }
static inline void playWallSound() { playSound(SoundType::Wall); }
static inline void playOnSound() { playSound(SoundType::On); }
static inline void playOffSound() { playSound(SoundType::Off); }
static inline void playSettingsSound() { playSound(SoundType::Settings); }
static inline void playMoveSound() { playSound(SoundType::Move); }
static inline void playNotificationSound() { playSound(SoundType::Notification); }
static void setMasterVolume(float volume);
static void setEnabled(bool enabled);
static bool isEnabled();
static bool reloadIfDockedChanged();
static void reloadAllSounds();
static void unloadAllSounds(const std::initializer_list<SoundType>& excludeSounds = {});
static std::mutex m_audioMutex;
static bool m_initialized;
private:
static std::atomic<bool> m_enabled;
static std::atomic<int32_t> m_masterVolumeFixed; // volume as 0256 fixed-point
static bool m_lastDockedState;
static std::vector<CachedSound> m_cachedSounds;
// Single shared DMA playback buffer — sized to the largest sound's
// 48 kHz stereo output. Reused on every playSound(); safe because audout
// is always drained before the buffer is written.
static void* m_playBuf;
static uint32_t m_playBufCap;
static AudioOutBuffer m_audoutBuf;
inline static constexpr const char* m_soundPaths[static_cast<size_t>(SoundType::Count)] = {
"sdmc:/config/ultrahand/sounds/tick.wav",
"sdmc:/config/ultrahand/sounds/enter.wav",
"sdmc:/config/ultrahand/sounds/exit.wav",
"sdmc:/config/ultrahand/sounds/wall.wav",
"sdmc:/config/ultrahand/sounds/on.wav",
"sdmc:/config/ultrahand/sounds/off.wav",
"sdmc:/config/ultrahand/sounds/settings.wav",
"sdmc:/config/ultrahand/sounds/move.wav",
"sdmc:/config/ultrahand/sounds/notification.wav"
};
// Loads WAV into rawBuf (16-bit, native channels, no volume applied).
// Must be called under m_audioMutex.
static bool loadSoundFromWav(SoundType type, const char* path);
// Ensures m_playBuf is large enough for the largest loaded sound's
// 48 kHz stereo output. Call after any load. Must hold m_audioMutex.
static void growPlayBuf();
// Renders rawBuf → m_playBuf on demand: resample to 48 kHz if needed,
// expand mono → stereo, apply current volume + dock attenuation.
// Returns actual output byte count written, or 0 on error.
// Must be called under m_audioMutex.
static uint32_t renderToPlayBuf(const CachedSound& s);
};
}