mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 11:28:56 +00:00
MVE: Implementing class for sound playback
Implemented abstraction layer and SDL backend.
This commit is contained in:
parent
2f288f6dc9
commit
a39ccfc3ca
@ -3,6 +3,7 @@ set(CPPS
|
|||||||
# mvelibl.cpp
|
# mvelibl.cpp
|
||||||
# platform.cpp
|
# platform.cpp
|
||||||
lnxdsound.cpp
|
lnxdsound.cpp
|
||||||
|
lnx_sound.cpp
|
||||||
|
|
||||||
# d2x implementation
|
# d2x implementation
|
||||||
decoder8.cpp
|
decoder8.cpp
|
||||||
|
64
libmve/lnx_sound.cpp
Normal file
64
libmve/lnx_sound.cpp
Normal 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
52
libmve/lnx_sound.h
Normal 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_
|
@ -28,15 +28,18 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
#if defined(AUDIO)
|
|
||||||
#include <SDL.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "byteswap.h"
|
#include "byteswap.h"
|
||||||
#include "decoders.h"
|
#include "decoders.h"
|
||||||
#include "mvelib.h"
|
#include "mvelib.h"
|
||||||
#include "mve_audio.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_ENDOFSTREAM 0x00
|
||||||
#define MVE_OPCODE_ENDOFCHUNK 0x01
|
#define MVE_OPCODE_ENDOFCHUNK 0x01
|
||||||
@ -65,10 +68,6 @@ int g_spdFactorNum = 0;
|
|||||||
static int g_spdFactorDenom = 10;
|
static int g_spdFactorDenom = 10;
|
||||||
static int g_frameUpdated = 0;
|
static int g_frameUpdated = 0;
|
||||||
|
|
||||||
static ISoundDevice *snd_ds = nullptr;
|
|
||||||
|
|
||||||
static SDL_AudioDeviceID device = 0;
|
|
||||||
|
|
||||||
static short get_short(const unsigned char *data) {
|
static short get_short(const unsigned char *data) {
|
||||||
return D3::convert_le<int16_t>(data[0] | (data[1] << 8));
|
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) {
|
static int default_seg_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context) {
|
||||||
unhandled_chunks[major << 8 | minor]++;
|
unhandled_chunks[major << 8 | minor]++;
|
||||||
// fprintf(stderr, "unknown chunk type %02x/%02x\n", major, minor);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,84 +208,31 @@ end:
|
|||||||
*************************/
|
*************************/
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
|
static std::unique_ptr<D3::MovieSoundDevice> snd_ds;
|
||||||
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 int mve_audio_enabled = 1;
|
static int mve_audio_enabled = 1;
|
||||||
|
#else
|
||||||
static void mve_audio_callback(void *userdata, unsigned char *stream, int len) {
|
static int mve_audio_enabled = 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int create_audiobuf_handler(unsigned char major, unsigned char minor, unsigned char *data, int len,
|
static int create_audiobuf_handler(unsigned char major, unsigned char minor, unsigned char *data, int len,
|
||||||
void *context) {
|
void *context) {
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
int flags;
|
|
||||||
int sample_rate;
|
|
||||||
int desired_buffer;
|
|
||||||
|
|
||||||
int stereo;
|
|
||||||
int bitsize;
|
|
||||||
|
|
||||||
int format;
|
|
||||||
|
|
||||||
if (!mve_audio_enabled)
|
if (!mve_audio_enabled)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
flags = get_ushort(data + 2);
|
int flags = get_ushort(data + 2);
|
||||||
sample_rate = get_ushort(data + 4);
|
int sample_rate = get_ushort(data + 4);
|
||||||
desired_buffer = get_int(data + 6);
|
int desired_buffer = get_int(data + 6);
|
||||||
|
|
||||||
stereo = (flags & MVE_AUDIO_FLAGS_STEREO) ? 1 : 0;
|
int channels = (flags & MVE_AUDIO_FLAGS_STEREO) ? 2 : 1;
|
||||||
bitsize = (flags & MVE_AUDIO_FLAGS_16BIT) ? 1 : 0;
|
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)) {
|
if (minor > 0 && (flags & MVE_AUDIO_FLAGS_COMPRESSED)) {
|
||||||
mve_audio_compressed = 1;
|
is_compressed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitsize == 1) {
|
snd_ds = std::make_unique<D3::MovieSoundDevice>(sample_rate, sample_size, channels, 4096, is_compressed);
|
||||||
#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 >>();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 1;
|
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) {
|
static int play_audio_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context) {
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
if (mve_audio_canplay && !mve_audio_playing) {
|
if (snd_ds && snd_ds->IsInitialized()) {
|
||||||
SDL_PauseAudioDevice(device, 0);
|
snd_ds->Play();
|
||||||
mve_audio_playing = 1;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return 1;
|
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) {
|
static int audio_data_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context) {
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
static const int selected_chan = 1;
|
static const int selected_chan = 1;
|
||||||
if (mve_audio_canplay) {
|
if (snd_ds->IsInitialized()) {
|
||||||
int chan = get_ushort(data + 2);
|
int chan = get_ushort(data + 2);
|
||||||
if (chan & selected_chan) {
|
if (chan & selected_chan) {
|
||||||
if (major == MVE_OPCODE_AUDIOFRAMEDATA) {
|
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 {
|
} else {
|
||||||
// SILENCE, MORTALS!
|
// SILENCE, MORTALS!
|
||||||
int nsamp = get_ushort(data + 4);
|
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;
|
timer_created = 0;
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
if (mve_audio_canplay) {
|
snd_ds.reset();
|
||||||
// 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;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mve_free(g_vBuffers);
|
mve_free(g_vBuffers);
|
||||||
@ -584,7 +525,7 @@ void MVE_rmHoldMovie() { timer_started = 0; }
|
|||||||
|
|
||||||
void MVE_sndInit(ISoundDevice *lpDS) {
|
void MVE_sndInit(ISoundDevice *lpDS) {
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
snd_ds = lpDS;
|
// snd_ds = lpDS;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
46
libmve/sound_interface.h
Normal file
46
libmve/sound_interface.h
Normal 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_
|
Loading…
Reference in New Issue
Block a user