Files
lity3d/Engine/Audio/audio.c
T
shuiyu1123 0669d102ce feat: major engine overhaul - scene demo with lighting, sphere, ground
- Scene Demo: cube + sphere + ground plane with proper lighting
- Audio: XAudio2 dynamic loading + WAV playback
- Physics: expanded collision system
- Renderer: improved pipeline with materials
- UI: extended widget system
- VisualScript: enhanced serializer + block interactions
- ASM: optimized math/memory/profiling routines
- Window: mouse input + scroll support
2026-07-12 08:21:07 +08:00

278 lines
8.1 KiB
C

#include "audio.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <windows.h>
#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);
}