Audio/Music: Rework Play() (ensure music has started before return and handle exceptions in thread)
This commit is contained in:
parent
c880a431a2
commit
001c9a6a61
|
|
@ -11,6 +11,9 @@
|
||||||
#include <Nazara/Audio/Enums.hpp>
|
#include <Nazara/Audio/Enums.hpp>
|
||||||
#include <Nazara/Audio/SoundEmitter.hpp>
|
#include <Nazara/Audio/SoundEmitter.hpp>
|
||||||
#include <Nazara/Audio/SoundStream.hpp>
|
#include <Nazara/Audio/SoundStream.hpp>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <exception>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace Nz
|
namespace Nz
|
||||||
{
|
{
|
||||||
|
|
@ -56,7 +59,7 @@ namespace Nz
|
||||||
std::unique_ptr<MusicImpl> m_impl;
|
std::unique_ptr<MusicImpl> m_impl;
|
||||||
|
|
||||||
bool FillAndQueueBuffer(unsigned int buffer);
|
bool FillAndQueueBuffer(unsigned int buffer);
|
||||||
void MusicThread();
|
void MusicThread(std::condition_variable& cv, std::mutex& m, std::exception_ptr& err);
|
||||||
void StopThread();
|
void StopThread();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,11 @@
|
||||||
#include <Nazara/Audio/Algorithm.hpp>
|
#include <Nazara/Audio/Algorithm.hpp>
|
||||||
#include <Nazara/Audio/OpenAL.hpp>
|
#include <Nazara/Audio/OpenAL.hpp>
|
||||||
#include <Nazara/Audio/SoundStream.hpp>
|
#include <Nazara/Audio/SoundStream.hpp>
|
||||||
|
#include <Nazara/Core/CallOnExit.hpp>
|
||||||
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <Nazara/Audio/Debug.hpp>
|
#include <Nazara/Audio/Debug.hpp>
|
||||||
|
|
@ -27,6 +28,7 @@ namespace Nz
|
||||||
struct MusicImpl
|
struct MusicImpl
|
||||||
{
|
{
|
||||||
ALenum audioFormat;
|
ALenum audioFormat;
|
||||||
|
std::atomic_bool streaming = false;
|
||||||
std::atomic<UInt64> processedSamples;
|
std::atomic<UInt64> processedSamples;
|
||||||
std::vector<Int16> chunkSamples;
|
std::vector<Int16> chunkSamples;
|
||||||
std::mutex bufferLock;
|
std::mutex bufferLock;
|
||||||
|
|
@ -34,7 +36,6 @@ namespace Nz
|
||||||
std::thread thread;
|
std::thread thread;
|
||||||
UInt64 playingOffset;
|
UInt64 playingOffset;
|
||||||
bool loop = false;
|
bool loop = false;
|
||||||
bool streaming = false;
|
|
||||||
unsigned int sampleRate;
|
unsigned int sampleRate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -304,9 +305,22 @@ namespace Nz
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Starting streaming's thread
|
std::mutex mutex;
|
||||||
|
std::condition_variable cv;
|
||||||
|
|
||||||
|
// Starting streaming thread
|
||||||
m_impl->streaming = true;
|
m_impl->streaming = true;
|
||||||
m_impl->thread = std::thread(&Music::MusicThread, this);
|
|
||||||
|
std::exception_ptr exceptionPtr;
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
m_impl->thread = std::thread(&Music::MusicThread, this, std::ref(cv), std::ref(mutex), std::ref(exceptionPtr));
|
||||||
|
|
||||||
|
// Wait until thread signal it has properly started (or an error occurred)
|
||||||
|
cv.wait(lock);
|
||||||
|
|
||||||
|
if (exceptionPtr)
|
||||||
|
std::rethrow_exception(exceptionPtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -387,20 +401,60 @@ namespace Nz
|
||||||
return sampleRead != sampleCount; // End of stream (Does not happen when looping)
|
return sampleRead != sampleCount; // End of stream (Does not happen when looping)
|
||||||
}
|
}
|
||||||
|
|
||||||
void Music::MusicThread()
|
void Music::MusicThread(std::condition_variable& cv, std::mutex& m, std::exception_ptr& err)
|
||||||
{
|
{
|
||||||
// Allocation of streaming buffers
|
// Allocation of streaming buffers
|
||||||
ALuint buffers[NAZARA_AUDIO_STREAMED_BUFFER_COUNT];
|
std::array<ALuint, NAZARA_AUDIO_STREAMED_BUFFER_COUNT> buffers;
|
||||||
alGenBuffers(NAZARA_AUDIO_STREAMED_BUFFER_COUNT, buffers);
|
alGenBuffers(NAZARA_AUDIO_STREAMED_BUFFER_COUNT, buffers.data());
|
||||||
|
|
||||||
for (unsigned int buffer : buffers)
|
CallOnExit freebuffers([&]
|
||||||
{
|
{
|
||||||
if (FillAndQueueBuffer(buffer))
|
alDeleteBuffers(NAZARA_AUDIO_STREAMED_BUFFER_COUNT, buffers.data());
|
||||||
break; // We have reached the end of the stream, there is no use to add new buffers
|
});
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (ALuint buffer : buffers)
|
||||||
|
{
|
||||||
|
if (FillAndQueueBuffer(buffer))
|
||||||
|
break; // We have reached the end of the stream, there is no use to add new buffers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception&)
|
||||||
|
{
|
||||||
|
err = std::current_exception();
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.notify_all();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CallOnExit unqueueBuffers([&]
|
||||||
|
{
|
||||||
|
// We delete buffers from the stream
|
||||||
|
ALint queuedBufferCount;
|
||||||
|
alGetSourcei(m_source, AL_BUFFERS_QUEUED, &queuedBufferCount);
|
||||||
|
|
||||||
|
ALuint buffer;
|
||||||
|
for (ALint i = 0; i < queuedBufferCount; ++i)
|
||||||
|
alSourceUnqueueBuffers(m_source, 1, &buffer);
|
||||||
|
});
|
||||||
|
|
||||||
alSourcePlay(m_source);
|
alSourcePlay(m_source);
|
||||||
|
|
||||||
|
CallOnExit stopSource([&]
|
||||||
|
{
|
||||||
|
// Stop playing of the sound (in the case where it has not been already done)
|
||||||
|
alSourceStop(m_source);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Signal we're good
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.notify_all();
|
||||||
|
} // m & cv no longer exists from here
|
||||||
|
|
||||||
// Reading loop (Filling new buffers as playing)
|
// Reading loop (Filling new buffers as playing)
|
||||||
while (m_impl->streaming)
|
while (m_impl->streaming)
|
||||||
{
|
{
|
||||||
|
|
@ -438,19 +492,6 @@ namespace Nz
|
||||||
// We go back to sleep
|
// We go back to sleep
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop playing of the sound (in the case where it has not been already done)
|
|
||||||
alSourceStop(m_source);
|
|
||||||
|
|
||||||
// We delete buffers from the stream
|
|
||||||
ALint queuedBufferCount;
|
|
||||||
alGetSourcei(m_source, AL_BUFFERS_QUEUED, &queuedBufferCount);
|
|
||||||
|
|
||||||
ALuint buffer;
|
|
||||||
for (ALint i = 0; i < queuedBufferCount; ++i)
|
|
||||||
alSourceUnqueueBuffers(m_source, 1, &buffer);
|
|
||||||
|
|
||||||
alDeleteBuffers(NAZARA_AUDIO_STREAMED_BUFFER_COUNT, buffers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Music::StopThread()
|
void Music::StopThread()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue