143 lines
No EOL
3.9 KiB
HolyC
143 lines
No EOL
3.9 KiB
HolyC
// 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 "; |