Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Streaming support, closes #1

  • Loading branch information...
commit e3901c0b7a854aa7bc921449941aa4a9275df3f8 1 parent 0b21dbd
@nddrylliog authored
Showing with 127 additions and 47 deletions.
  1. +127 −47 source/ldkit/Sound.ooc
View
174 source/ldkit/Sound.ooc
@@ -1,5 +1,7 @@
use openal, alut, vorbis, deadlogger
-import openal, alut, vorbis, os/Time, io/File, structs/[ArrayList, HashMap], deadlogger/Log
+import openal, alut, vorbis, os/Time, io/File, structs/[ArrayList, HashMap, List], deadlogger/Log
+
+import Timing
SourceState: enum {
STOPPED
@@ -13,6 +15,8 @@ Source: class {
autofree: Bool
loop := false
+ BURST := static 64
+
state := SourceState PLAYING
state: ALint // state of the sound source
@@ -21,12 +25,36 @@ Source: class {
init: func (=boombox, =sample, =autofree) {
alGenSources(1, sourceID&)
-
- alSourceQueueBuffers(sourceID, sample bufferIDs size, sample bufferIDs toArray())
-
alSource3f(sourceID, AL_POSITION, 0.0, 0.0, 0.0)
alSourcei(sourceID, AL_REFERENCE_DISTANCE, 1.0)
alSourcei(sourceID, AL_MAX_DISTANCE, 1000.0)
+
+ if (sample streaming) {
+ // fill a first few buffers
+ sample refill(0, BURST, |bufferID| , |bufferID|
+ //"Got a bufferID %d for %s" printfln(bufferID, sample path)
+ queue(bufferID)
+ )
+ } else {
+ // we can queue everything at once
+ //"%d [Source %d] Queuing %d buffers for %s" printfln(LTime getTicks(), sourceID, sample bufferIDs size, sample path)
+ queueAll(sample bufferIDs)
+ }
+ }
+
+ unqueue: func (bufferID: ALuint) {
+ alSourceUnqueueBuffers(sourceID, 1, bufferID&)
+ }
+
+ queue: func (bufferID: ALuint) {
+ alSourceQueueBuffers(sourceID, 1, bufferID&)
+ }
+
+ queueAll: func (bufferIDs: ArrayList<ALuint>) {
+ bufferIDs each(|bi| queue(bi))
+
+ // FIXME: the following should work, but it doesn't:
+ //alSourceQueueBuffers(sourceID, bufferIDs size, bufferIDs toArray())
}
getState: func -> SourceState {
@@ -41,25 +69,37 @@ Source: class {
update: func {
if (sample streaming) {
+ processed: Int
+ alGetSourcei(sourceID, AL_BUFFERS_PROCESSED, processed&)
+
+ if (processed > 16) {
+ //"%d processed buffers for %s, refilling" printfln(processed, sample path)
+ sample refill(processed, BURST, |bufferID|
+ unqueue(bufferID)
+ , |bufferID|
+ queue(bufferID)
+ )
+ }
}
if(getState() == SourceState STOPPED) {
if(autofree) {
- // "Freeing source %d, because already stopped" printfln(sourceID)
+ //"%d [Source %d] Freeing because stopped" printfln(LTime getTicks(), sourceID)
boombox freeSource(this)
} else if (loop) {
- "Looping source %d" printfln(sourceID)
+ //"[Source %d] Looping" printfln(sourceID)
play()
}
}
}
play: func {
- //"Playing source %d" printfln(sourceID)
+ //"%d [Source %d] playing" printfln(LTime getTicks(), sourceID)
alSourcePlay(sourceID)
}
free: func {
+ alSourceStop(sourceID)
alDeleteSources(1, sourceID&)
}
@@ -74,39 +114,53 @@ Sample: class {
freq: ALsizei
path: String
-
streaming: Bool
+ // internal state
+ fstream: FStream
+ endian: Int
+ oggFile: OggFile
+ pInfo: VorbisInfo*
+ buffer: Char*
+ hasNext := true
+
// loads the sample
init: func (=path, =streaming) {
- if (path endsWith?(".ogg")) {
- loadOgg(path)
- } else {
+ if (!path endsWith?(".ogg")) {
Exception new("Cannot load %s file, unknown format (only OGG is supported)" format(path)) throw()
}
+ if (streaming) {
+ open()
+ } else {
+ loadFull()
+ }
}
- // This function loads a .ogg file into a memory buffer and returns
- // the format and frequency.
- loadOgg: func (fileName: String) {
- endian := 0 // 0 for little endian, 1 for big endian
+ loadFull: func {
+ open()
+ while (hasNext) {
+ decodeFrame()
+ }
+ //close()
+ }
+
+ open: func {
+ endian = 0 // 0 for little endian, 1 for big endian
// Open for binary reading
- f := fopen(fileName, "rb")
- if (!f) {
- Exception new("Cannot open %s for reading..." format(fileName)) throw()
+ fstream = fopen(path, "rb")
+ if (!fstream) {
+ Exception new("Cannot open %s for reading..." format(path)) throw()
}
- oggFile: OggFile
-
// Try opening the given file
- if (ov_open(f, oggFile&, null, 0) != 0) {
- Exception new("Error opening %s for decoding..." format(fileName)) throw()
+ if (ov_open(fstream, oggFile&, null, 0) != 0) {
+ Exception new("Error opening %s for decoding..." format(path)) throw()
}
// Get some information about the OGG file
- pInfo := ov_info(oggFile&, -1)
+ pInfo = ov_info(oggFile&, -1)
// Check the number of channels... always use 16-bit samples
format = (pInfo@ channels == 1) ?
@@ -116,31 +170,55 @@ Sample: class {
// The frequency of the sampling rate
freq = pInfo@ rate
- buffer: Char* = gc_malloc(TINY_BUFFER_SIZE)
+ // initialize buffer
+ buffer = gc_malloc(TINY_BUFFER_SIZE)
+ }
+
+ refill: func (processed, required: Int, unqueue: Func (ALuint), queue: Func (ALuint)) {
+ for (i in 0..processed) {
+ bufferID := bufferIDs removeAt(0)
+ unqueue(bufferID)
+ alDeleteBuffers(1, bufferID&)
+ }
+
+ for (i in 0..required) {
+ bufferID := decodeFrame()
+ if (bufferID == -1) {
+ break
+ }
+ queue(bufferID)
+ }
+ }
+
+ decodeFrame: func -> ALuint {
bufferID: ALuint
bitStream: Int
- totalSize := 0
- while (true) {
- bytes := ov_read(oggFile&, buffer, TINY_BUFFER_SIZE, endian, 2, 1, bitStream&)
- totalSize += bytes
-
- match {
- case bytes > 0 =>
- // create a new buffer
- alGenBuffers(1, bufferID&)
- bufferIDs add(bufferID)
- alBufferData(bufferID, format, buffer, bytes, freq)
- case bytes < 0 =>
- // something wrong happened
- ov_clear(oggFile&)
- Exception new("Error decoding %s..." format(fileName)) throw()
- case =>
- // end of file!
- break
- }
- }
+ bytes := ov_read(oggFile&, buffer, TINY_BUFFER_SIZE, endian, 2, 1, bitStream&)
+
+ match {
+ case bytes > 0 =>
+ // create a new buffer
+ alGenBuffers(1, bufferID&)
+ bufferIDs add(bufferID)
+ alBufferData(bufferID, format, buffer, bytes, freq)
+ case bytes < 0 =>
+ // something wrong happened
+ close()
+ hasNext = false
+ Exception new("Error decoding %s..." format(path)) throw()
+ case =>
+ // end of file!
+ hasNext = false
+ alDeleteBuffers(1, bufferID&)
+ bufferID = -1
+ }
+
+ //"Got %d bytes, buffer %d size = %d for %s" printfln(bytes, bufferID, TINY_BUFFER_SIZE, path)
+ bufferID
+ }
+ close: func {
// Clean up!
gc_free(buffer)
ov_clear(oggFile&)
@@ -170,14 +248,16 @@ Boombox: class {
logger info("Sound system initialized")
}
- load: func (path: String) -> Sample {
+ load: func (path: String, streaming := false) -> Sample {
aPath := File new(path) getAbsolutePath()
if (cache contains?(aPath)) {
cache get(aPath)
} else {
logger info("Loading audio file... %s" format(path))
- s := Sample new(aPath, false)
- cache put(aPath, s)
+ s := Sample new(aPath, streaming)
+ if (!streaming) {
+ cache put(aPath, s)
+ }
s
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.