// WAV header spec information: // https://web.archive.org/web/20140327141505/https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ // http://www.topherlee.com/software/pcm-tut-wavformat.html class @sound_file_wav_header { // RIFF Header U8 riff_header[4]; // Contains "RIFF" I32 wav_size; // Size of the wav portion of the file, which follows the first // 8 bytes. File size - 8 U8 wave_header[4]; // Contains "WAVE" // Format Header U8 fmt_header[4]; // Contains "fmt " (includes trailing space) I32 fmt_chunk_size; // Should be 16 for PCM I16 audio_format; // Should be 1 for PCM. 3 for IEEE Float I16 num_channels; I32 sample_rate; I32 byte_rate; // Number of bytes per second. sample_rate * num_channels * // Bytes Per Sample I16 sample_alignment; // num_channels * Bytes Per Sample I16 bit_depth; // Number of bits per sample // Data U8 data_header[4]; // Contains "data" I32 data_bytes; // Number of bytes in data. Number of samples * num_channels * // sample byte size U8 bytes[0]; // Remainder of wave file is bytes }; I64 @audio_get_available_output_stream() { I64 stream = 0; while (FifoI64Cnt(Audio.output[stream].data)) stream++; if (stream > AUDIO_MAX_STREAMS - 1) return -1; return stream; } Bool @audio_buffer_is_wav(@sound_file_wav_header* wav, I64 size) { if (!MemCmp(&wav->riff_header, "RIFF", 4) && !MemCmp(&wav->wave_header, "WAVE", 4)) return TRUE; return FALSE; } U0 @audio_free_sound(Sound* snd) { if (!snd) return; if (snd->data) Free(snd->data); Free(snd); } I64 @audio_play_sound(Sound* snd) { I64 i; I64 stream = @audio_get_available_output_stream; if (stream < 0) return stream; if (!snd->data || !snd->length) return stream; for (i = 0; i < snd->length; i++) FifoI64Ins(Audio.output[stream].data, snd->data[i]); return stream; } Sound* @audio_sound_from_buffer(U32* buf, I64 length) { if (!buf || !length) return NULL; Sound* snd = CAlloc(sizeof(Sound)); snd->rate = 44100; snd->channels = 2; snd->bits = 16; snd->data = buf; snd->length = length; return snd; } U32* @audio_buffer_mono_to_stereo(U16* buf, I64 size) { U32* out = CAlloc(size * 2); I64 i; for (i = 0; i < size / 2; i++) { out[i].u16[0] = buf[i]; out[i].u16[1] = buf[i]; } return out; } U32* @audio_buffer_copy(U32* buf, I64 size) { U32* out = MAlloc(size); MemCpyU32(out, buf, size / 4); return out; } Sound* @audio_sound_from_file(U8* filename) { if (!FileSystem.PathExists(filename)) return NULL; I64 length = 0; U32* buf = NULL; I64 size = 0; U8* data = FileSystem.ReadFile(filename, &size); if (!data) return NULL; if (@audio_buffer_is_wav(data, size)) { @sound_file_wav_header* wav = data; if (wav->fmt_chunk_size == 16 && wav->audio_format == 1 && wav->sample_rate == 48000) { switch (wav->num_channels) { case 1: buf = @audio_buffer_mono_to_stereo(&wav->bytes, wav->data_bytes); length = wav->data_bytes / 2; break; case 2: buf = @audio_buffer_copy(&wav->bytes, wav->data_bytes); length = wav->data_bytes / 4; break; } } } Free(data); return @audio_sound_from_buffer(buf, length); } U0 @audio_snd(I8 ona = 0) { Audio.wavegen.frequency = Ona2Freq(ona); } Audio.SoundFromFile = &@audio_sound_from_file; Audio.FreeSound = &@audio_free_sound; Audio.PlaySound = &@audio_play_sound; Sound* @snd_beep = Audio.SoundFromFile("/mnt/redsea/t/Media/Sounds/Beep.wav"); U0 @audio_beep() { Audio.PlaySound(@snd_beep); } Audio.Beep = &@audio_beep; Function.Patch(&Snd, &@audio_snd); "audio ";