fuck git and fuck vs code
This commit is contained in:
@@ -1,12 +1,26 @@
|
||||
#include "audio.h"
|
||||
|
||||
#include <SDL3/SDL_properties.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <xm.h>
|
||||
|
||||
namespace anm2ed::resource
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr int XM_SAMPLE_RATE = 44100;
|
||||
constexpr uint16_t XM_CHUNK_FRAMES = 1024;
|
||||
constexpr int XM_MAX_SECONDS = 600;
|
||||
constexpr int XM_CHANNELS = 2;
|
||||
}
|
||||
|
||||
MIX_Mixer* Audio::mixer_get()
|
||||
{
|
||||
static MIX_Mixer* mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, nullptr);
|
||||
static auto mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, nullptr);
|
||||
return mixer;
|
||||
}
|
||||
|
||||
@@ -15,21 +29,130 @@ namespace anm2ed::resource
|
||||
if (path && *path) internal = MIX_LoadAudio(mixer_get(), path, true);
|
||||
}
|
||||
|
||||
void Audio::unload()
|
||||
Audio::Audio(const unsigned char* data, size_t size)
|
||||
{
|
||||
if (!internal) return;
|
||||
MIX_DestroyAudio(internal);
|
||||
internal = nullptr;
|
||||
if (!data || size == 0) return;
|
||||
|
||||
auto is_chunk_silent = [](const float* samples, size_t count)
|
||||
{
|
||||
constexpr float epsilon = 1e-6f;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
if (std::fabs(samples[i]) > epsilon) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
auto prescanStorage = std::make_unique<unsigned char[]>(XM_PRESCAN_DATA_SIZE);
|
||||
auto prescan = (xm_prescan_data_t*)prescanStorage.get();
|
||||
if (!xm_prescan_module((const char*)data, (uint32_t)size, prescan)) return;
|
||||
|
||||
auto contextSize = xm_size_for_context(prescan);
|
||||
auto pool = std::make_unique<char[]>(contextSize);
|
||||
auto context = xm_create_context(pool.get(), prescan, (const char*)data, (uint32_t)size);
|
||||
if (!context) return;
|
||||
|
||||
xm_set_sample_rate(context, (uint16_t)XM_SAMPLE_RATE);
|
||||
xm_set_max_loop_count(context, 1);
|
||||
|
||||
auto pcm = std::vector<float>{};
|
||||
pcm.reserve(XM_CHUNK_FRAMES * XM_CHANNELS * 8);
|
||||
|
||||
auto framesGenerated = (size_t)0;
|
||||
const auto maxFrames = (size_t)XM_SAMPLE_RATE * XM_MAX_SECONDS;
|
||||
auto heardAudio = false;
|
||||
|
||||
while (framesGenerated < maxFrames)
|
||||
{
|
||||
auto framesThisPass = (uint16_t)std::min<size_t>(XM_CHUNK_FRAMES, maxFrames - framesGenerated);
|
||||
auto offset = pcm.size();
|
||||
pcm.resize(offset + framesThisPass * XM_CHANNELS);
|
||||
auto* chunkStart = pcm.data() + offset;
|
||||
xm_generate_samples(context, chunkStart, framesThisPass);
|
||||
framesGenerated += framesThisPass;
|
||||
|
||||
auto chunkSamples = (size_t)framesThisPass * XM_CHANNELS;
|
||||
auto chunkSilent = is_chunk_silent(chunkStart, chunkSamples);
|
||||
if (!chunkSilent)
|
||||
{
|
||||
heardAudio = true;
|
||||
}
|
||||
else if (heardAudio)
|
||||
{
|
||||
pcm.resize(offset);
|
||||
break;
|
||||
}
|
||||
|
||||
if (xm_get_loop_count(context) > 0) break;
|
||||
}
|
||||
|
||||
if (pcm.empty()) return;
|
||||
|
||||
auto spec = SDL_AudioSpec{};
|
||||
spec.freq = XM_SAMPLE_RATE;
|
||||
spec.format = SDL_AUDIO_F32;
|
||||
spec.channels = XM_CHANNELS;
|
||||
|
||||
internal = MIX_LoadRawAudio(nullptr, pcm.data(), pcm.size() * sizeof(float), &spec);
|
||||
}
|
||||
|
||||
void Audio::play(MIX_Mixer* mixer)
|
||||
void Audio::unload()
|
||||
{
|
||||
MIX_PlayAudio(mixer ? mixer : mixer_get(), internal);
|
||||
if (track)
|
||||
{
|
||||
MIX_DestroyTrack(track);
|
||||
track = nullptr;
|
||||
}
|
||||
if (internal)
|
||||
{
|
||||
MIX_DestroyAudio(internal);
|
||||
internal = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::play(bool loop, MIX_Mixer* mixer)
|
||||
{
|
||||
if (!internal) return;
|
||||
auto targetMixer = mixer ? mixer : mixer_get();
|
||||
if (!targetMixer) return;
|
||||
|
||||
if (track && MIX_GetTrackMixer(track) != targetMixer)
|
||||
{
|
||||
MIX_DestroyTrack(track);
|
||||
track = nullptr;
|
||||
}
|
||||
|
||||
if (!track)
|
||||
{
|
||||
track = MIX_CreateTrack(targetMixer);
|
||||
if (!track) return;
|
||||
}
|
||||
|
||||
MIX_SetTrackAudio(track, internal);
|
||||
|
||||
SDL_PropertiesID options = 0;
|
||||
if (loop)
|
||||
{
|
||||
options = SDL_CreateProperties();
|
||||
if (options) SDL_SetNumberProperty(options, MIX_PROP_PLAY_LOOPS_NUMBER, -1);
|
||||
}
|
||||
|
||||
MIX_PlayTrack(track, options);
|
||||
|
||||
if (options) SDL_DestroyProperties(options);
|
||||
}
|
||||
|
||||
void Audio::stop(MIX_Mixer* mixer)
|
||||
{
|
||||
if (!track) return;
|
||||
if (mixer && MIX_GetTrackMixer(track) != mixer) return;
|
||||
MIX_StopTrack(track, 0);
|
||||
}
|
||||
|
||||
bool Audio::is_playing() const { return track && MIX_TrackPlaying(track); }
|
||||
|
||||
Audio::Audio(Audio&& other) noexcept
|
||||
{
|
||||
internal = std::exchange(other.internal, nullptr);
|
||||
track = std::exchange(other.track, nullptr);
|
||||
}
|
||||
|
||||
Audio& Audio::operator=(Audio&& other) noexcept
|
||||
@@ -38,17 +161,11 @@ namespace anm2ed::resource
|
||||
{
|
||||
unload();
|
||||
internal = std::exchange(other.internal, nullptr);
|
||||
track = std::exchange(other.track, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Audio::~Audio()
|
||||
{
|
||||
unload();
|
||||
}
|
||||
|
||||
bool Audio::is_valid()
|
||||
{
|
||||
return internal;
|
||||
}
|
||||
Audio::~Audio() { unload(); }
|
||||
bool Audio::is_valid() { return internal; }
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3_mixer/SDL_mixer.h>
|
||||
#include <cstddef>
|
||||
|
||||
namespace anm2ed::resource
|
||||
{
|
||||
class Audio
|
||||
{
|
||||
MIX_Audio* internal{nullptr};
|
||||
MIX_Track* track{nullptr};
|
||||
MIX_Mixer* mixer_get();
|
||||
void unload();
|
||||
|
||||
public:
|
||||
Audio(const char*);
|
||||
Audio(const unsigned char*, size_t);
|
||||
~Audio();
|
||||
Audio() = default;
|
||||
Audio(Audio&&) noexcept;
|
||||
@@ -20,6 +23,8 @@ namespace anm2ed::resource
|
||||
Audio& operator=(const Audio&) = delete;
|
||||
|
||||
bool is_valid();
|
||||
void play(MIX_Mixer* = nullptr);
|
||||
void play(bool loop = false, MIX_Mixer* = nullptr);
|
||||
void stop(MIX_Mixer* = nullptr);
|
||||
bool is_playing() const;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -212,18 +212,4 @@ namespace anm2ed::resource::icon
|
||||
SVG_LIST
|
||||
#undef X
|
||||
};
|
||||
|
||||
constexpr uint8_t PROGRAM[] = {
|
||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00,
|
||||
0x40, 0x00, 0x00, 0x00, 0x40, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x60, 0xb9, 0x55, 0x00, 0x00, 0x00, 0x09, 0x70,
|
||||
0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00,
|
||||
0x8b, 0x49, 0x44, 0x41, 0x54, 0x68, 0xde, 0xed, 0xd7, 0x3d, 0x0e, 0x80, 0x20, 0x0c, 0x86, 0x61, 0x7b, 0x53, 0x3c,
|
||||
0x99, 0xbd, 0x69, 0x1d, 0x8c, 0x83, 0x4d, 0xd0, 0x44, 0x29, 0x7f, 0xbe, 0x5d, 0x08, 0x30, 0xf4, 0x19, 0x80, 0x2f,
|
||||
0xc8, 0xd2, 0xb8, 0x04, 0xc0, 0xb4, 0x80, 0x64, 0xc7, 0xb8, 0xf9, 0x86, 0x02, 0x60, 0x36, 0x80, 0xd9, 0x75, 0xbe,
|
||||
0xba, 0x7d, 0x00, 0xf3, 0x00, 0x72, 0x8d, 0x7c, 0x83, 0x73, 0x5d, 0xa5, 0xf0, 0x43, 0x04, 0xa0, 0x1a, 0x20, 0xaa,
|
||||
0x11, 0x80, 0xf1, 0x01, 0x1a, 0x14, 0x5b, 0x00, 0xfa, 0x03, 0x24, 0xbb, 0x0f, 0x93, 0xa7, 0xb0, 0xf9, 0x1c, 0x46,
|
||||
0x00, 0x00, 0x00, 0xe8, 0x06, 0xa0, 0xa5, 0x3f, 0x2a, 0x99, 0x07, 0x0d, 0x40, 0xbf, 0x80, 0xa8, 0x02, 0x30, 0x0e,
|
||||
0x40, 0xdf, 0x1e, 0xb2, 0x56, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x07, 0x90, 0x86, 0xff, 0x05, 0xd4,
|
||||
0x2e, 0x00, 0x00, 0x00, 0x34, 0x07, 0xec, 0x94, 0x51, 0xac, 0x41, 0x55, 0x6e, 0xe4, 0x26, 0x00, 0x00, 0x00, 0x00,
|
||||
0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82};
|
||||
}
|
||||
Reference in New Issue
Block a user