Skip to content
This repository was archived by the owner on May 8, 2024. It is now read-only.

Commit e9a1a92

Browse files
authored
Merge pull request #7 from zyebytevt/zyedev
Add streaming audio, optimize Particles2D, fix few issues
2 parents 64971d9 + 312ec1c commit e9a1a92

File tree

15 files changed

+394
-94
lines changed

15 files changed

+394
-94
lines changed

TODO.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ A lot of stuff has already been done before this file has been started.
4343
[X] Work on (G)UI
4444
[ ] Add more GUI widgets (TextInputBox, CheckBox, RadioButton, Slider)
4545
[X] Change cursor shape
46-
[ ] Explain building steps
47-
[ ] Markup for creating GUI
48-
[ ] Get audio working
49-
[ ] Add streaming audio
46+
[X] Explain building steps
47+
[X] Markup for creating GUI
48+
[X] Get audio working
49+
[X] Add streaming audio
5050
[ ] Fix profiler omg
5151
[ ] Add unittests
5252
[ ] MMAP shenanigans for ZPK files

platform/openal/buffer.d

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,37 @@ import bindbc.openal;
1010
import zyeware.common;
1111
import zyeware.audio;
1212

13+
// TODO: Check memory constness someday.
14+
@asset(Yes.cache)
15+
class AudioStream
16+
{
17+
protected:
18+
const(ubyte)[] mEncodedMemory;
19+
20+
public:
21+
this(const(ubyte)[] encodedMemory)
22+
{
23+
mEncodedMemory = encodedMemory;
24+
}
25+
26+
const(ubyte)[] encodedMemory() pure nothrow
27+
{
28+
return mEncodedMemory;
29+
}
30+
31+
static AudioStream load(string path)
32+
{
33+
VFSFile source = VFS.getFile(path);
34+
ubyte[] bestCommunityData = source.readAll!(ubyte[])();
35+
source.close();
36+
37+
Logger.core.log(LogLevel.debug_, "Loaded file '%s' as audio.", path);
38+
39+
return new AudioStream(bestCommunityData);
40+
}
41+
}
42+
43+
/*
1344
@asset(Yes.cache)
1445
class Sound
1546
{
@@ -56,4 +87,4 @@ class StreamingSound
5687
{
5788
public:
5889
static StreamingSound load(string path);
59-
}
90+
}*/

platform/openal/source.d

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,161 @@ import bindbc.openal;
1111

1212
import zyeware.common;
1313
import zyeware.audio;
14+
import zyeware.audio.thread;
1415

16+
class AudioSource
17+
{
18+
private:
19+
static const(ubyte)[] sEmptyData = new ubyte[0];
20+
21+
protected:
22+
enum State
23+
{
24+
stopped,
25+
paused,
26+
playing
27+
}
28+
29+
// TODO: Add engine-wide buffer size settings
30+
enum bufferSize = 4096 * 4;
31+
enum bufferCount = 4;
32+
33+
float[] mProcBuffer;
34+
35+
AudioStream mAudioStream;
36+
AudioDecoder mDecoder;
37+
uint mSourceId;
38+
uint[] mBufferIDs;
39+
int mProcessed;
40+
41+
State mState;
42+
bool mLooping; // TODO: Maybe add loop point?
43+
AudioBus mBus;
44+
45+
package(zyeware):
46+
void updateBuffers()
47+
{
48+
if (mState == State.stopped)
49+
return;
50+
51+
long lastReadLength;
52+
int processed;
53+
uint pBuf;
54+
alGetSourcei(mSourceId, AL_BUFFERS_PROCESSED, &processed);
55+
56+
while (processed--)
57+
{
58+
alSourceUnqueueBuffers(mSourceId, 1, &pBuf);
59+
60+
lastReadLength = mDecoder.read(mProcBuffer);
61+
62+
if (lastReadLength <= 0)
63+
{
64+
if (mLooping)
65+
{
66+
mDecoder.seekTo(0); // TODO: Replace with a loop point
67+
lastReadLength = mDecoder.read(mProcBuffer);
68+
}
69+
else
70+
break;
71+
}
72+
73+
alBufferData(pBuf, mDecoder.channels == 1 ? AL_FORMAT_MONO_FLOAT32 : AL_FORMAT_STEREO_FLOAT32,
74+
&mProcBuffer[0], cast(int) (lastReadLength * float.sizeof), cast(int) mDecoder.sampleRate);
75+
76+
alSourceQueueBuffers(mSourceId, 1, &pBuf);
77+
}
78+
79+
int buffersQueued;
80+
alGetSourcei(mSourceId, AL_BUFFERS_QUEUED, &buffersQueued);
81+
if (buffersQueued == 0)
82+
stop();
83+
}
84+
85+
public:
86+
this(AudioBus bus = null)
87+
{
88+
mBus = bus ? bus : AudioAPI.getBus("master");
89+
90+
mProcBuffer = new float[bufferSize];
91+
mBufferIDs = new uint[bufferCount];
92+
93+
alGenSources(1, &mSourceId);
94+
alGenBuffers(cast(int) mBufferIDs.length, &mBufferIDs[0]);
95+
96+
AudioThread.register(this);
97+
}
98+
99+
~this()
100+
{
101+
AudioThread.unregister(this);
102+
103+
destroy!false(mDecoder);
104+
105+
alDeleteBuffers(cast(int) mBufferIDs.length, &mBufferIDs[0]);
106+
alDeleteSources(1, &mSourceId);
107+
}
108+
109+
void play()
110+
{
111+
if (mState == State.playing)
112+
stop();
113+
114+
if (mState == State.stopped)
115+
{
116+
long lastReadLength;
117+
for (size_t i; i < bufferCount; ++i)
118+
{
119+
lastReadLength = mDecoder.read(mProcBuffer);
120+
alBufferData(mBufferIDs[i], mDecoder.channels == 1 ? AL_FORMAT_MONO_FLOAT32 : AL_FORMAT_STEREO_FLOAT32,
121+
&mProcBuffer[0], cast(int) (lastReadLength * float.sizeof), cast(int) mDecoder.sampleRate);
122+
alSourceQueueBuffers(mSourceId, 1, &mBufferIDs[i]);
123+
}
124+
}
125+
126+
mState = State.playing;
127+
alSourcePlay(mSourceId);
128+
}
129+
130+
void pause()
131+
{
132+
mState = State.paused;
133+
alSourcePause(mSourceId);
134+
}
135+
136+
void stop()
137+
{
138+
mState = State.stopped;
139+
alSourceStop(mSourceId);
140+
141+
mDecoder.seekTo(0);
142+
143+
int bufferCount;
144+
alGetSourcei(mSourceId, AL_BUFFERS_QUEUED, &bufferCount);
145+
146+
for (size_t i; i < bufferCount; ++i)
147+
{
148+
uint removedBuffer;
149+
alSourceUnqueueBuffers(mSourceId, 1, &removedBuffer);
150+
}
151+
}
152+
153+
inout(AudioStream) stream() inout nothrow
154+
{
155+
return mAudioStream;
156+
}
157+
158+
void stream(AudioStream value)
159+
{
160+
if (mState != State.stopped)
161+
stop();
162+
163+
mAudioStream = value;
164+
mDecoder.setData(mAudioStream.encodedMemory);
165+
}
166+
}
167+
168+
/*
15169
class AudioSource
16170
{
17171
protected:
@@ -113,4 +267,4 @@ public:
113267
{
114268
alSourcei(mId, AL_LOOPING, value);
115269
}
116-
}
270+
}*/

platform/opengl/shader.d

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ protected:
7878

7979
mTextureCount = samplerID;
8080

81-
foreach (string name, int binding; sUniformBlockBindings)
81+
foreach (string uniformName, int binding; sUniformBlockBindings)
8282
{
83-
int blockIndex = glGetUniformBlockIndex(mProgramID, name.toStringz);
83+
int blockIndex = glGetUniformBlockIndex(mProgramID, uniformName.toStringz);
8484
if (blockIndex != -1)
8585
glUniformBlockBinding(mProgramID, blockIndex, binding);
8686
}

source/zyeware/audio/buffer.di

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,23 @@ module zyeware.audio.buffer;
88
import zyeware.common;
99
import zyeware.audio;
1010

11+
alias NoiseBitties = AudioStream;
12+
alias AirVibrationData = AudioStream;
13+
alias EarMassager = AudioStream;
14+
alias SonicStream = AudioStream;
15+
16+
@asset(Yes.cache)
17+
class AudioStream
18+
{
19+
public:
20+
this(const(ubyte)[] encodedMemory);
21+
22+
const(ubyte)[] encodedMemory() pure nothrow;
23+
24+
static AudioStream load(string path);
25+
}
26+
27+
/*
1128
@asset(Yes.cache)
1229
class Sound
1330
{
@@ -22,4 +39,5 @@ class StreamedSound
2239
{
2340
public:
2441
static StreamedSound load(string path);
25-
}
42+
}
43+
*/

source/zyeware/audio/decoder.d

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,26 @@ import zyeware.common;
1212
struct AudioDecoder
1313
{
1414
protected:
15-
VFSFile mFile;
16-
AudioStream stream;
15+
const(ubyte)[] mEncodedMemory;
16+
AudioStream mStream;
1717

1818
public:
19-
@disable this();
19+
//@disable this();
2020
@disable this(this);
2121

22-
/// Params:
23-
/// file = The file used for audio decoding.
24-
/// Throws: `AudioException` if the file could not be opened for decoding.
25-
this(VFSFile file)
22+
~this()
23+
{
24+
if (mStream.isOpenForReading())
25+
destroy!false(mStream);
26+
}
27+
28+
void setData(const(ubyte)[] encodedMemory)
2629
{
27-
mFile = file;
30+
mEncodedMemory = encodedMemory;
2831

2932
try
3033
{
31-
stream.openFromMemory(mFile.readAll!(ubyte[])());
34+
mStream.openFromMemory(mEncodedMemory);
3235
}
3336
catch (AudioFormatsException ex)
3437
{
@@ -38,30 +41,24 @@ public:
3841
size_t errLine = ex.line;
3942
destroyAudioFormatException(ex);
4043

41-
throw new Exception(errMsg, null, errFile, errLine);
44+
throw new AudioException(errMsg, errFile, errLine, null);
4245
}
4346
}
4447

45-
~this()
46-
{
47-
if (stream.isOpenForReading())
48-
destroy!false(stream);
49-
}
50-
5148
/// Tries to read samples into the supplied buffer.
5249
///
5350
/// Params:
5451
/// buffer = The buffer to read into. It's length should be a multiple of the channel count.
5552
/// Returns: The amount of samples actually read.
5653
size_t read(T)(ref T[] buffer)
57-
in (buffer.length % stream.getNumChannels() == 0, "Buffer length is not a multiple of channel count.")
54+
in (buffer.length % mStream.getNumChannels() == 0, "Buffer length is not a multiple of channel count.")
5855
{
5956
static if (is(T == float))
60-
return stream.readSamplesFloat(buffer.ptr, cast(int)(buffer.length/stream.getNumChannels()))
61-
* stream.getNumChannels();
57+
return mStream.readSamplesFloat(buffer.ptr, cast(int)(buffer.length/mStream.getNumChannels()))
58+
* mStream.getNumChannels();
6259
else static if (is(T == double))
63-
return stream.readSamplesDouble(buffer.ptr, cast(int)(buffer.length/stream.getNumChannels()))
64-
* stream.getNumChannels();
60+
return mStream.readSamplesDouble(buffer.ptr, cast(int)(buffer.length/mStream.getNumChannels()))
61+
* mStream.getNumChannels();
6562
else
6663
static assert(false, "'read' cannot process type " ~ T.stringof);
6764
}
@@ -76,27 +73,32 @@ public:
7673
{
7774
auto buffer = new T[samples * mAudioInfo.channels];
7875
static if (is(T == float))
79-
return stream.readSamplesFloat(buffer.ptr, buffer.length/stream.getNumChannels());
76+
return mStream.readSamplesFloat(buffer.ptr, buffer.length/mStream.getNumChannels());
8077
else static if (is(T == double))
81-
return stream.readSamplesDouble(buffer.ptr, buffer.length/stream.getNumChannels());
78+
return mStream.readSamplesDouble(buffer.ptr, buffer.length/mStream.getNumChannels());
8279
else
8380
static assert(false, "'read' cannot process type " ~ T.stringof);
8481
8582
return buffer[0 .. sampleCount];
8683
}*/
8784

85+
void seekTo(size_t frame)
86+
{
87+
mStream.seekPosition(cast(int) frame); // ?????
88+
}
89+
8890
size_t sampleCount()
8991
{
90-
return stream.getLengthInFrames();
92+
return mStream.getLengthInFrames();
9193
}
9294

9395
size_t sampleRate()
9496
{
95-
return cast(size_t) stream.getSamplerate();
97+
return cast(size_t) mStream.getSamplerate();
9698
}
9799

98100
size_t channels()
99101
{
100-
return stream.getNumChannels();
102+
return mStream.getNumChannels();
101103
}
102104
}

0 commit comments

Comments
 (0)