Move audio callback from AudioSpec into AudioDevice

Though this is not 100% compatible with SDL2, this makes API much
more consistent and less error prone. For example, you don't need
to store AudioSpec along with AudioDevice just to have your callback
lambda around, you don't need to copy AudioSpec from Wav file just
to fill in the callback (see wav demo), you are no more obliged to
take care of locking AudioDevice while replacing the callback.
This commit is contained in:
Dmitry Marakasov 2014-12-16 05:51:50 +03:00
parent 93a77542d8
commit 5abf3558be
5 changed files with 50 additions and 58 deletions

View File

@ -32,19 +32,10 @@
namespace SDL2pp { namespace SDL2pp {
class AudioSpec : public SDL_AudioSpec { class AudioSpec : public SDL_AudioSpec {
public:
typedef std::function<void(Uint8* stream, int len)> AudioCallback;
private:
AudioCallback callback_;
private:
static void SDLCallback(void *userdata, Uint8* stream, int len);
public: public:
AudioSpec(); AudioSpec();
AudioSpec(int freq, SDL_AudioFormat format, Uint8 channels, Uint16 samples, AudioCallback&& callback = AudioCallback()); AudioSpec(int freq, SDL_AudioFormat format, Uint8 channels, Uint16 samples);
AudioSpec(const AudioSpec& other, AudioCallback&& callback = AudioCallback());
~AudioSpec(); ~AudioSpec();
AudioSpec(AudioSpec&& other); AudioSpec(AudioSpec&& other);
@ -52,8 +43,6 @@ public:
AudioSpec(const AudioSpec& other) = delete; AudioSpec(const AudioSpec& other) = delete;
AudioSpec& operator=(const AudioSpec& other) = delete; AudioSpec& operator=(const AudioSpec& other) = delete;
void ChangeCallback(AudioCallback&& callback); // should be called with audio device using this spec locked!
void MergeChanges(const SDL_AudioSpec& obtained); void MergeChanges(const SDL_AudioSpec& obtained);
const SDL_AudioSpec* Get() const; const SDL_AudioSpec* Get() const;
@ -61,9 +50,6 @@ public:
}; };
class AudioDevice { class AudioDevice {
private:
SDL_AudioDeviceID device_id_;
public: public:
class LockHandle { class LockHandle {
friend class AudioDevice; friend class AudioDevice;
@ -83,9 +69,18 @@ public:
LockHandle& operator=(const LockHandle& other) = delete; LockHandle& operator=(const LockHandle& other) = delete;
}; };
typedef std::function<void(Uint8* stream, int len)> AudioCallback;
private:
SDL_AudioDeviceID device_id_;
AudioCallback callback_;
private:
static void SDLCallback(void *userdata, Uint8* stream, int len);
public: public:
AudioDevice(const std::string& device, bool iscapture, const AudioSpec& spec); AudioDevice(const std::string& device, bool iscapture, const AudioSpec& spec, AudioCallback&& callback = AudioCallback());
AudioDevice(const std::string& device, bool iscapture, AudioSpec& spec, int allowed_changes); AudioDevice(const std::string& device, bool iscapture, AudioSpec& spec, int allowed_changes, AudioCallback&& callback = AudioCallback());
virtual ~AudioDevice(); virtual ~AudioDevice();
AudioDevice(const AudioDevice& other) = delete; AudioDevice(const AudioDevice& other) = delete;
@ -98,6 +93,8 @@ public:
void Pause(bool pause_on); void Pause(bool pause_on);
SDL_AudioStatus GetStatus() const; SDL_AudioStatus GetStatus() const;
void ChangeCallback(AudioCallback&& callback);
LockHandle Lock(); LockHandle Lock();
#ifdef SDL2PP_NEW_2_0_4 #ifdef SDL2PP_NEW_2_0_4

View File

@ -25,17 +25,32 @@
namespace SDL2pp { namespace SDL2pp {
AudioDevice::AudioDevice(const std::string& device, bool iscapture, const AudioSpec& spec) { void AudioDevice::SDLCallback(void *userdata, Uint8* stream, int len) {
AudioDevice* audiodevice = static_cast<AudioDevice*>(userdata);
audiodevice->callback_(stream, len);
}
AudioDevice::AudioDevice(const std::string& device, bool iscapture, const AudioSpec& spec, AudioDevice::AudioCallback&& callback) : callback_(std::move(callback)) {
SDL_AudioSpec spec_with_callback = *spec.Get();
if (callback_) {
spec_with_callback.callback = SDLCallback;
spec_with_callback.userdata = static_cast<void*>(this);
}
SDL_AudioSpec obtained; SDL_AudioSpec obtained;
if ((device_id_ = SDL_OpenAudioDevice(device.empty() ? nullptr : device.c_str(), iscapture ? 1 : 0, &spec, &obtained, 0)) == 0) if ((device_id_ = SDL_OpenAudioDevice(device.empty() ? nullptr : device.c_str(), iscapture ? 1 : 0, &spec_with_callback, &obtained, 0)) == 0)
throw Exception("SDL_OpenAudioDevice failed"); throw Exception("SDL_OpenAudioDevice failed");
} }
AudioDevice::AudioDevice(const std::string& device, bool iscapture, AudioSpec& spec, int allowed_changes) { AudioDevice::AudioDevice(const std::string& device, bool iscapture, AudioSpec& spec, int allowed_changes, AudioDevice::AudioCallback&& callback) : callback_(std::move(callback)) {
SDL_AudioSpec spec_with_callback = *spec.Get();
if (callback_) {
spec_with_callback.callback = SDLCallback;
spec_with_callback.userdata = static_cast<void*>(this);
}
SDL_AudioSpec obtained; SDL_AudioSpec obtained;
if ((device_id_ = SDL_OpenAudioDevice(device.empty() ? nullptr : device.c_str(), iscapture ? 1 : 0, &spec, &obtained, allowed_changes)) == 0) if ((device_id_ = SDL_OpenAudioDevice(device.empty() ? nullptr : device.c_str(), iscapture ? 1 : 0, &spec_with_callback, &obtained, allowed_changes)) == 0)
throw Exception("SDL_OpenAudioDevice failed"); throw Exception("SDL_OpenAudioDevice failed");
spec.MergeChanges(obtained); spec.MergeChanges(obtained);
@ -46,7 +61,7 @@ AudioDevice::~AudioDevice() {
SDL_CloseAudioDevice(device_id_); SDL_CloseAudioDevice(device_id_);
} }
AudioDevice::AudioDevice(AudioDevice&& other) noexcept : device_id_(other.device_id_) { AudioDevice::AudioDevice(AudioDevice&& other) noexcept : device_id_(other.device_id_), callback_(std::move(other.callback_)) {
other.device_id_ = 0; other.device_id_ = 0;
} }
@ -58,6 +73,7 @@ AudioDevice& AudioDevice::operator=(AudioDevice&& other) noexcept {
SDL_CloseAudioDevice(device_id_); SDL_CloseAudioDevice(device_id_);
device_id_ = other.device_id_; device_id_ = other.device_id_;
callback_ = std::move(other.callback_);
other.device_id_ = 0; other.device_id_ = 0;
return *this; return *this;
@ -75,6 +91,13 @@ SDL_AudioStatus AudioDevice::GetStatus() const {
return SDL_GetAudioDeviceStatus(device_id_); return SDL_GetAudioDeviceStatus(device_id_);
} }
void AudioDevice::ChangeCallback(AudioDevice::AudioCallback&& callback) {
// make sure callback is not called while it's being replaced
LockHandle lock = Lock();
callback_ = std::move(callback);
}
AudioDevice::LockHandle AudioDevice::Lock() { AudioDevice::LockHandle AudioDevice::Lock() {
return LockHandle(this); return LockHandle(this);
} }

View File

@ -25,33 +25,16 @@
namespace SDL2pp { namespace SDL2pp {
void AudioSpec::SDLCallback(void *userdata, Uint8* stream, int len) {
AudioSpec* audiospec = static_cast<AudioSpec*>(userdata);
audiospec->callback_(stream, len);
}
AudioSpec::AudioSpec() { AudioSpec::AudioSpec() {
std::fill((char*)this, (char*)this + sizeof(SDL_AudioSpec), 0); std::fill((char*)this, (char*)this + sizeof(SDL_AudioSpec), 0);
} }
AudioSpec::AudioSpec(int freq, SDL_AudioFormat format, Uint8 channels, Uint16 samples, AudioSpec::AudioCallback&& callback) AudioSpec::AudioSpec(int freq, SDL_AudioFormat format, Uint8 channels, Uint16 samples) {
: callback_(std::move(callback)) {
std::fill((char*)this, (char*)this + sizeof(SDL_AudioSpec), 0); std::fill((char*)this, (char*)this + sizeof(SDL_AudioSpec), 0);
SDL_AudioSpec::freq = freq; SDL_AudioSpec::freq = freq;
SDL_AudioSpec::format = format; SDL_AudioSpec::format = format;
SDL_AudioSpec::channels = channels; SDL_AudioSpec::channels = channels;
SDL_AudioSpec::samples = samples; SDL_AudioSpec::samples = samples;
if (callback) {
SDL_AudioSpec::callback = SDLCallback;
SDL_AudioSpec::userdata = static_cast<void*>(this);
}
}
AudioSpec::AudioSpec(const AudioSpec& other, AudioSpec::AudioCallback&& callback) : SDL_AudioSpec(*other.Get()), callback_(std::move(callback)) {
if (callback) {
SDL_AudioSpec::callback = SDLCallback;
SDL_AudioSpec::userdata = static_cast<void*>(this);
}
} }
AudioSpec::~AudioSpec() { AudioSpec::~AudioSpec() {
@ -60,10 +43,6 @@ AudioSpec::~AudioSpec() {
AudioSpec::AudioSpec(AudioSpec&&) = default; AudioSpec::AudioSpec(AudioSpec&&) = default;
AudioSpec& AudioSpec::operator=(AudioSpec&&) = default; AudioSpec& AudioSpec::operator=(AudioSpec&&) = default;
void AudioSpec::ChangeCallback(AudioCallback&& callback) {
callback_ = callback;
}
void AudioSpec::MergeChanges(const SDL_AudioSpec& obtained) { void AudioSpec::MergeChanges(const SDL_AudioSpec& obtained) {
freq = obtained.freq; freq = obtained.freq;
format = obtained.format; format = obtained.format;

View File

@ -35,16 +35,16 @@ int Run() {
int64_t nsample = 0; int64_t nsample = 0;
// Setup audio device, and provide callback which plays sine wave with specified frequency // Setup audio device, and provide callback which plays sine wave with specified frequency
AudioSpec spec(samplerate, AUDIO_S16SYS, 1, 4096, [&nsample, frequency, samplerate](Uint8* stream, int len) { AudioSpec spec(samplerate, AUDIO_S16SYS, 1, 4096);
// Open audio device
AudioDevice dev("", 0, spec, [&nsample, frequency, samplerate](Uint8* stream, int len) {
// fill provided buffer with sine wave // fill provided buffer with sine wave
for (Uint8* ptr = stream; ptr < stream + len; ptr += 2) for (Uint8* ptr = stream; ptr < stream + len; ptr += 2)
*(Uint16*)ptr = (Uint16)(32766.0f * sin(nsample++ / (float)samplerate * frequency)); *(Uint16*)ptr = (Uint16)(32766.0f * sin(nsample++ / (float)samplerate * frequency));
} }
); );
// Open audio device
AudioDevice dev("", 0, spec);
// Sound plays after this call // Sound plays after this call
dev.Pause(false); dev.Pause(false);

View File

@ -32,10 +32,10 @@ int Run() {
SDL sdl(SDL_INIT_AUDIO); SDL sdl(SDL_INIT_AUDIO);
Wav wav(TESTDATA_DIR "/test.wav"); Wav wav(TESTDATA_DIR "/test.wav");
// Setup audio device, and provide callback which plays looped wave sound
Uint8* wav_pos = wav.GetBuffer(); Uint8* wav_pos = wav.GetBuffer();
AudioSpec spec(wav.GetSpec(), [&wav, &wav_pos](Uint8* stream, int len) {
// Open audio device
AudioDevice dev("", 0, wav.GetSpec(), [&wav, &wav_pos](Uint8* stream, int len) {
// Fill provided buffer with wave contents // Fill provided buffer with wave contents
Uint8* stream_pos = stream; Uint8* stream_pos = stream;
Uint8* stream_end = stream + len; Uint8* stream_end = stream + len;
@ -54,13 +54,6 @@ int Run() {
} }
); );
// Open audio device
AudioDevice dev("", 0, spec);
// Ensure SDL has set up format conversion for us
if (!spec.IsSameFormat(wav.GetSpec()))
throw std::runtime_error("WAV format is not the same as output format");
// Sound plays after this call // Sound plays after this call
dev.Pause(false); dev.Pause(false);