MVE: Implementing class for sound playback

Implemented abstraction layer and SDL backend.
This commit is contained in:
Azamat H. Hackimov 2024-05-11 22:47:13 +03:00
parent 2f288f6dc9
commit a39ccfc3ca
5 changed files with 193 additions and 89 deletions

View File

@ -3,6 +3,7 @@ set(CPPS
# mvelibl.cpp
# platform.cpp
lnxdsound.cpp
lnx_sound.cpp
# d2x implementation
decoder8.cpp

64
libmve/lnx_sound.cpp Normal file
View File

@ -0,0 +1,64 @@
/*
* Descent 3
* Copyright (C) 2024 Descent Developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <functional>
#include "lnx_sound.h"
namespace D3 {
void MovieSoundDevice::SDLAudioCallback(void *userdata, unsigned char *stream, int len) {
auto device = static_cast<MovieSoundDevice *>(userdata);
for (int i = 0; i < len; i += 2) {
int16_t sample = device->m_sound_buffer->front();
device->m_sound_buffer->pop_front();
stream[i] = sample & 0xff;
stream[i + 1] = sample >> 8;
}
}
MovieSoundDevice::MovieSoundDevice(int sample_rate, uint16_t sample_size, uint8_t channels, uint32_t buf_size,
bool is_compressed) {
SDL_AudioFormat format = (sample_size == 2) ? AUDIO_S16LSB : AUDIO_U8;
SDL_AudioSpec spec{
.freq = sample_rate,
.format = format,
.channels = channels,
.size = buf_size,
.callback = &MovieSoundDevice::SDLAudioCallback,
.userdata = this,
};
m_device_id = SDL_OpenAudioDevice(nullptr, 0, &spec, nullptr, SDL_AUDIO_ALLOW_ANY_CHANGE);
m_is_compressed = is_compressed;
};
MovieSoundDevice::~MovieSoundDevice() {
if (m_device_id > 0) {
SDL_CloseAudioDevice(m_device_id);
}
m_sound_buffer.reset();
}
void MovieSoundDevice::Play() { SDL_PauseAudioDevice(m_device_id, 0); }
void MovieSoundDevice::Stop() { SDL_PauseAudioDevice(m_device_id, 1); }
void MovieSoundDevice::Lock() { SDL_LockAudioDevice(m_device_id); };
void MovieSoundDevice::Unlock() { SDL_UnlockAudioDevice(m_device_id); };
} // namespace D3

52
libmve/lnx_sound.h Normal file
View File

@ -0,0 +1,52 @@
/*
* Descent 3
* Copyright (C) 2024 Descent Developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIBMVE_LNX_SOUND_H_
#define LIBMVE_LNX_SOUND_H_
#include <SDL_audio.h>
#include "sound_interface.h"
namespace D3 {
class MovieSoundDevice : ISoundDevice {
private:
SDL_AudioDeviceID m_device_id = 0;
public:
MovieSoundDevice(int sample_rate, uint16_t sample_size, uint8_t channels, uint32_t buf_size, bool is_compressed);
~MovieSoundDevice();
[[nodiscard]] bool IsInitialized() const { return m_device_id > 0 ? true : false; }
void static SDLAudioCallback(void *userdata, unsigned char *stream, int len);
void Play() override;
void Stop() override;
void Lock() override;
void Unlock() override;
using ISoundDevice::GetBuffer;
using ISoundDevice::IsCompressed;
};
}
#endif // LIBMVE_LNX_SOUND_H_

View File

@ -28,15 +28,18 @@
#include <sys/time.h>
#endif // _WIN32
#if defined(AUDIO)
#include <SDL.h>
#endif
#include "byteswap.h"
#include "decoders.h"
#include "mvelib.h"
#include "mve_audio.h"
#include "SystemInterfaces.h"
#ifdef AUDIO
#ifdef _WIN32
#else
#include "lnx_sound.h"
#endif
#endif
#define MVE_OPCODE_ENDOFSTREAM 0x00
#define MVE_OPCODE_ENDOFCHUNK 0x01
@ -65,10 +68,6 @@ int g_spdFactorNum = 0;
static int g_spdFactorDenom = 10;
static int g_frameUpdated = 0;
static ISoundDevice *snd_ds = nullptr;
static SDL_AudioDeviceID device = 0;
static short get_short(const unsigned char *data) {
return D3::convert_le<int16_t>(data[0] | (data[1] << 8));
}
@ -85,7 +84,6 @@ static unsigned int unhandled_chunks[32 * 256];
static int default_seg_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context) {
unhandled_chunks[major << 8 | minor]++;
// fprintf(stderr, "unknown chunk type %02x/%02x\n", major, minor);
return 1;
}
@ -210,84 +208,31 @@ end:
*************************/
#ifdef AUDIO
static std::unique_ptr<std::deque<int16_t>> mve_sound_buffer;
static int mve_audio_playing = 0;
static int mve_audio_canplay = 0;
static int mve_audio_compressed = 0;
static std::unique_ptr<D3::MovieSoundDevice> snd_ds;
static int mve_audio_enabled = 1;
static void mve_audio_callback(void *userdata, unsigned char *stream, int len) {
for(int i = 0; i < len; i += 2) {
int16_t sample = mve_sound_buffer->front();
mve_sound_buffer->pop_front();
stream[i] = sample & 0xff;
stream[i + 1] = sample >> 8;
}
}
#else
static int mve_audio_enabled = 0;
#endif
static int create_audiobuf_handler(unsigned char major, unsigned char minor, unsigned char *data, int len,
void *context) {
#ifdef AUDIO
int flags;
int sample_rate;
int desired_buffer;
int stereo;
int bitsize;
int format;
if (!mve_audio_enabled)
return 1;
flags = get_ushort(data + 2);
sample_rate = get_ushort(data + 4);
desired_buffer = get_int(data + 6);
int flags = get_ushort(data + 2);
int sample_rate = get_ushort(data + 4);
int desired_buffer = get_int(data + 6);
stereo = (flags & MVE_AUDIO_FLAGS_STEREO) ? 1 : 0;
bitsize = (flags & MVE_AUDIO_FLAGS_16BIT) ? 1 : 0;
int channels = (flags & MVE_AUDIO_FLAGS_STEREO) ? 2 : 1;
int sample_size = (flags & MVE_AUDIO_FLAGS_16BIT) ? 2 : 1;
mve_audio_compressed = 0;
bool is_compressed = false;
if (minor > 0 && (flags & MVE_AUDIO_FLAGS_COMPRESSED)) {
mve_audio_compressed = 1;
is_compressed = true;
}
if (bitsize == 1) {
#ifdef OUTRAGE_BIG_ENDIAN
format = AUDIO_S16MSB;
#else
format = AUDIO_S16LSB;
#endif
} else {
format = AUDIO_U8;
}
fprintf(stderr, "creating audio buffers:\n");
fprintf(stderr, "sample rate = %d, stereo = %d, bitsize = %d, compressed = %d\n", sample_rate, stereo,
bitsize ? 16 : 8, mve_audio_compressed);
SDL_AudioSpec spec {
.freq = sample_rate,
.format = (unsigned short)format,
.channels = (unsigned char)(stereo ? 2 : 1),
.size = 4096,
.callback = mve_audio_callback,
};
device = SDL_OpenAudioDevice(nullptr, 0, &spec, nullptr, SDL_AUDIO_ALLOW_ANY_CHANGE);
if (device != 0) {
fprintf(stderr, " success\n");
mve_audio_canplay = 1;
} else {
fprintf(stderr, " failure : %s\n", SDL_GetError());
mve_audio_canplay = 0;
}
mve_audio_canplay = 1;
mve_sound_buffer = std::make_unique<std::deque<int16_t >>();
snd_ds = std::make_unique<D3::MovieSoundDevice>(sample_rate, sample_size, channels, 4096, is_compressed);
#endif
return 1;
@ -295,9 +240,8 @@ static int create_audiobuf_handler(unsigned char major, unsigned char minor, uns
static int play_audio_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context) {
#ifdef AUDIO
if (mve_audio_canplay && !mve_audio_playing) {
SDL_PauseAudioDevice(device, 0);
mve_audio_playing = 1;
if (snd_ds && snd_ds->IsInitialized()) {
snd_ds->Play();
}
#endif
return 1;
@ -306,15 +250,19 @@ static int play_audio_handler(unsigned char major, unsigned char minor, unsigned
static int audio_data_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context) {
#ifdef AUDIO
static const int selected_chan = 1;
if (mve_audio_canplay) {
if (snd_ds->IsInitialized()) {
int chan = get_ushort(data + 2);
if (chan & selected_chan) {
if (major == MVE_OPCODE_AUDIOFRAMEDATA) {
mveaudio_process(mve_sound_buffer, data, mve_audio_compressed);
snd_ds->Lock();
mveaudio_process(snd_ds->GetBuffer(), data, snd_ds->IsCompressed());
snd_ds->Unlock();
} else {
// SILENCE, MORTALS!
int nsamp = get_ushort(data + 4);
mve_sound_buffer->insert(mve_sound_buffer->end(), nsamp, 0);
snd_ds->Lock();
snd_ds->GetBuffer()->insert(snd_ds->GetBuffer()->end(), nsamp, 0);
snd_ds->Unlock();
}
}
}
@ -559,14 +507,7 @@ void MVE_rmEndMovie(MVESTREAM *mve) {
timer_created = 0;
#ifdef AUDIO
if (mve_audio_canplay) {
// only close audio if we opened it
SDL_CloseAudioDevice(device);
mve_audio_canplay = 0;
}
mve_audio_playing = 0;
mve_audio_canplay = 0;
mve_audio_compressed = 0;
snd_ds.reset();
#endif
mve_free(g_vBuffers);
@ -584,7 +525,7 @@ void MVE_rmHoldMovie() { timer_started = 0; }
void MVE_sndInit(ISoundDevice *lpDS) {
#ifdef AUDIO
snd_ds = lpDS;
// snd_ds = lpDS;
#endif
}

46
libmve/sound_interface.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Descent 3
* Copyright (C) 2024 Descent Developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIBMVE_SOUND_INTERFACE_H_
#define LIBMVE_SOUND_INTERFACE_H_
#include <cstdint>
#include <deque>
#include <memory>
namespace D3 {
class ISoundDevice {
protected:
std::unique_ptr<std::deque<int16_t>> m_sound_buffer;
bool m_is_compressed = false;
public:
ISoundDevice() { m_sound_buffer = std::make_unique<std::deque<int16_t>>(); };
virtual void Play() {};
virtual void Stop() {};
virtual void Lock() {};
virtual void Unlock() {};
std::unique_ptr<std::deque<int16_t>> &GetBuffer() { return m_sound_buffer; }
[[nodiscard]] bool IsCompressed() const { return m_is_compressed; };
};
}
#endif // LIBMVE_SOUND_INTERFACE_H_