diff --git a/libmve/CMakeLists.txt b/libmve/CMakeLists.txt index ef90c497..5762c55d 100644 --- a/libmve/CMakeLists.txt +++ b/libmve/CMakeLists.txt @@ -3,6 +3,7 @@ set(CPPS # mvelibl.cpp # platform.cpp lnxdsound.cpp + lnx_sound.cpp # d2x implementation decoder8.cpp diff --git a/libmve/lnx_sound.cpp b/libmve/lnx_sound.cpp new file mode 100644 index 00000000..4ccb77d5 --- /dev/null +++ b/libmve/lnx_sound.cpp @@ -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 . + */ + +#include +#include "lnx_sound.h" + +namespace D3 { + +void MovieSoundDevice::SDLAudioCallback(void *userdata, unsigned char *stream, int len) { + auto device = static_cast(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 diff --git a/libmve/lnx_sound.h b/libmve/lnx_sound.h new file mode 100644 index 00000000..d56d7a2e --- /dev/null +++ b/libmve/lnx_sound.h @@ -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 . +*/ + +#ifndef LIBMVE_LNX_SOUND_H_ +#define LIBMVE_LNX_SOUND_H_ + +#include + +#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_ diff --git a/libmve/mveplay.cpp b/libmve/mveplay.cpp index ff952f17..44319a2e 100644 --- a/libmve/mveplay.cpp +++ b/libmve/mveplay.cpp @@ -28,15 +28,18 @@ #include #endif // _WIN32 -#if defined(AUDIO) -#include -#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(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> 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 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>(); + snd_ds = std::make_unique(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 } diff --git a/libmve/sound_interface.h b/libmve/sound_interface.h new file mode 100644 index 00000000..5dcfdd2e --- /dev/null +++ b/libmve/sound_interface.h @@ -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 . + */ + +#ifndef LIBMVE_SOUND_INTERFACE_H_ +#define LIBMVE_SOUND_INTERFACE_H_ + +#include +#include +#include + +namespace D3 { + +class ISoundDevice { +protected: + std::unique_ptr> m_sound_buffer; + bool m_is_compressed = false; + +public: + ISoundDevice() { m_sound_buffer = std::make_unique>(); }; + virtual void Play() {}; + virtual void Stop() {}; + virtual void Lock() {}; + virtual void Unlock() {}; + + std::unique_ptr> &GetBuffer() { return m_sound_buffer; } + [[nodiscard]] bool IsCompressed() const { return m_is_compressed; }; + +}; + +} +#endif // LIBMVE_SOUND_INTERFACE_H_