= 11 rokov pred
rodič
commit
69f865b5b3

+ 156 - 43
gameplay/src/AudioBuffer.cpp

@@ -38,62 +38,85 @@ static long tellStream(void *datasource)
     return stream->position();
 }
 
-AudioBuffer::AudioBuffer(const char* path, ALuint buffer)
-    : _filePath(path), _alBuffer(buffer)
+AudioBuffer::AudioBuffer(const char* path, ALuint* buffer, bool streamed)
+: _filePath(path), _streamed(streamed), _buffersNeededCount(0)
 {
+    memcpy(_alBufferQueue, buffer, sizeof(_alBufferQueue));
 }
 
 AudioBuffer::~AudioBuffer()
 {
     // Remove the buffer from the cache.
     unsigned int bufferCount = (unsigned int)__buffers.size();
-    for (unsigned int i = 0; i < bufferCount; i++)
+
+    if (!_streamed)
     {
-        if (this == __buffers[i])
+        unsigned int bufferCount = (unsigned int)__buffers.size();
+        for (unsigned int i = 0; i < bufferCount; i++)
         {
-            __buffers.erase(__buffers.begin() + i);
-            break;
+            if (this == __buffers[i])
+            {
+                __buffers.erase(__buffers.begin() + i);
+                break;
+            }
         }
     }
+    else if (_streamStateOgg.get())
+    {
+        ov_clear(&_streamStateOgg->oggFile);
+    }
 
-    if (_alBuffer)
+    for (int i = 0; i < STREAMING_BUFFER_QUEUE_SIZE; i++)
     {
-        AL_CHECK( alDeleteBuffers(1, &_alBuffer) );
-        _alBuffer = 0;
+        if (_alBufferQueue[i])
+        {
+            AL_CHECK(alDeleteBuffers(1, &_alBufferQueue[i]));
+            _alBufferQueue[i] = 0;
+        }
     }
 }
 
-AudioBuffer* AudioBuffer::create(const char* path)
+AudioBuffer* AudioBuffer::create(const char* path, bool streamed)
 {
     GP_ASSERT(path);
 
-    // Search the cache for a stream from this file.
-    unsigned int bufferCount = (unsigned int)__buffers.size();
     AudioBuffer* buffer = NULL;
-    for (unsigned int i = 0; i < bufferCount; i++)
+    if (!streamed)
     {
-        buffer = __buffers[i];
-        GP_ASSERT(buffer);
-        if (buffer->_filePath.compare(path) == 0)
+        unsigned int bufferCount = (unsigned int)__buffers.size();
+        for (unsigned int i = 0; i < bufferCount; i++)
         {
-            buffer->addRef();
-            return buffer;
+            buffer = __buffers[i];
+            GP_ASSERT(buffer);
+            if (buffer->_filePath.compare(path) == 0)
+            {
+                buffer->addRef();
+                return buffer;
+            }
         }
     }
+    ALuint alBuffer[STREAMING_BUFFER_QUEUE_SIZE];
+    memset(alBuffer, 0, sizeof(alBuffer));
 
-    ALuint alBuffer;
-
-    // Load audio data into a buffer.
-    AL_CHECK( alGenBuffers(1, &alBuffer) );
-    if (AL_LAST_ERROR())
+    // Create 1 buffer for non-streamed sounds or full queue for streamed ones.
+    unsigned int queueSize = streamed ? STREAMING_BUFFER_QUEUE_SIZE : 1;
+    for (unsigned int i = 0; i < queueSize; i++)
     {
-        GP_ERROR("Failed to create OpenAL buffer; alGenBuffers error: %d", AL_LAST_ERROR());
-        AL_CHECK( alDeleteBuffers(1, &alBuffer) );
-        return NULL;
+        // Load audio data into a buffer.
+        AL_CHECK(alGenBuffers(1, &alBuffer[i]));
+        if (AL_LAST_ERROR())
+        {
+            GP_ERROR("Failed to create OpenAL buffer; alGenBuffers error: %d", AL_LAST_ERROR());
+            AL_CHECK(alDeleteBuffers(1, &alBuffer[i]));
+            return NULL;
+        }
     }
     
+    std::unique_ptr<AudioStreamStateWav> streamStateWav;
+    std::unique_ptr<AudioStreamStateOgg> streamStateOgg;
+
     // Load sound file.
-    std::auto_ptr<Stream> stream(FileSystem::open(path));
+    std::unique_ptr<Stream> stream(FileSystem::open(path));
     if (stream.get() == NULL || !stream->canRead())
     {
         GP_ERROR("Failed to load audio file %s.", path);
@@ -111,7 +134,9 @@ AudioBuffer* AudioBuffer::create(const char* path)
     // Check the file format
     if (memcmp(header, "RIFF", 4) == 0)
     {
-        if (!AudioBuffer::loadWav(stream.get(), alBuffer))
+        // Fill at least one buffer with sound data.
+        streamStateWav.reset(new AudioStreamStateWav());
+        if (!AudioBuffer::loadWav(stream.get(), alBuffer[0], streamed, streamStateWav.get()))
         {
             GP_ERROR("Invalid wave file: %s", path);
             goto cleanup;
@@ -119,7 +144,9 @@ AudioBuffer* AudioBuffer::create(const char* path)
     }
     else if (memcmp(header, "OggS", 4) == 0)
     {
-        if (!AudioBuffer::loadOgg(stream.get(), alBuffer))
+        // Fill at least one buffer with sound data.
+        streamStateOgg.reset(new AudioStreamStateOgg());
+        if (!AudioBuffer::loadOgg(stream.get(), alBuffer[0], streamed, streamStateOgg.get()))
         {
             GP_ERROR("Invalid ogg file: %s", path);
             goto cleanup;
@@ -131,20 +158,31 @@ AudioBuffer* AudioBuffer::create(const char* path)
         goto cleanup;
     }
 
-    buffer = new AudioBuffer(path, alBuffer);
+    buffer = new AudioBuffer(path, alBuffer, streamed);
 
-    // Add the buffer to the cache.
-    __buffers.push_back(buffer);
+    buffer->_fileStream.reset(stream.release());
+    buffer->_streamStateWav.reset(streamStateWav.release());
+    buffer->_streamStateOgg.reset(streamStateOgg.release());
+    if (buffer->_streamStateWav.get())
+        buffer->_buffersNeededCount = (buffer->_streamStateWav->dataSize + STREAMING_BUFFER_SIZE - 1) / STREAMING_BUFFER_SIZE;
+    else if (buffer->_streamStateOgg.get())
+        buffer->_buffersNeededCount = (buffer->_streamStateOgg->dataSize + STREAMING_BUFFER_SIZE - 1) / STREAMING_BUFFER_SIZE;
+
+    if (!streamed)
+        __buffers.push_back(buffer);
 
     return buffer;
     
 cleanup:
-    if (alBuffer)
-        AL_CHECK( alDeleteBuffers(1, &alBuffer) );
+    for (unsigned int i = 0; i < STREAMING_BUFFER_QUEUE_SIZE; i++)
+    {
+        if (alBuffer[i])
+            AL_CHECK(alDeleteBuffers(1, &alBuffer[i]));
+    }
     return NULL;
 }
 
-bool AudioBuffer::loadWav(Stream* stream, ALuint buffer)
+bool AudioBuffer::loadWav(Stream* stream, ALuint buffer, bool streamed, AudioStreamStateWav* streamingState)
 {
     GP_ASSERT(stream);
 
@@ -275,6 +313,19 @@ bool AudioBuffer::loadWav(Stream* stream, ALuint buffer)
                 return false;
             }
 
+            if (streamed)
+            {
+                // Save streaming state for later use.
+                streamingState->dataStart = stream->position();
+                streamingState->dataSize = dataSize;
+                streamingState->format = format;
+                streamingState->frequency = frequency;
+            
+                // Limit data size to STREAMING_BUFFER_SIZE.
+                if (dataSize > STREAMING_BUFFER_SIZE)
+                    dataSize = STREAMING_BUFFER_SIZE;
+            }
+
             char* data = new char[dataSize];
             if (stream->read(data, sizeof(char), dataSize) != dataSize)
             {
@@ -330,11 +381,10 @@ bool AudioBuffer::loadWav(Stream* stream, ALuint buffer)
     return false;
 }
 
-bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer)
+bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer, bool streamed, AudioStreamStateOgg* streamState)
 {
     GP_ASSERT(stream);
 
-    OggVorbis_File ogg_file;
     vorbis_info* info;
     ALenum format;
     long result;
@@ -349,13 +399,13 @@ bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer)
     callbacks.close_func = closeStream;
     callbacks.tell_func = tellStream;
 
-    if ((result = ov_open_callbacks(stream, &ogg_file, NULL, 0, callbacks)) < 0)
+    if ((result = ov_open_callbacks(stream, &streamState->oggFile, NULL, 0, callbacks)) < 0)
     {
         GP_ERROR("Failed to open ogg file.");
         return false;
     }
 
-    info = ov_info(&ogg_file, -1);
+    info = ov_info(&streamState->oggFile, -1);
     GP_ASSERT(info);
     if (info->channels == 1)
         format = AL_FORMAT_MONO16;
@@ -363,12 +413,26 @@ bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer)
         format = AL_FORMAT_STEREO16;
 
     // size = #samples * #channels * 2 (for 16 bit).
-    long data_size = ov_pcm_total(&ogg_file, -1) * info->channels * 2;
+    long data_size = ov_pcm_total(&streamState->oggFile, -1) * info->channels * 2;
+
+    if (streamed)
+    {
+        // Save streaming state for later use.
+        streamState->dataStart = ov_pcm_tell(&streamState->oggFile);
+        streamState->dataSize = data_size;
+        streamState->format = format;
+        streamState->frequency = info->rate;
+        
+        // Limit data size to STREAMING_BUFFER_SIZE.
+        if (data_size > STREAMING_BUFFER_SIZE)
+            data_size = STREAMING_BUFFER_SIZE;
+    }
+
     char* data = new char[data_size];
 
     while (size < data_size)
     {
-        result = ov_read(&ogg_file, data + size, data_size - size, 0, 2, 1, &section);
+        result = ov_read(&streamState->oggFile, data + size, data_size - size, 0, 2, 1, &section);
         if (result > 0)
         {
             size += result;
@@ -392,12 +456,61 @@ bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer)
         return false;
     }
 
-    AL_CHECK( alBufferData(buffer, format, data, data_size, info->rate) );
+    AL_CHECK(alBufferData(buffer, format, data, size, info->rate));
 
     SAFE_DELETE_ARRAY(data);
-    ov_clear(&ogg_file);
+
+    if (!streamed)
+        ov_clear(&streamState->oggFile);
 
     return true;
 }
 
+bool AudioBuffer::streamData(ALuint buffer, bool looped)
+{
+    static char buffers[STREAMING_BUFFER_SIZE];
+    
+    if (_streamStateWav.get())
+    {
+        ALsizei bytesRead = _fileStream->read(buffers, sizeof(char), STREAMING_BUFFER_SIZE);
+        if (bytesRead != STREAMING_BUFFER_SIZE)
+        {
+            if (looped)
+                _fileStream->seek(_streamStateWav->dataStart, SEEK_SET);
+        }
+        if (bytesRead > 0)
+            AL_CHECK(alBufferData(buffer, _streamStateWav->format, buffers, bytesRead, _streamStateWav->frequency));
+        
+        return bytesRead > 0 || looped;
+    }
+    else if (_streamStateOgg.get())
+    {
+        int section;
+        int result = 0;
+        ALsizei bytesRead = 0;
+
+        while (bytesRead < STREAMING_BUFFER_SIZE)
+        {
+            result = ov_read(&_streamStateOgg->oggFile, buffers + bytesRead, STREAMING_BUFFER_SIZE - bytesRead, 0, 2, 1, &section);
+            if (result > 0)
+            {
+                bytesRead += result;
+            }
+            else
+            {
+                if (looped)
+                    ov_pcm_seek(&_streamStateOgg->oggFile, _streamStateOgg->dataStart);
+                break;
+            }
+        }
+
+        if (bytesRead > 0)
+            AL_CHECK(alBufferData(buffer, _streamStateOgg->format, buffers, bytesRead, _streamStateOgg->frequency));
+        
+        return (bytesRead > 0) || looped;
+    }
+    
+    return false;
+}
+
 }

+ 33 - 6
gameplay/src/AudioBuffer.h

@@ -23,7 +23,7 @@ private:
     /**
      * Constructor.
      */
-    AudioBuffer(const char* path, ALuint buffer);
+    AudioBuffer(const char* path, ALuint* buffers, bool streamed);
 
     /**
      * Destructor.
@@ -42,14 +42,41 @@ private:
      * 
      * @return The buffer from a file.
      */
-    static AudioBuffer* create(const char* path);
-    
-    static bool loadWav(Stream* stream, ALuint buffer);
+    static AudioBuffer* create(const char* path, bool streamed);
+
+    struct AudioStreamStateWav
+    {
+        long dataStart;
+        unsigned int dataSize;
+        ALuint format;
+        ALuint frequency;
+    };
+
+    struct AudioStreamStateOgg
+    {
+        long dataStart;
+        unsigned int dataSize;
+        ALuint format;
+        ALuint frequency;
+        OggVorbis_File oggFile;
+    };
+
+    enum { STREAMING_BUFFER_QUEUE_SIZE = 3 };
+    enum { STREAMING_BUFFER_SIZE = 48000 };
+
+    static bool loadWav(Stream* stream, ALuint buffer, bool streamed, AudioStreamStateWav* streamState);
     
-    static bool loadOgg(Stream* stream, ALuint buffer);
+    static bool loadOgg(Stream* stream, ALuint buffer, bool streamed, AudioStreamStateOgg* streamState);
+
+    bool streamData(ALuint buffer, bool looped);
 
+    ALuint _alBufferQueue[STREAMING_BUFFER_QUEUE_SIZE];
     std::string _filePath;
-    ALuint _alBuffer;
+    bool _streamed;
+    std::unique_ptr<Stream> _fileStream;
+    std::unique_ptr<AudioStreamStateWav> _streamStateWav;
+    std::unique_ptr<AudioStreamStateOgg> _streamStateOgg;
+    int _buffersNeededCount;
 };
 
 }

+ 66 - 1
gameplay/src/AudioController.cpp

@@ -8,7 +8,7 @@ namespace gameplay
 {
 
 AudioController::AudioController() 
-    : _alcDevice(NULL), _alcContext(NULL), _pausingSource(NULL)
+: _alcDevice(NULL), _alcContext(NULL), _pausingSource(NULL), _streamingThreadActive(true)
 {
 }
 
@@ -40,10 +40,19 @@ void AudioController::initialize()
     {
         GP_ERROR("Unable to make OpenAL context current. Error: %d\n", alcErr);
     }
+    _streamingMutex.reset(new std::mutex());
 }
 
 void AudioController::finalize()
 {
+    GP_ASSERT(_streamingSources.empty());
+    if (_streamingThread.get())
+    {
+        _streamingThreadActive = false;
+        _streamingThread->join();
+        _streamingThread.reset(NULL);
+    }
+
     alcMakeContextCurrent(NULL);
     if (_alcContext)
     {
@@ -103,4 +112,60 @@ void AudioController::update(float elapsedTime)
     }
 }
 
+void AudioController::addPlayingSource(AudioSource* source)
+{
+    if (_playingSources.find(source) == _playingSources.end())
+    {
+        _playingSources.insert(source);
+
+        if (source->isStreamed())
+        {
+            GP_ASSERT(_streamingSources.find(source) == _streamingSources.end());
+            bool startThread = _streamingSources.empty() && _streamingThread.get() == NULL;
+            _streamingMutex->lock();
+            _streamingSources.insert(source);
+            _streamingMutex->unlock();
+
+            if (startThread)
+                _streamingThread.reset(new std::thread(&streamingThreadProc, this));
+        }
+    }
+}
+
+void AudioController::removePlayingSource(AudioSource* source)
+{
+    if (_pausingSource != source)
+    {
+        std::set<AudioSource*>::iterator iter = _playingSources.find(source);
+        if (iter != _playingSources.end())
+        {
+            _playingSources.erase(iter);
+ 
+            if (source->isStreamed())
+            {
+                GP_ASSERT(_streamingSources.find(source) != _streamingSources.end());
+                _streamingMutex->lock();
+                _streamingSources.erase(source);
+                _streamingMutex->unlock();
+            }
+        }
+    } 
+}
+
+void AudioController::streamingThreadProc(void* arg)
+{
+    AudioController* controller = (AudioController*)arg;
+
+    while (controller->_streamingThreadActive)
+    {
+        controller->_streamingMutex->lock();
+
+        std::for_each(controller->_streamingSources.begin(), controller->_streamingSources.end(), std::mem_fn(&AudioSource::streamDataIfNeeded));
+        
+        controller->_streamingMutex->unlock();
+   
+        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+    }
+}
+
 }

+ 10 - 0
gameplay/src/AudioController.h

@@ -54,11 +54,21 @@ private:
      */
     void update(float elapsedTime);
 
+    void addPlayingSource(AudioSource* source);
+    
+    void removePlayingSource(AudioSource* source);
+
+    static void streamingThreadProc(void* arg);
 
     ALCdevice* _alcDevice;
     ALCcontext* _alcContext;
     std::set<AudioSource*> _playingSources;
+    std::set<AudioSource*> _streamingSources;
     AudioSource* _pausingSource;
+
+    bool _streamingThreadActive;
+    std::unique_ptr<std::thread> _streamingThread;
+    std::unique_ptr<std::mutex> _streamingMutex;
 };
 
 }

+ 73 - 19
gameplay/src/AudioSource.cpp

@@ -13,8 +13,14 @@ AudioSource::AudioSource(AudioBuffer* buffer, ALuint source)
     : _alSource(source), _buffer(buffer), _looped(false), _gain(1.0f), _pitch(1.0f), _node(NULL)
 {
     GP_ASSERT(buffer);
-    AL_CHECK( alSourcei(_alSource, AL_BUFFER, buffer->_alBuffer) );
-    AL_CHECK( alSourcei(_alSource, AL_LOOPING, _looped) );
+
+    if (isStreamed())
+        AL_CHECK(alSourceQueueBuffers(_alSource, 1, &buffer->_alBufferQueue[0]));
+    else
+        AL_CHECK(alSourcei(_alSource, AL_BUFFER, buffer->_alBufferQueue[0]));
+    
+    AL_CHECK(alSourcei(_alSource, AL_LOOPING, _looped && !isStreamed()));
+    
     AL_CHECK( alSourcef(_alSource, AL_PITCH, _pitch) );
     AL_CHECK( alSourcef(_alSource, AL_GAIN, _gain) );
     AL_CHECK( alSourcefv(_alSource, AL_VELOCITY, (const ALfloat*)&_velocity) );
@@ -24,13 +30,18 @@ AudioSource::~AudioSource()
 {
     if (_alSource)
     {
-        AL_CHECK( alDeleteSources(1, &_alSource) );
-        _alSource = 0;
+        if (getState() == PLAYING || getState() == STOPPED)
+        {
+            // Remove the source from the controller's set of currently playing sources.
+            AudioController* audioController = Game::getInstance()->getAudioController();
+            GP_ASSERT(audioController);
+            audioController->removePlayingSource(this);
+        }
     }
     SAFE_RELEASE(_buffer);
 }
 
-AudioSource* AudioSource::create(const char* url)
+AudioSource* AudioSource::create(const char* url, bool streamed)
 {
     // Load from a .audio file.
     std::string pathStr = url;
@@ -49,7 +60,7 @@ AudioSource* AudioSource::create(const char* url)
     }
 
     // Create an audio buffer from this URL.
-    AudioBuffer* buffer = AudioBuffer::create(url);
+    AudioBuffer* buffer = AudioBuffer::create(url, streamed);
     if (buffer == NULL)
         return NULL;
 
@@ -84,8 +95,14 @@ AudioSource* AudioSource::create(Properties* properties)
         return NULL;
     }
 
+    bool streamed = false;
+    if (properties->exists("streamed"))
+    {
+        streamed = properties->getBool("streamed");
+    }
+
     // Create the audio source.
-    AudioSource* audio = AudioSource::create(path.c_str());
+    AudioSource* audio = AudioSource::create(path.c_str(), streamed);
     if (audio == NULL)
     {
         GP_ERROR("Audio file '%s' failed to load properly.", path.c_str());
@@ -133,6 +150,12 @@ AudioSource::State AudioSource::getState() const
     return INITIAL;
 }
 
+bool AudioSource::isStreamed() const
+{
+    GP_ASSERT(_buffer);
+    return _buffer->_streamed;
+}
+
 void AudioSource::play()
 {
     AL_CHECK( alSourcePlay(_alSource) );
@@ -140,8 +163,7 @@ void AudioSource::play()
     // Add the source to the controller's list of currently playing sources.
     AudioController* audioController = Game::getInstance()->getAudioController();
     GP_ASSERT(audioController);
-    if (audioController->_playingSources.find(this) == audioController->_playingSources.end())
-        audioController->_playingSources.insert(this);
+    audioController->addPlayingSource(this);
 }
 
 void AudioSource::pause()
@@ -152,12 +174,7 @@ void AudioSource::pause()
     // if the source is being paused by the user and not the controller itself.
     AudioController* audioController = Game::getInstance()->getAudioController();
     GP_ASSERT(audioController);
-    if (audioController->_pausingSource != this)
-    {
-        std::set<AudioSource*>::iterator iter = audioController->_playingSources.find(this);
-        if (iter != audioController->_playingSources.end())
-            audioController->_playingSources.erase(iter);
-    }
+    audioController->removePlayingSource(this);
 }
 
 void AudioSource::resume()
@@ -175,9 +192,7 @@ void AudioSource::stop()
     // Remove the source from the controller's set of currently playing sources.
     AudioController* audioController = Game::getInstance()->getAudioController();
     GP_ASSERT(audioController);
-    std::set<AudioSource*>::iterator iter = audioController->_playingSources.find(this);
-    if (iter != audioController->_playingSources.end())
-        audioController->_playingSources.erase(iter);
+    audioController->removePlayingSource(this);
 }
 
 void AudioSource::rewind()
@@ -192,7 +207,7 @@ bool AudioSource::isLooped() const
 
 void AudioSource::setLooped(bool looped)
 {
-    AL_CHECK( alSourcei(_alSource, AL_LOOPING, (looped) ? AL_TRUE : AL_FALSE) );
+    AL_CHECK(alSourcei(_alSource, AL_LOOPING, (looped && !isStreamed()) ? AL_TRUE : AL_FALSE));
     if (AL_LAST_ERROR())
     {
         GP_ERROR("Failed to set audio source's looped attribute with error: %d", AL_LAST_ERROR());
@@ -303,4 +318,43 @@ AudioSource* AudioSource::clone(NodeCloneContext &context) const
     return audioClone;
 }
 
+bool AudioSource::streamDataIfNeeded()
+{
+    GP_ASSERT( isStreamed() );
+    if( getState() != PLAYING )
+        return false;
+
+    int queuedBuffers;
+    alGetSourcei(_alSource, AL_BUFFERS_QUEUED, &queuedBuffers);
+ 
+    int buffersNeeded = std::min<int>(_buffer->_buffersNeededCount, AudioBuffer::STREAMING_BUFFER_QUEUE_SIZE);
+    if (queuedBuffers < buffersNeeded)
+    {
+        while (queuedBuffers < buffersNeeded)
+        {
+            if (!_buffer->streamData(_buffer->_alBufferQueue[queuedBuffers], _looped))
+                return false;
+            
+            AL_CHECK( alSourceQueueBuffers(_alSource, 1, &_buffer->_alBufferQueue[queuedBuffers]) );
+            queuedBuffers++;
+        }
+    }
+    else
+    {
+        int processedBuffers;
+        alGetSourcei(_alSource, AL_BUFFERS_PROCESSED, &processedBuffers);
+ 
+        while (processedBuffers-- > 0)
+        {
+            ALuint bufferID;
+            AL_CHECK( alSourceUnqueueBuffers(_alSource, 1, &bufferID) );
+            if (!_buffer->streamData(bufferID, _looped))
+                return false;
+            
+            AL_CHECK( alSourceQueueBuffers(_alSource, 1, &bufferID) );
+        }
+    }
+    return true;
+}
+
 }

+ 11 - 1
gameplay/src/AudioSource.h

@@ -43,10 +43,11 @@ public:
      * "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>" and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional).
      * 
      * @param url The relative location on disk of the sound file or a URL specifying a Properties object defining an audio source.
+     * @param streamed Don't read the entire audio buffer first before playing, instead play immediately from a stream that is read on demand.
      * @return The newly created audio source, or NULL if an audio source cannot be created.
      * @script{create}
      */
-    static AudioSource* create(const char* url);
+    static AudioSource* create(const char* url, bool streamed = false);
 
     /**
      * Create an audio source from the given properties object.
@@ -89,6 +90,13 @@ public:
      */
     AudioSource::State getState() const;
 
+    /**
+     * Determines whether the audio source is streaming or not.
+     *
+     * @return true if the audio source is streaming, false if not.
+     */
+    bool isStreamed() const;
+
     /**
      * Determines whether the audio source is looped or not.
      *
@@ -197,6 +205,8 @@ private:
      */
     AudioSource* clone(NodeCloneContext &context) const;
 
+    bool streamDataIfNeeded();
+
     ALuint _alSource;
     AudioBuffer* _buffer;
     bool _looped;

+ 3 - 0
gameplay/src/Base.h

@@ -28,6 +28,9 @@
 #include <functional>
 #include <bitset>
 #include <typeinfo>
+#include <thread>
+#include <mutex>
+#include <chrono>
 #include "Logger.h"
 
 // Bring common functions from C into global namespace

+ 8 - 8
samples/racer/src/RacerGame.cpp

@@ -32,7 +32,7 @@ RacerGame game;
 
 RacerGame::RacerGame()
     : _scene(NULL), _keyFlags(0), _mouseFlags(0), _steering(0), _gamepad(NULL), _carVehicle(NULL), _upsetTimer(0),
-      _backgroundSound(NULL), _engineSound(NULL), _brakingSound(NULL)
+    _backgroundMusic(NULL), _engineSound(NULL), _brakingSound(NULL)
 {
 }
 
@@ -83,12 +83,12 @@ void RacerGame::initialize()
     }
 
     // Create audio tracks
-    _backgroundSound = AudioSource::create("res/common/background_track.ogg");
-    if (_backgroundSound)
+    _backgroundMusic = AudioSource::create("res/common/background_track.ogg", true);
+    if (_backgroundMusic)
     {
-        _backgroundSound->setLooped(true);
-        _backgroundSound->play();
-        _backgroundSound->setGain(0.3f);
+        _backgroundMusic->setLooped(true);
+        _backgroundMusic->play();
+        _backgroundMusic->setGain(0.3f);
     }
 
     _engineSound = AudioSource::create("res/common/engine_loop.ogg");
@@ -99,7 +99,7 @@ void RacerGame::initialize()
         _engineSound->setGain(0.7f);
     }
 
-    _brakingSound = AudioSource::create("res/common/braking.wav");
+    _brakingSound = AudioSource::create("res/common/braking.wav", true);
     _brakingSound->setLooped(false);
     _brakingSound->setGain(0.5f);
 
@@ -128,7 +128,7 @@ bool RacerGame::initializeScene(Node* node)
 
 void RacerGame::finalize()
 {
-    SAFE_RELEASE(_backgroundSound);
+    SAFE_RELEASE(_backgroundMusic);
     SAFE_RELEASE(_engineSound);
     SAFE_RELEASE(_brakingSound);
     SAFE_RELEASE(_scene);

+ 2 - 2
samples/racer/src/RacerGame.h

@@ -129,8 +129,8 @@ private:
     PhysicsVehicle* _carVehicle;
     float _upsetTimer;
 
-    // Sounds
-    AudioSource* _backgroundSound;
+    // Music and Sounds
+    AudioSource* _backgroundMusic;
     AudioSource* _engineSound;
     AudioSource* _brakingSound;
 };

+ 11 - 11
samples/spaceship/src/SpaceshipGame.cpp

@@ -94,10 +94,10 @@ void SpaceshipGame::initialize()
     initializeSpaceship();
     initializeEnvironment();
 
-    // Create a background audio track
-    _backgroundSound = AudioSource::create("res/background.ogg");
-    if (_backgroundSound)
-        _backgroundSound->setLooped(true);
+    // Create a background music audio track.
+    _backgroundMusic = AudioSource::create("res/background.ogg", true);
+    if (_backgroundMusic)
+        _backgroundMusic->setLooped(true);
 
     // Create font
     _font = Font::create("res/airstrip.gpb");
@@ -223,7 +223,7 @@ void SpaceshipGame::initializeMaterial(Material* material, bool lighting, bool s
 
 void SpaceshipGame::finalize()
 {
-    SAFE_RELEASE(_backgroundSound);
+    SAFE_RELEASE(_backgroundMusic);
     SAFE_RELEASE(_spaceshipSound);
     SAFE_RELEASE(_font);
     SAFE_RELEASE(_stateBlock);
@@ -239,16 +239,16 @@ void SpaceshipGame::update(float elapsedTime)
     {
         _time += t;
 
-        // Play the background track
-        if (_backgroundSound->getState() != AudioSource::PLAYING)
-            _backgroundSound->play();
+        // Play the background music track
+        if (_backgroundMusic->getState() != AudioSource::PLAYING)
+            _backgroundMusic->play();
     }
     else
     {
-        // Stop the background track
-        if (_backgroundSound->getState() == AudioSource::PLAYING || _backgroundSound->getState() == AudioSource::PAUSED)
+        // Stop the background music track
+        if (_backgroundMusic->getState() == AudioSource::PLAYING || _backgroundMusic->getState() == AudioSource::PAUSED)
 		{
-            _backgroundSound->stop();
+            _backgroundMusic->stop();
         	_throttle = 0.0f;
 		}
     }

+ 1 - 1
samples/spaceship/src/SpaceshipGame.h

@@ -143,7 +143,7 @@ private:
     MaterialParameter* _shipSpecularParameter;
 
     // Sounds
-    AudioSource* _backgroundSound;
+    AudioSource* _backgroundMusic;
     AudioSource* _spaceshipSound;
 
     bool _hitSomething;