- 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
278 lines
8.1 KiB
C
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);
|
|
}
|