#include "audio.h" #include #include #include #include #pragma comment(lib, "xaudio2.lib") // MinGW 没有 xaudio2.lib,通过系统 DLL 动态加载 XAudio2Create typedef HRESULT (WINAPI *XAudio2CreateFunc)(IXAudio2** ppxaudio2, UINT32 flags, XAUDIO2_PROCESSOR processor); static HRESULT engine_xaudio2_create(IXAudio2** ppxaudio2, UINT32 flags, XAUDIO2_PROCESSOR processor) { static HMODULE mod = NULL; static XAudio2CreateFunc func = NULL; if (!func) { mod = LoadLibraryA("xaudio2_9.dll"); if (!mod) mod = LoadLibraryA("xaudio2_8.dll"); if (!mod) mod = LoadLibraryA("xaudio2_7.dll"); if (mod) func = (XAudio2CreateFunc)GetProcAddress(mod, "XAudio2Create"); } if (!func) return E_FAIL; return func(ppxaudio2, flags, processor); } typedef struct { WAVEFORMATEX format; uint8_t* data; uint32_t size; } WavData; static int wav_load(const uint8_t* file_data, uint32_t file_size, WavData* out) { memset(out, 0, sizeof(*out)); if (file_size < 12) return 0; if (memcmp(file_data, "RIFF", 4) != 0) return 0; if (memcmp(file_data + 8, "WAVE", 4) != 0) return 0; uint32_t offset = 12; int has_fmt = 0; int has_data = 0; while (offset + 8 <= file_size) { const uint8_t* chunk_id = file_data + offset; uint32_t chunk_size = *(const uint32_t*)(file_data + offset + 4); const uint8_t* chunk_data = file_data + offset + 8; if (offset + 8 + chunk_size > file_size) return 0; if (memcmp(chunk_id, "fmt ", 4) == 0 && chunk_size >= 16) { uint32_t copy_size = chunk_size >= sizeof(WAVEFORMATEX) ? sizeof(WAVEFORMATEX) : chunk_size; memcpy(&out->format, chunk_data, copy_size); if (out->format.wFormatTag != WAVE_FORMAT_PCM && out->format.wFormatTag != WAVE_FORMAT_IEEE_FLOAT) { return 0; } if (out->format.cbSize != 0) { out->format.cbSize = 0; } has_fmt = 1; } else if (memcmp(chunk_id, "data", 4) == 0) { out->data = (uint8_t*)chunk_data; out->size = chunk_size; has_data = 1; } offset += 8 + chunk_size; if (chunk_size & 1) offset += 1; // 对齐到偶数字节 } return has_fmt && has_data; } AudioSystem* audio_create(void) { CoInitializeEx(NULL, COINIT_MULTITHREADED); AudioSystem* audio = (AudioSystem*)calloc(1, sizeof(AudioSystem)); if (!audio) return NULL; HRESULT hr = engine_xaudio2_create(&audio->xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR); if (FAILED(hr) || !audio->xaudio2) { free(audio); return NULL; } hr = audio->xaudio2->lpVtbl->CreateMasteringVoice( audio->xaudio2, &audio->mastering_voice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, NULL, NULL, AudioCategory_GameEffects); if (FAILED(hr)) { audio->xaudio2->lpVtbl->Release(audio->xaudio2); free(audio); return NULL; } return audio; } void audio_destroy(AudioSystem* audio) { if (!audio) return; if (audio->mastering_voice) { audio->mastering_voice->lpVtbl->DestroyVoice(audio->mastering_voice); } if (audio->xaudio2) { audio->xaudio2->lpVtbl->Release(audio->xaudio2); } free(audio); CoUninitialize(); } AudioSource* audio_source_create(AudioSystem* audio) { if (!audio) return NULL; AudioSource* source = (AudioSource*)calloc(1, sizeof(AudioSource)); if (!source) return NULL; source->system = audio; source->volume = 1.0f; source->pitch = 1.0f; source->loop = 0; source->state = AUDIO_STOPPED; return source; } void audio_source_destroy(AudioSource* source) { if (!source) return; if (source->source) { source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW); source->source->lpVtbl->DestroyVoice(source->source); } free(source->audio_data); free(source); } static int audio_source_load_data(AudioSource* source, const WavData* wav) { if (!source || !source->system || !source->system->xaudio2) return 0; // 释放旧的语音与数据 if (source->source) { source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW); source->source->lpVtbl->DestroyVoice(source->source); source->source = NULL; } free(source->audio_data); source->audio_data = NULL; source->audio_size = 0; source->audio_data = (uint8_t*)malloc(wav->size); if (!source->audio_data) return 0; memcpy(source->audio_data, wav->data, wav->size); source->audio_size = wav->size; HRESULT hr = source->system->xaudio2->lpVtbl->CreateSourceVoice( source->system->xaudio2, &source->source, &wav->format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, NULL, NULL); if (FAILED(hr)) { free(source->audio_data); source->audio_data = NULL; source->audio_size = 0; return 0; } return 1; } int audio_load_wav(AudioSource* source, const char* path) { if (!source || !path) return 0; FILE* f = fopen(path, "rb"); if (!f) return 0; fseek(f, 0, SEEK_END); long file_size = ftell(f); fseek(f, 0, SEEK_SET); if (file_size <= 0) { fclose(f); return 0; } uint8_t* file_data = (uint8_t*)malloc((size_t)file_size); if (!file_data) { fclose(f); return 0; } size_t read = fread(file_data, 1, (size_t)file_size, f); fclose(f); if (read != (size_t)file_size) { free(file_data); return 0; } WavData wav; int ok = wav_load(file_data, (uint32_t)file_size, &wav); if (ok) { ok = audio_source_load_data(source, &wav); } free(file_data); return ok; } int audio_load_wav_memory(AudioSource* source, const uint8_t* data, uint32_t size) { if (!source || !data || size == 0) return 0; WavData wav; int ok = wav_load(data, size, &wav); if (ok) { ok = audio_source_load_data(source, &wav); } return ok; } void audio_source_play(AudioSource* source) { if (!source || !source->source) return; XAUDIO2_BUFFER buffer = {0}; buffer.AudioBytes = source->audio_size; buffer.pAudioData = source->audio_data; buffer.Flags = XAUDIO2_END_OF_STREAM; if (source->loop) { buffer.LoopCount = XAUDIO2_LOOP_INFINITE; } source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW); source->source->lpVtbl->FlushSourceBuffers(source->source); source->source->lpVtbl->SubmitSourceBuffer(source->source, &buffer, NULL); source->source->lpVtbl->Start(source->source, 0, XAUDIO2_COMMIT_NOW); source->state = AUDIO_PLAYING; } void audio_source_stop(AudioSource* source) { if (!source || !source->source) return; source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW); source->source->lpVtbl->FlushSourceBuffers(source->source); source->state = AUDIO_STOPPED; } void audio_source_pause(AudioSource* source) { if (!source || !source->source) return; source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW); source->state = AUDIO_PAUSED; } void audio_source_resume(AudioSource* source) { if (!source || !source->source) return; source->source->lpVtbl->Start(source->source, 0, XAUDIO2_COMMIT_NOW); source->state = AUDIO_PLAYING; } void audio_source_set_volume(AudioSource* source, float volume) { if (!source || !source->source) return; source->volume = volume; source->source->lpVtbl->SetVolume(source->source, volume, XAUDIO2_COMMIT_NOW); } void audio_source_set_pitch(AudioSource* source, float pitch) { if (!source || !source->source) return; source->pitch = pitch; source->source->lpVtbl->SetFrequencyRatio(source->source, pitch, XAUDIO2_COMMIT_NOW); } void audio_source_set_loop(AudioSource* source, int loop) { if (!source) return; source->loop = loop; } void audio_set_master_volume(AudioSystem* audio, float volume) { if (!audio || !audio->mastering_voice) return; audio->mastering_voice->lpVtbl->SetVolume(audio->mastering_voice, volume, XAUDIO2_COMMIT_NOW); }