-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathAudioContext.cs
More file actions
176 lines (136 loc) · 5.3 KB
/
AudioContext.cs
File metadata and controls
176 lines (136 loc) · 5.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
using System;
using System.IO;
using System.Runtime.InteropServices;
using static FlowTimer.SDL;
namespace FlowTimer {
public unsafe class AudioContext {
public static int MinBufferSize;
public SDL_AudioSpec AudioSpec;
public uint DeviceId;
public int SampleRate {
get { return AudioSpec.freq; }
}
public int Format {
get { return AudioSpec.format; }
}
public int NumChannels {
get { return AudioSpec.channels; }
}
public int Samples {
get { return AudioSpec.samples; }
}
public int BytesPerSample;
public static void GlobalInit() {
if(SDL_Init(SDL_INIT_AUDIO) < 0) {
throw new Exception("Unable to load SDL!");
}
MMDevice audioDevice = MMDeviceAPI.CreateMMDeviceEnumerator().GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
AudioClient audioClient = audioDevice.CreateAudioCilent();
audioClient.GetSharedModeEnginePeriod(out _, out _, out MinBufferSize, out _);
}
public static void GlobalDestroy() {
SDL_Quit();
}
public AudioContext(int freq, ushort format, byte channels) {
AudioSpec = new SDL_AudioSpec() {
freq = freq,
format = format,
channels = channels,
samples = (ushort) MinBufferSize,
};
switch(format) {
case AUDIO_U8: {
BytesPerSample = 1;
} break;
case AUDIO_S16LSB: {
BytesPerSample = 2;
} break;
};
DeviceId = SDL_OpenAudioDevice(null, 0, ref AudioSpec, out AudioSpec, 0);
if(DeviceId == 0) {
throw new Exception(SDL_GetError());
}
SDL_PauseAudioDevice(DeviceId, 0);
}
public void Destroy() {
SDL_CloseAudioDevice(DeviceId);
}
public void QueueAudio(byte[] pcm) {
fixed(byte* ptr = pcm) SDL_QueueAudio(DeviceId, ptr, pcm.Length);
}
public void ClearQueuedAudio() {
SDL_ClearQueuedAudio(DeviceId);
}
}
public static class Wave {
private static readonly uint WaveId_RIFF = MakeRiff("RIFF");
private static readonly uint WaveId_WAVE = MakeRiff("WAVE");
private static readonly uint WaveId_data = MakeRiff("data");
private static readonly uint WaveId_fmt = MakeRiff("fmt ");
private static uint MakeRiff(string str) {
return (uint) ((str[3] << 24) | (str[2] << 16) | (str[1] << 8) | str[0]);
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct WaveHeader {
public uint RiffId;
public uint Size;
public uint WaveId;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct WaveChunkHeader {
public uint Id;
public uint Size;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct WaveFmt {
public ushort FormatTag;
public ushort Channels;
public uint SampleRate;
public uint AvgBytesPerSec;
public ushort BlockAlign;
public ushort BitsPerSample;
}
public static bool LoadWAV(string fileName, out byte[] pcm, out SDL_AudioSpec spec) {
pcm = new byte[0];
spec = new SDL_AudioSpec();
byte[] bytes = File.ReadAllBytes(fileName);
int pointer = 0;
WaveHeader header = bytes.Consume<WaveHeader>(ref pointer);
if(header.RiffId != WaveId_RIFF) return false;
if(header.WaveId != WaveId_WAVE) return false;
while(pointer < bytes.Length) {
WaveChunkHeader chunkHeader = bytes.Consume<WaveChunkHeader>(ref pointer);
if(chunkHeader.Id == WaveId_fmt) {
WaveFmt fmt = bytes.ReadStruct<WaveFmt>(pointer);
spec = new SDL_AudioSpec() {
freq = (int) fmt.SampleRate,
channels = (byte) fmt.Channels,
};
if(fmt.FormatTag != 1) return false;
switch(fmt.BitsPerSample) {
case 8: {
spec.format = AUDIO_U8;
} break;
case 16: {
spec.format = AUDIO_S16LSB;
} break;
};
} else if(chunkHeader.Id == WaveId_data) {
pcm = bytes.Subarray(pointer, (int) chunkHeader.Size);
}
pointer += (int) chunkHeader.Size;
}
if(spec.format == AUDIO_U8) {
byte[] newPCM = new byte[pcm.Length * 2];
for(int i = 0; i < pcm.Length; i++) {
short sample16 = (short) ((pcm[i] - 0x80) << 8);
newPCM[i * 2 + 0] = (byte) (sample16 & 0xFF);
newPCM[i * 2 + 1] = (byte) (sample16 >> 8);
}
pcm = newPCM;
spec.format = AUDIO_S16LSB;
}
return true;
}
}
}