use SDL_mixer to handle all our sound needs, and delete all the custom code

This commit is contained in:
Tim Felgentreff 2020-04-14 15:18:49 +02:00
parent d3e33a8840
commit 6b73fd7b97
15 changed files with 169 additions and 2052 deletions

View file

@ -249,16 +249,12 @@ set(pathfinder_SRCS
source_group(pathfinder FILES ${pathfinder_SRCS})
set(sound_SRCS
src/sound/fluidsynth.cpp
src/sound/mikmod.cpp
src/sound/music.cpp
src/sound/ogg.cpp
src/sound/script_sound.cpp
src/sound/sound.cpp
src/sound/sound_id.cpp
src/sound/sound_server.cpp
src/sound/unitsound.cpp
src/sound/wav.cpp
)
source_group(sound FILES ${sound_SRCS})
@ -633,6 +629,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_P
find_package(Lua51 REQUIRED)
find_package(PNG REQUIRED)
find_package(SDL2 REQUIRED)
find_package(SDL2_mixer REQUIRED)
find_package(Tolua++ REQUIRED)
find_package(ZLIB REQUIRED)
@ -727,8 +724,8 @@ endif()
# Stratagus definitions
add_definitions(${PNG_DEFINITIONS} -DUSE_ZLIB -DPIXMAPS=\"${PIXMAPSDIRABS}\")
include_directories(${LUA_INCLUDE_DIR} ${PNG_INCLUDE_DIR} ${SDL2_INCLUDE_DIR} ${TOLUA++_INCLUDE_DIR} ${ZLIB_INCLUDE_DIRS})
set(stratagus_LIBS ${stratagus_LIBS} ${LUA_LIBRARIES} ${PNG_LIBRARIES} ${SDL2_LIBRARY} ${TOLUA++_LIBRARY} ${ZLIB_LIBRARIES})
include_directories(${LUA_INCLUDE_DIR} ${PNG_INCLUDE_DIR} ${SDL2_INCLUDE_DIR} ${SDL2_MIXER_INCLUDE_DIR} ${TOLUA++_INCLUDE_DIR} ${ZLIB_INCLUDE_DIRS})
set(stratagus_LIBS ${stratagus_LIBS} ${LUA_LIBRARIES} ${PNG_LIBRARIES} ${SDL2_LIBRARY} ${SDL2_MIXER_LIBRARY} ${TOLUA++_LIBRARY} ${ZLIB_LIBRARIES})
if(WIN32 AND NOT ENABLE_STDIO_REDIRECT)
add_definitions(-DNO_STDIO_REDIRECT)

View file

@ -37,6 +37,7 @@
----------------------------------------------------------------------------*/
#include <vector>
#include "SDL.h"
/*----------------------------------------------------------------------------
-- Definitons
@ -110,6 +111,7 @@ public:
int read(void *buf, size_t len);
int seek(long offset, int whence);
long tell();
SDL_RWops * as_SDL_RWops();
int printf(const char *format, ...) PRINTF_VAARG_ATTRIBUTE(2, 3); // Don't forget to count this
private:

View file

@ -37,6 +37,8 @@
----------------------------------------------------------------------------*/
#include "unitsound.h"
#include "SDL.h"
#include "SDL_mixer.h"
/*----------------------------------------------------------------------------
-- Declarations
@ -44,7 +46,6 @@
class CUnit;
class Missile;
class CSample;
class LuaActionListener;
/*----------------------------------------------------------------------------
@ -111,8 +112,8 @@ public:
unsigned char Range; /// Range is a multiplier for DistanceSilent
unsigned char Number; /// single, group, or table of sounds.
union {
CSample *OneSound; /// if it's only a simple sound
CSample **OneGroup; /// when it's a simple group
Mix_Chunk *OneSound; /// if it's only a simple sound
Mix_Chunk **OneGroup; /// when it's a simple group
struct {
CSound *First; /// first group: selected sound
CSound *Second; /// second group: annoyed sound

View file

@ -46,67 +46,18 @@
#define MaxVolume 255
#define SOUND_BUFFER_SIZE 65536
/**
** RAW samples.
*/
class CSample
{
public:
CSample() : Channels(0), SampleSize(0), Frequency(0), BitsPerSample(0),
Buffer(NULL), Pos(0), Len(0) {}
virtual ~CSample() {}
virtual int Read(void *buf, int len) = 0;
unsigned char Channels; /// mono or stereo
unsigned char SampleSize; /// sample size in bits
unsigned int Frequency; /// frequency in hz
unsigned short BitsPerSample; /// bits in a sample 8/16/32
unsigned char *Buffer; /// sample buffer
int Pos; /// buffer position
int Len; /// length of filled buffer
};
/**
** Play audio flags.
*/
enum _play_audio_flags_ {
PlayAudioStream = 1, /// Stream the file from medium
PlayAudioPreLoad = 2, /// Load compressed in memory
PlayAudioLoadInMemory = 4, /// Preload file into memory
PlayAudioLoadOnDemand = 8 /// Load only if needed.
};
/**
** Fluidsynth flags
*/
#ifdef USE_FLUIDSYNTH
enum SynthState
{
StateCleaned = 0,
StateInitialized,
StatePlaying
};
#endif
/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/
extern CSample *LoadWav(const char *name, int flags); /// Load a wav file
extern CSample *LoadVorbis(const char *name, int flags); /// Load a vorbis file
extern CSample *LoadMikMod(const char *name, int flags); /// Load a module file
extern CSample *LoadFluidSynth(const char *name, int flags); /// Load a MIDI file
/// Set the channel volume
extern int SetChannelVolume(int channel, int volume);
/// Set the channel stereo
extern int SetChannelStereo(int channel, int stereo);
extern void SetChannelStereo(int channel, int stereo);
/// Set the channel's callback for when a sound finishes playing
extern void SetChannelFinishedCallback(int channel, void (*callback)(int channel));
/// Get the sample playing on a channel
extern CSample *GetChannelSample(int channel);
extern Mix_Chunk *GetChannelSample(int channel);
/// Stop a channel
extern void StopChannel(int channel);
/// Stop all channels
@ -115,11 +66,13 @@ extern void StopAllChannels();
/// Check if this unit plays some sound
extern bool UnitSoundIsPlaying(Origin *origin);
/// Check, if this sample is already playing
extern bool SampleIsPlaying(CSample *sample);
extern bool SampleIsPlaying(Mix_Chunk *sample);
/// Load music
extern Mix_Music *LoadMusic(const std::string &name);
/// Load a sample
extern CSample *LoadSample(const std::string &name);
extern Mix_Chunk *LoadSample(const std::string &name);
/// Play a sample
extern int PlaySample(CSample *sample, Origin *origin = NULL);
extern int PlaySample(Mix_Chunk *sample, Origin *origin = NULL);
/// Play a sound file
extern int PlaySoundFile(const std::string &name);
@ -135,7 +88,7 @@ extern bool IsEffectsEnabled();
/// Set the music finished callback
void SetMusicFinishedCallback(void (*callback)());
/// Play a music file
extern int PlayMusic(CSample *sample);
extern int PlayMusic(Mix_Music *sample);
/// Play a music file
extern int PlayMusic(const std::string &file);
/// Stop music playing
@ -158,15 +111,6 @@ extern int InitSound();
/// Cleanup sound.
extern void QuitSound();
#ifdef USE_FLUIDSYNTH
/// Gets the state of Fluidsynth player
SynthState GetFluidSynthState();
/// Init FluidSynth library
extern int InitFluidSynth();
// Cleans all FluidSynth data
extern void CleanFluidSynth(bool reinit = false);
#endif
//@}
#endif // !__SOUND_SERVER_H__

View file

@ -1,317 +0,0 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name fluidsynth.cpp - FluidSynth support */
//
// (c) Copyright 2014 by cybermind
//
// 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; only version 2 of the License.
//
// 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, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
//@{
/*----------------------------------------------------------------------------
-- Includes
----------------------------------------------------------------------------*/
#include "stratagus.h"
#ifdef USE_FLUIDSYNTH // {
#include "sound.h"
#include "sound_server.h"
#include "iolib.h"
#include "unit.h"
#include <fluidsynth.h>
/*----------------------------------------------------------------------------
-- Declarations
----------------------------------------------------------------------------*/
struct FluidSynthData {
CFile *MIDIFile; /// MIDI file handle
};
class CSynthesizer {
public:
CSynthesizer() : Settings(NULL), Synth(NULL), Player(NULL), State(StateCleaned) {}
fluid_settings_t *Settings;
fluid_synth_t *Synth;
fluid_player_t *Player;
SynthState State;
};
CSynthesizer FluidSynthesizer;
class CSampleFluidSynth : public CSample
{
public:
~CSampleFluidSynth();
int Read(void *buf, int len);
FluidSynthData Data;
};
class CSampleFluidSynthStream : public CSample
{
public:
~CSampleFluidSynthStream();
int Read(void *buf, int len);
FluidSynthData Data;
};
/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/
/**
** Type member function to read from the MIDI file
**
** @param buf Buffer to write data to
** @param len Length of the buffer
**
** @return Number of bytes read
*/
int CSampleFluidSynthStream::Read(void *buf, int len)
{
while (this->Len < SOUND_BUFFER_SIZE / 2 && fluid_player_get_status(FluidSynthesizer.Player) == FLUID_PLAYER_PLAYING) {
memcpy(this->Buffer, this->Buffer + this->Pos, this->Len);
this->Pos = 0;
fluid_synth_write_s16(FluidSynthesizer.Synth, SOUND_BUFFER_SIZE / 8, this->Buffer + this->Len, 0, 2,
this->Buffer + this->Len, 1, 2);
this->Len += SOUND_BUFFER_SIZE / 2;
}
if (this->Len < len) {
// EOF
len = this->Len;
}
memcpy(buf, this->Buffer + this->Pos, len);
this->Len -= len;
this->Pos += len;
return len;
}
/**
** Type member function to free sample
*/
CSampleFluidSynthStream::~CSampleFluidSynthStream()
{
this->Data.MIDIFile->close();
delete this->Data.MIDIFile;
delete[] this->Buffer;
}
/**
** Type member function to read from the module
**
** @param buf Buffer to write data to
** @param len Length of the buffer
**
** @return Number of bytes read
*/
int CSampleFluidSynth::Read(void *buf, int len)
{
/* TO-DO: not supported yet*/
return len;
}
/**
** Type member function to free sample
*/
CSampleFluidSynth::~CSampleFluidSynth()
{
delete[] this->Buffer;
}
/**
** Gets the state of Fluidsynth player
**
*/
SynthState GetFluidSynthState()
{
return FluidSynthesizer.State;
}
/**
** Cleans FluidSynth data
**
*/
void CleanFluidSynth(bool reinit)
{
if (reinit) {
delete_fluid_player(FluidSynthesizer.Player);
FluidSynthesizer.Player = new_fluid_player(FluidSynthesizer.Synth);
FluidSynthesizer.State = StateInitialized;
} else if (FluidSynthesizer.State != StateCleaned) {
if (FluidSynthesizer.Player) {
delete_fluid_player(FluidSynthesizer.Player);
}
if (FluidSynthesizer.Synth) {
delete_fluid_synth(FluidSynthesizer.Synth);
}
if (FluidSynthesizer.Settings) {
delete_fluid_settings(FluidSynthesizer.Settings);
}
FluidSynthesizer.State = StateCleaned;
}
}
/**
** Inits FluidSynth and loads SF2 soundfont
**
*/
int InitFluidSynth()
{
// Don't reinit
if (FluidSynthesizer.State > StateCleaned) {
return 0;
}
FluidSynthesizer.State = StateInitialized;
// Settings
FluidSynthesizer.Settings = new_fluid_settings();
if (FluidSynthesizer.Settings == NULL) {
fprintf(stderr, "Failed to create the FluidSynth settings\n");
CleanFluidSynth();
return -1;
}
// Default settings
fluid_settings_setstr(FluidSynthesizer.Settings, "audio.file.type", "raw");
fluid_settings_setnum(FluidSynthesizer.Settings, "synth.sample-rate", 44100);
fluid_settings_setstr(FluidSynthesizer.Settings, "audio.file.format", "s16");
fluid_settings_setstr(FluidSynthesizer.Settings, "audio.file.endian", "little");
fluid_settings_setint(FluidSynthesizer.Settings, "audio.period-size", 4096);
fluid_settings_setint(FluidSynthesizer.Settings, "synth.parallel-render", 1);
fluid_settings_setnum(FluidSynthesizer.Settings, "synth.gain", 0.2);
fluid_settings_setstr(FluidSynthesizer.Settings, "synth-midi-bank-select", "gm");
fluid_settings_setint(FluidSynthesizer.Settings, "synth.synth-polyphony", 24);
// Synthesizer itself
FluidSynthesizer.Synth = new_fluid_synth(FluidSynthesizer.Settings);
if (FluidSynthesizer.Synth == NULL) {
fprintf(stderr, "Failed to create the SF2 synthesizer\n");
CleanFluidSynth();
return -1;
}
// Load the soundfont
std::string sf2Path = LibraryFileName(Preference.SF2Soundfont.c_str());
if (fluid_synth_sfload(FluidSynthesizer.Synth, sf2Path.c_str(), 1) == -1) {
if (fluid_synth_sfload(FluidSynthesizer.Synth, Preference.SF2Soundfont.c_str(), 1) == -1) {
fprintf(stderr, "Failed to load the SoundFont: %s\n", Preference.SF2Soundfont.c_str());
CleanFluidSynth();
return -1;
}
}
// Create player
FluidSynthesizer.Player = new_fluid_player(FluidSynthesizer.Synth);
if (FluidSynthesizer.Player == NULL) {
fprintf(stderr, "Failed to create SF2 player\n");
CleanFluidSynth();
return -1;
}
return 0;
}
/**
** Load MIDI file using FluidSynth library.
**
** @param name MIDI file.
** @param flags Unused.
**
** @return Returns the loaded sample.
*/
CSample *LoadFluidSynth(const char *name, int flags)
{
CSample *sample;
FluidSynthData *data;
CFile *f;
char s[256];
// If library isn't loaded, load it now
if (FluidSynthesizer.State == StateCleaned) {
if (InitFluidSynth() != 0) {
DebugPrint("Can't init FluidSynth!\n");
return NULL;
}
} else if (FluidSynthesizer.State == StatePlaying) {
// Reinit the player
CleanFluidSynth(true);
}
strcpy_s(s, sizeof(s), name);
f = new CFile;
if (f->open(name, CL_OPEN_READ) == -1) {
delete f;
return NULL;
}
// check if this is a MIDI file
if (!fluid_is_midifile(name)) {
fprintf(stderr, "Not a MIDI file: %s\n", name);
f->close();
delete f;
return NULL;
} else {
fluid_player_add(FluidSynthesizer.Player, name);
}
if (flags & PlayAudioStream) {
CSampleFluidSynthStream *sampleFluidSynthStream = new CSampleFluidSynthStream;
sample = sampleFluidSynthStream;
data = &sampleFluidSynthStream->Data;
} else {
CSampleFluidSynth *sampleFluidSynth = new CSampleFluidSynth;
sample = sampleFluidSynth;
data = &sampleFluidSynth->Data;
}
data->MIDIFile = f;
sample->Channels = 2;
sample->SampleSize = 16;
sample->Frequency = 44100;
sample->Pos = 0;
if (flags & PlayAudioStream) {
sample->Len = 0;
sample->Buffer = new unsigned char[SOUND_BUFFER_SIZE];
fluid_player_play(FluidSynthesizer.Player);
FluidSynthesizer.State = StatePlaying;
} else {
/* not supported yet*/
f->close();
delete f;
return NULL;
}
return sample;
}
#endif // } USE_FLUIDSYNTH
//@}

View file

@ -1,277 +0,0 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name mikmod.cpp - MikMod support */
//
// (c) Copyright 2004-2005 by Nehal Mistry
//
// 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; only version 2 of the License.
//
// 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, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
//@{
/*----------------------------------------------------------------------------
-- Includes
----------------------------------------------------------------------------*/
#include "stratagus.h"
#ifdef USE_MIKMOD // {
#include "sound.h"
#include "sound_server.h"
#include "iolib.h"
#include <mikmod.h>
/*----------------------------------------------------------------------------
-- Declarations
----------------------------------------------------------------------------*/
struct MikModData {
MODULE *MikModModule;
CFile *MikModFile;
};
static CFile *CurrentFile;
class CSampleMikMod : public CSample
{
public:
~CSampleMikMod();
int Read(void *buf, int len);
MikModData Data;
};
class CSampleMikModStream : public CSample
{
public:
~CSampleMikModStream();
int Read(void *buf, int len);
MikModData Data;
};
/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/
static BOOL Seek(struct MREADER *, long off, int whence)
{
return CurrentFile->seek(off, whence);
}
static long Tell(struct MREADER *)
{
return CurrentFile->tell();
}
static BOOL Read(struct MREADER *, void *buf, size_t len)
{
return CurrentFile->read(buf, len);
}
static int Get(struct MREADER *)
{
char c;
CurrentFile->read(&c, 1);
return c;
}
static BOOL Eof(struct MREADER *)
{
return 0;
}
static MREADER MReader = { Seek, Tell, Read, Get, Eof };
/**
** Type member function to read from the module
**
** @param buf Buffer to write data to
** @param len Length of the buffer
**
** @return Number of bytes read
*/
int CSampleMikModStream::Read(void *buf, int len)
{
int read;
// fill up the buffer
while (this->Len < SOUND_BUFFER_SIZE / 2 && Player_Active()) {
memcpy(this->Buffer, this->Buffer + this->Pos, this->Len);
this->Pos = 0;
CurrentFile = this->Data.MikModFile;
read = VC_WriteBytes((SBYTE *)this->Buffer + this->Pos,
SOUND_BUFFER_SIZE - (this->Pos + this->Len));
this->Len += read;
}
if (this->Len < len) {
// EOF
len = this->Len;
}
memcpy(buf, this->Buffer + this->Pos, len);
this->Len -= len;
this->Pos += len;
return len;
}
/**
** Type member function to free sample
*/
CSampleMikModStream::~CSampleMikModStream()
{
CurrentFile = this->Data.MikModFile;
Player_Stop();
Player_Free(this->Data.MikModModule);
MikMod_Exit();
this->Data.MikModFile->close();
delete this->Data.MikModFile;
delete[] this->Buffer;
}
/**
** Type member function to read from the module
**
** @param buf Buffer to write data to
** @param len Length of the buffer
**
** @return Number of bytes read
*/
int CSampleMikMod::Read(void *buf, int len)
{
if (this->Len < len) {
len = this->Len;
}
memcpy(buf, this->Buffer + this->Pos, len);
this->Pos += len;
this->Len -= len;
return len;
}
/**
** Type member function to free sample
*/
CSampleMikMod::~CSampleMikMod()
{
delete[] this->Buffer;
}
/**
** Load MikMod.
**
** @param name Filename of the module.
** @param flags Unused.
**
** @return Returns the loaded sample.
*/
CSample *LoadMikMod(const char *name, int flags)
{
CSample *sample;
MikModData *data;
MODULE *module;
CFile *f;
char s[256];
static int registered = 0;
md_mode |= DMODE_STEREO | DMODE_INTERP | DMODE_SURROUND | DMODE_HQMIXER;
MikMod_RegisterDriver(&drv_nos);
if (!registered) {
MikMod_RegisterAllLoaders();
registered = 1;
}
strcpy_s(s, sizeof(s), name);
f = new CFile;
if (f->open(name, CL_OPEN_READ) == -1) {
delete f;
return NULL;
}
CurrentFile = f;
MikMod_Init((char *)"");
module = Player_LoadGeneric(&MReader, 64, 0);
if (!module) {
MikMod_Exit();
f->close();
delete f;
return NULL;
}
if (flags & PlayAudioStream) {
CSampleMikModStream *sampleMikModStream = new CSampleMikModStream;
sample = sampleMikModStream;
data = &sampleMikModStream->Data;
} else {
CSampleMikMod *sampleMikMod = new CSampleMikMod;
sample = sampleMikMod;
data = &sampleMikMod->Data;
}
data->MikModFile = f;
data->MikModModule = module;
sample->Channels = 2;
sample->SampleSize = 16;
sample->Frequency = 44100;
sample->Pos = 0;
if (flags & PlayAudioStream) {
sample->Len = 0;
sample->Buffer = new unsigned char[SOUND_BUFFER_SIZE];
Player_Start(data->MikModModule);
} else {
int read;
int pos;
// FIXME: need to find the correct length
sample->Len = 55000000;
sample->Buffer = new unsigned char[sample->Len];
pos = 0;
Player_Start(data->MikModModule);
while (Player_Active()) {
read = VC_WriteBytes((SBYTE *)sample->Buffer + pos, sample->Len - pos);
pos += read;
}
Player_Stop();
Player_Free(data->MikModModule);
MikMod_Exit();
data->MikModFile->close();
delete data->MikModFile;
}
return sample;
}
#endif // } USE_MIKMOD
//@}

View file

@ -87,7 +87,6 @@ void CheckMusicFinished(bool force)
lua_getglobal(Lua, "MusicStopped");
if (!lua_isfunction(Lua, -1)) {
fprintf(stderr, "No MusicStopped function in Lua\n");
StopMusic();
} else {
LuaCall(0, 1);
}

View file

@ -1,471 +0,0 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name ogg.cpp - ogg support */
//
// (c) Copyright 2005 by Nehal Mistry
//
// 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; only version 2 of the License.
//
// 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, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
//@{
// This file contains code for both the ogg file format, and ogg vorbis.
/*----------------------------------------------------------------------------
-- Includes
----------------------------------------------------------------------------*/
#include "stratagus.h"
#ifdef USE_VORBIS // {
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
#ifdef USE_THEORA
#include <theora/theora.h>
#endif
#include "SDL.h"
#include "SDL_endian.h"
#include "iolib.h"
#include "movie.h"
#include "sound_server.h"
/*----------------------------------------------------------------------------
-- Declaration
----------------------------------------------------------------------------*/
class CSampleVorbis : public CSample
{
public:
~CSampleVorbis();
int Read(void *buf, int len);
OggData Data;
};
class CSampleVorbisStream : public CSample
{
public:
~CSampleVorbisStream();
int Read(void *buf, int len);
OggData Data;
};
/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/
int OggGetNextPage(ogg_page *page, ogg_sync_state *sync, CFile *f)
{
char *buf;
int bytes;
while (ogg_sync_pageout(sync, page) != 1) {
// need more bytes
buf = ogg_sync_buffer(sync, 4096);
bytes = f->read(buf, 4096);
if (!bytes || ogg_sync_wrote(sync, bytes)) {
return -1;
}
}
return 0;
}
int VorbisProcessData(OggData *data, char *buffer)
{
int num_samples;
float **pcm;
float *chan;
int i, j;
int val;
ogg_packet packet;
int len = 0;
while (!len) {
if (ogg_stream_packetout(&data->astream, &packet) != 1) {
if (OggGetNextPage(&data->page, &data->sync, data->File)) {
// EOF
return -1;
}
ogg_stream_pagein(&data->astream, &data->page);
} else {
if (vorbis_synthesis(&data->vblock, &packet) == 0) {
vorbis_synthesis_blockin(&data->vdsp, &data->vblock);
}
while ((num_samples = vorbis_synthesis_pcmout(&data->vdsp, &pcm)) > 0) {
j = 0;
for (i = 0; i < data->vinfo.channels; ++i) {
chan = pcm[i];
for (j = 0; j < num_samples; ++j) {
val = static_cast<int>(chan[j] * 32767.f);
if (val > 32767) {
val = 32767;
} else if (val < -32768) {
val = -32768;
}
*(Sint16 *)(buffer + len
+ (j * 2 * data->vinfo.channels) + i * 2) = (Sint16)val;
}
}
len += j * 2 * data->vinfo.channels;
// we consumed num_samples
vorbis_synthesis_read(&data->vdsp, num_samples);
}
}
}
return len;
}
int OggInit(CFile *f, OggData *data)
{
ogg_packet packet;
int num_vorbis;
#ifdef USE_THEORA
int num_theora;
#endif
int ret;
unsigned magic;
f->read(&magic, sizeof(magic));
if (SDL_SwapLE32(magic) != 0x5367674F) { // "OggS" in ASCII
return -1;
}
f->seek(0, SEEK_SET);
ogg_sync_init(&data->sync);
vorbis_info_init(&data->vinfo);
vorbis_comment_init(&data->vcomment);
#ifdef USE_THEORA
theora_info_init(&data->tinfo);
theora_comment_init(&data->tcomment);
#endif
#ifdef USE_THEORA
num_theora = 0;
#endif
num_vorbis = 0;
while (1) {
ogg_stream_state test;
if (OggGetNextPage(&data->page, &data->sync, f)) {
return -1;
}
if (!ogg_page_bos(&data->page)) {
if (num_vorbis) {
ogg_stream_pagein(&data->astream, &data->page);
}
#ifdef USE_THEORA
if (num_theora) {
ogg_stream_pagein(&data->vstream, &data->page);
}
#endif
break;
}
ogg_stream_init(&test, ogg_page_serialno(&data->page));
ogg_stream_pagein(&test, &data->page);
// initial codec headers
while (ogg_stream_packetout(&test, &packet) == 1) {
#ifdef USE_THEORA
if (theora_decode_header(&data->tinfo, &data->tcomment, &packet) >= 0) {
memcpy(&data->vstream, &test, sizeof(test));
++num_theora;
} else
#endif
if (!vorbis_synthesis_headerin(&data->vinfo, &data->vcomment, &packet)) {
memcpy(&data->astream, &test, sizeof(test));
++num_vorbis;
} else {
ogg_stream_clear(&test);
}
}
}
data->audio = num_vorbis;
#ifdef USE_THEORA
data->video = num_theora;
#endif
// remainint codec headers
while ((num_vorbis && num_vorbis < 3)
#ifdef USE_THEORA
|| (num_theora && num_theora < 3)) {
// are we in the theora page ?
while (num_theora && num_theora < 3 &&
(ret = ogg_stream_packetout(&data->vstream, &packet))) {
if (ret < 0) {
return -1;
}
if (theora_decode_header(&data->tinfo, &data->tcomment, &packet)) {
return -1;
}
++num_theora;
}
#else
) {
#endif
// are we in the vorbis page ?
while (num_vorbis && num_vorbis < 3 &&
(ret = ogg_stream_packetout(&data->astream, &packet))) {
if (ret < 0) {
return -1;
}
if (vorbis_synthesis_headerin(&data->vinfo, &data->vcomment, &packet)) {
return -1;
}
++num_vorbis;
}
if (OggGetNextPage(&data->page, &data->sync, f)) {
break;
}
if (num_vorbis) {
ogg_stream_pagein(&data->astream, &data->page);
}
#ifdef USE_THEORA
if (num_theora) {
ogg_stream_pagein(&data->vstream, &data->page);
}
#endif
}
if (num_vorbis) {
vorbis_synthesis_init(&data->vdsp, &data->vinfo);
vorbis_block_init(&data->vdsp, &data->vblock);
} else {
vorbis_info_clear(&data->vinfo);
vorbis_comment_clear(&data->vcomment);
}
#ifdef USE_THEORA
if (num_theora) {
theora_decode_init(&data->tstate, &data->tinfo);
data->tstate.internal_encode = NULL; // needed for a bug in libtheora (fixed in next release)
} else {
theora_info_clear(&data->tinfo);
theora_comment_clear(&data->tcomment);
}
return !(num_vorbis || num_theora);
#else
return !num_vorbis;
#endif
}
void OggFree(OggData *data)
{
if (data->audio) {
ogg_stream_clear(&data->astream);
vorbis_block_clear(&data->vblock);
vorbis_dsp_clear(&data->vdsp);
vorbis_comment_clear(&data->vcomment);
vorbis_info_clear(&data->vinfo);
}
#ifdef USE_THEORA
if (data->video) {
ogg_stream_clear(&data->vstream);
theora_comment_clear(&data->tcomment);
theora_info_clear(&data->tinfo);
theora_clear(&data->tstate);
}
#endif
ogg_sync_clear(&data->sync);
}
int VorbisStreamRead(CSample *sample, OggData *data, void *buf, int len)
{
int bytes;
if (sample->Pos > SOUND_BUFFER_SIZE / 2) {
memcpy(sample->Buffer, sample->Buffer + sample->Pos, sample->Len);
sample->Pos = 0;
}
while (sample->Len < SOUND_BUFFER_SIZE / 4) {
bytes = VorbisProcessData(data, (char *)sample->Buffer + sample->Pos + sample->Len);
if (bytes > 0) {
sample->Len += bytes;
} else {
break;
}
}
if (sample->Len < len) {
len = sample->Len;
}
memcpy(buf, sample->Buffer + sample->Pos, len);
sample->Pos += len;
sample->Len -= len;
return len;
}
int CSampleVorbisStream::Read(void *buf, int len)
{
return VorbisStreamRead(this, &this->Data, buf, len);
}
CSampleVorbisStream::~CSampleVorbisStream()
{
if (this->Data.File) {
this->Data.File->close();
delete this->Data.File;
}
OggFree(&this->Data);
delete[] this->Buffer;
}
int CSampleVorbis::Read(void *buf, int len)
{
if (len > this->Len) {
len = this->Len;
}
memcpy(buf, this->Buffer + this->Pos, len);
this->Pos += len;
this->Len -= len;
return len;
}
CSampleVorbis::~CSampleVorbis()
{
delete[] this->Buffer;
}
/**
** Load vorbis.
**
** @param name File name.
** @param flags Load flags.
**
** @return Returns the loaded sample.
*/
CSample *LoadVorbis(const char *name, int flags)
{
CSample *sample;
OggData *data;
CFile *f;
vorbis_info *info;
f = new CFile;
if (f->open(name, CL_OPEN_READ) == -1) {
fprintf(stderr, "Can't open file '%s'\n", name);
delete f;
return NULL;
}
if (flags & PlayAudioStream) {
CSampleVorbisStream *sampleVorbisStream = new CSampleVorbisStream;
sample = sampleVorbisStream;
data = &sampleVorbisStream->Data;
} else {
CSampleVorbis *sampleVorbis = new CSampleVorbis;
sample = sampleVorbis;
data = &sampleVorbis->Data;
}
memset(data, 0, sizeof(*data));
if (OggInit(f, data) || !data->audio) {
delete sample;
f->close();
delete f;
return NULL;
}
info = &data->vinfo;
sample->Channels = info->channels;
sample->SampleSize = 16;
sample->Frequency = info->rate;
sample->Len = 0;
sample->Pos = 0;
data->File = f;
if (flags & PlayAudioStream) {
sample->Buffer = new unsigned char[SOUND_BUFFER_SIZE];
} else {
unsigned char *buf;
int total;
ogg_page pg;
ogg_sync_state sync;
// find total size
int pos = f->tell();
ogg_sync_init(&sync);
for (int i = 0; ; ++i) {
f->seek(-1 * i * 4096, SEEK_END);
buf = (unsigned char *)ogg_sync_buffer(&sync, 4096);
f->read(buf, 8192);
ogg_sync_wrote(&sync, 8192);
if (ogg_sync_pageout(&sync, &pg) == 1 && ogg_page_eos(&pg)) {
total = ogg_page_granulepos(&pg) * 2 * sample->Channels;
break;
}
}
f->seek(pos, SEEK_SET);
sample->Buffer = new unsigned char[total];
pos = 0;
int ret;
while ((ret = VorbisStreamRead(sample, &(static_cast<CSampleVorbis *>(sample))->Data,
sample->Buffer + pos, 8192))) {
pos += ret;
}
sample->Len = total;
sample->Pos = 0;
f->close();
delete f;
OggFree(data);
}
return sample;
}
#endif // USE_VORBIS
//@}

View file

@ -78,7 +78,7 @@ int DistanceSilent; /// silent distance
/**
** "Randomly" choose a sample from a sound group.
*/
static CSample *SimpleChooseSample(const CSound &sound)
static Mix_Chunk *SimpleChooseSample(const CSound &sound)
{
if (sound.Number == ONE_SOUND) {
return sound.Sound.OneSound;
@ -92,9 +92,9 @@ static CSample *SimpleChooseSample(const CSound &sound)
/**
** Choose the sample to play
*/
static CSample *ChooseSample(CSound *sound, bool selection, Origin &source)
static Mix_Chunk *ChooseSample(CSound *sound, bool selection, Origin &source)
{
CSample *result = NULL;
Mix_Chunk *result = NULL;
if (!sound || !SoundEnabled()) {
return NULL;
@ -292,7 +292,7 @@ void PlayUnitSound(const CUnit &unit, UnitVoiceGroup voice, bool sampleUnique)
return;
}
CSample *sample = ChooseSample(sound, selection, source);
Mix_Chunk *sample = ChooseSample(sound, selection, source);
if (sampleUnique && SampleIsPlaying(sample)) {
return;
@ -376,7 +376,7 @@ void PlayGameSound(CSound *sound, unsigned char volume, bool always)
}
Origin source = {NULL, 0};
CSample *sample = ChooseSample(sound, false, source);
Mix_Chunk *sample = ChooseSample(sound, false, source);
if (!always && SampleIsPlaying(sample)) {
return;
@ -415,10 +415,10 @@ static void PlaySoundFileCallback(int channel)
int PlayFile(const std::string &name, LuaActionListener *listener)
{
int channel = -1;
CSample *sample = LoadSample(name);
Mix_Chunk *sample = LoadSample(name);
if (sample) {
channel = PlaySample(sample);
channel = PlaySample(sample, NULL);
if (channel != -1) {
SetChannelVolume(channel, MaxVolume);
SetChannelFinishedCallback(channel, PlaySoundFileCallback);
@ -459,8 +459,8 @@ CSound *RegisterSound(const std::vector<std::string> &files)
size_t number = files.size();
if (number > 1) { // load a sound group
id->Sound.OneGroup = new CSample *[number];
memset(id->Sound.OneGroup, 0, sizeof(CSample *) * number);
id->Sound.OneGroup = new Mix_Chunk *[number];
memset(id->Sound.OneGroup, 0, sizeof(Mix_Chunk *) * number);
id->Number = number;
for (unsigned int i = 0; i < number; ++i) {
id->Sound.OneGroup[i] = LoadSample(files[i]);

View file

@ -48,322 +48,35 @@
#include "unit.h"
#include "SDL.h"
#include "SDL_mixer.h"
/*----------------------------------------------------------------------------
-- Variables
----------------------------------------------------------------------------*/
static bool SoundInitialized; /// is sound initialized
static bool MusicPlaying; /// flag true if playing music
static int EffectsVolume = 128; /// effects sound volume
static int MusicVolume = 128; /// music volume
static bool MusicEnabled = true;
static bool EffectsEnabled = true;
/// Channels for sound effects and unit speech
struct SoundChannel {
CSample *Sample; /// sample to play
Origin *Unit; /// pointer to unit, who plays the sound, if any
unsigned char Volume; /// Volume of this channel
signed char Stereo; /// stereo location of sound (-128 left, 0 center, 127 right)
bool Playing; /// channel is currently playing
int Point; /// point in sample if playing or next free channel
void (*FinishedCallback)(int channel); /// Callback for when a sample finishes playing
};
#define MaxChannels 64 /// How many channels are supported
static SoundChannel Channels[MaxChannels];
static int NextFreeChannel;
static struct {
CSample *Sample; /// Music sample
void (*FinishedCallback)(); /// Callback for when music finishes playing
} MusicChannel;
static void ChannelFinished(int channel);
static struct {
SDL_AudioSpec Format;
SDL_mutex *Lock;
SDL_cond *Cond;
SDL_Thread *Thread;
int *MixerBuffer;
Uint8 *Buffer;
bool Running;
} Audio;
/*----------------------------------------------------------------------------
-- Mixers
----------------------------------------------------------------------------*/
/**
** Convert RAW sound data to 44100 hz, Stereo, 16 bits per channel
**
** @param src Source buffer
** @param dest Destination buffer
** @param frequency Frequency of source
** @param chansize Bitrate in bytes per channel of source
** @param channels Number of channels of source
** @param bytes Number of compressed bytes to read
**
** @return Number of bytes written in 'dest'
*/
static int ConvertToStereo32(const char *src, char *dest, int frequency,
int chansize, int channels, int bytes)
{
SDL_AudioCVT acvt;
Uint16 format;
if (chansize == 1) {
format = AUDIO_U8;
} else {
format = AUDIO_S16SYS;
}
SDL_BuildAudioCVT(&acvt, format, channels, frequency, AUDIO_S16SYS, 2, 44100);
acvt.buf = (Uint8 *)dest;
memcpy(dest, src, bytes);
acvt.len = bytes;
SDL_ConvertAudio(&acvt);
return acvt.len_mult * bytes;
}
/**
** Mix music to stereo 32 bit.
**
** @param buffer Buffer for mixed samples.
** @param size Number of samples that fits into buffer.
**
** @todo this functions can be called from inside the SDL audio callback,
** which is bad, the buffer should be precalculated.
*/
static void MixMusicToStereo32(int *buffer, int size)
{
if (MusicPlaying) {
Assert(MusicChannel.Sample);
short *buf = new short[size];
int len = size * sizeof(short);
char *tmp = new char[len];
int div = 176400 / (MusicChannel.Sample->Frequency * (MusicChannel.Sample->SampleSize / 8) * MusicChannel.Sample->Channels);
size = MusicChannel.Sample->Read(tmp, len / div);
int n = ConvertToStereo32(tmp, (char *)buf, MusicChannel.Sample->Frequency,
MusicChannel.Sample->SampleSize / 8, MusicChannel.Sample->Channels, size);
for (int i = 0; i < n / (int)sizeof(*buf); ++i) {
// Add to our samples
// FIXME: why taking out '/ 2' leads to distortion
buffer[i] += buf[i] * MusicVolume / MaxVolume / 2;
}
delete[] tmp;
delete[] buf;
if (n < len) { // End reached
MusicPlaying = false;
delete MusicChannel.Sample;
MusicChannel.Sample = NULL;
if (MusicChannel.FinishedCallback) {
MusicChannel.FinishedCallback();
}
}
}
}
/**
** Mix sample to buffer.
**
** The input samples are adjusted by the local volume and resampled
** to the output frequence.
**
** @param sample Input sample
** @param index Position into input sample
** @param volume Volume of the input sample
** @param stereo Stereo (left/right) position of sample
** @param buffer Output buffer
** @param size Size of output buffer (in samples per channel)
**
** @return The number of bytes used to fill buffer
**
** @todo Can mix faster if signed 8 bit buffers are used.
*/
static int MixSampleToStereo32(CSample *sample, int index, unsigned char volume,
char stereo, int *buffer, int size)
{
static int buf[SOUND_BUFFER_SIZE / 2];
unsigned char left;
unsigned char right;
int div = 176400 / (sample->Frequency * (sample->SampleSize / 8) * sample->Channels);
int local_volume = (int)volume * EffectsVolume / MaxVolume;
if (stereo < 0) {
left = 128;
right = 128 + stereo;
} else {
left = 128 - stereo;
right = 128;
}
Assert(!(index & 1));
size = std::min((sample->Len - index) * div / 2, size);
size = ConvertToStereo32((char *)(sample->Buffer + index), (char *)buf, sample->Frequency,
sample->SampleSize / 8, sample->Channels,
size * 2 / div);
size /= 2;
for (int i = 0; i < size; i += 2) {
// FIXME: why taking out '/ 2' leads to distortion
buffer[i] += ((short *)buf)[i] * local_volume * left / 128 / MaxVolume / 2;
buffer[i + 1] += ((short *)buf)[i + 1] * local_volume * right / 128 / MaxVolume / 2;
}
return 2 * size / div;
}
/**
** Mix channels to stereo 32 bit.
**
** @param buffer Buffer for mixed samples.
** @param size Number of samples that fits into buffer.
**
** @return How many channels become free after mixing them.
*/
static int MixChannelsToStereo32(int *buffer, int size)
{
int new_free_channels = 0;
for (int channel = 0; channel < MaxChannels; ++channel) {
if (Channels[channel].Playing && Channels[channel].Sample) {
int i = MixSampleToStereo32(Channels[channel].Sample,
Channels[channel].Point, Channels[channel].Volume,
Channels[channel].Stereo, buffer, size);
Channels[channel].Point += i;
Assert(Channels[channel].Point <= Channels[channel].Sample->Len);
if (Channels[channel].Point == Channels[channel].Sample->Len) {
ChannelFinished(channel);
++new_free_channels;
}
}
}
return new_free_channels;
}
/**
** Clip mix to output stereo 16 signed bit.
**
** @param mix signed 32 bit input.
** @param size number of samples in input.
** @param output clipped 16 signed bit output buffer.
*/
static void ClipMixToStereo16(const int *mix, int size, short *output)
{
const int *end = mix + size;
while (mix < end) {
int s = (*mix++);
clamp(&s, SHRT_MIN, SHRT_MAX);
*output++ = s;
}
}
/**
** Mix into buffer.
**
** @param buffer Buffer to be filled with samples. Buffer must be big enough.
** @param samples Number of samples.
*/
static void MixIntoBuffer(void *buffer, int samples)
{
// FIXME: can save the memset here, if first channel sets the values
memset(Audio.MixerBuffer, 0, samples * sizeof(*Audio.MixerBuffer));
if (EffectsEnabled) {
// Add channels to mixer buffer
// MixChannelsToStereo32(Audio.MixerBuffer, samples);
}
if (MusicEnabled) {
// Add music to mixer buffer
// MixMusicToStereo32(Audio.MixerBuffer, samples);
}
ClipMixToStereo16(Audio.MixerBuffer, samples, (short *)buffer);
}
/**
** Fill buffer for the sound card.
**
** @see SDL_OpenAudio
**
** @param udata the pointer stored in userdata field of SDL_AudioSpec.
** @param stream pointer to buffer you want to fill with information.
** @param len is length of audio buffer in bytes.
*/
static void FillAudio(void *, Uint8 *stream, int len)
{
if (!Audio.Running) return;
Assert(len == Audio.Format.size);
SDL_memset(stream, 0, len);
SDL_LockMutex(Audio.Lock);
SDL_MixAudio(stream, Audio.Buffer, len, SDL_MIX_MAXVOLUME);
// Signal our FillThread, we can fill the Audio.Buffer again
SDL_CondSignal(Audio.Cond);
SDL_UnlockMutex(Audio.Lock);
}
/**
** Fill audio thread.
*/
static int FillThread(void *)
{
while (Audio.Running == true) {
SDL_LockMutex(Audio.Lock);
#ifdef USE_WIN32
// This is kind of a hackfix, without this on windows audio can get sluggish
if (SDL_CondWaitTimeout(Audio.Cond, Audio.Lock, 1000) == 0) {
#else
if (SDL_CondWaitTimeout(Audio.Cond, Audio.Lock, 100) == 0) {
#endif
MixIntoBuffer(Audio.Buffer, Audio.Format.samples * Audio.Format.channels);
}
SDL_UnlockMutex(Audio.Lock);
}
SDL_LockMutex(Audio.Lock);
// Mustn't call SDL_CloseAudio here, it'll be called again from SDL_Quit
SDL_DestroyCond(Audio.Cond);
SDL_DestroyMutex(Audio.Lock);
return 0;
}
/*----------------------------------------------------------------------------
-- Effects
----------------------------------------------------------------------------*/
/**
** Check if this sound is already playing
*/
bool SampleIsPlaying(CSample *sample)
bool SampleIsPlaying(Mix_Chunk *sample)
{
for (int i = 0; i < MaxChannels; ++i) {
if (Channels[i].Sample == sample && Channels[i].Playing) {
if (Mix_GetChunk(i) == sample && Mix_Playing(i)) {
return true;
}
}
@ -374,7 +87,7 @@ bool UnitSoundIsPlaying(Origin *origin)
{
for (int i = 0; i < MaxChannels; ++i) {
if (origin && Channels[i].Unit && origin->Id && Channels[i].Unit->Id
&& origin->Id == Channels[i].Unit->Id && Channels[i].Playing) {
&& origin->Id == Channels[i].Unit->Id && Mix_Playing(i)) {
return true;
}
}
@ -389,40 +102,8 @@ static void ChannelFinished(int channel)
if (Channels[channel].FinishedCallback) {
Channels[channel].FinishedCallback(channel);
}
delete Channels[channel].Unit;
Channels[channel].Unit = NULL;
Channels[channel].Playing = false;
Channels[channel].Point = NextFreeChannel;
NextFreeChannel = channel;
}
/**
** Put a sound request in the next free channel.
*/
static int FillChannel(CSample *sample, unsigned char volume, char stereo, Origin *origin)
{
Assert(NextFreeChannel < MaxChannels);
int old_free = NextFreeChannel;
int next_free = Channels[NextFreeChannel].Point;
Channels[NextFreeChannel].Volume = volume;
Channels[NextFreeChannel].Point = 0;
Channels[NextFreeChannel].Playing = true;
Channels[NextFreeChannel].Sample = sample;
Channels[NextFreeChannel].Stereo = stereo;
Channels[NextFreeChannel].FinishedCallback = NULL;
if (origin && origin->Base) {
Origin *source = new Origin;
source->Base = origin->Base;
source->Id = origin->Id;
Channels[NextFreeChannel].Unit = source;
}
NextFreeChannel = next_free;
return old_free;
}
/**
@ -435,21 +116,7 @@ static int FillChannel(CSample *sample, unsigned char volume, char stereo, Origi
*/
int SetChannelVolume(int channel, int volume)
{
if (channel < 0 || channel >= MaxChannels) {
return -1;
}
if (volume < 0) {
volume = Channels[channel].Volume;
} else {
SDL_LockMutex(Audio.Lock);
volume = std::min(MaxVolume, volume);
Channels[channel].Volume = volume;
SDL_UnlockMutex(Audio.Lock);
}
return volume;
return Mix_Volume(channel, volume);
}
/**
@ -460,23 +127,15 @@ int SetChannelVolume(int channel, int volume)
**
** @return Current stereo of the channel, -1 for error
*/
int SetChannelStereo(int channel, int stereo)
void SetChannelStereo(int channel, int stereo)
{
if (Preference.StereoSound == false) {
stereo = 0;
}
if (channel < 0 || channel >= MaxChannels) {
return -1;
}
if (stereo < -128 || stereo > 127) {
stereo = Channels[channel].Stereo;
Mix_SetPanning(channel, 255, 255);
} else {
SDL_LockMutex(Audio.Lock);
Channels[channel].Stereo = stereo;
SDL_UnlockMutex(Audio.Lock);
Assert(stereo >= -128 && stereo <= 127);
int left_volume = (127 - stereo) * 2; // 127 would play only on the right
Mix_SetPanning(channel, left_volume, 254 - left_volume);
}
return stereo;
}
/**
@ -496,12 +155,12 @@ void SetChannelFinishedCallback(int channel, void (*callback)(int channel))
/**
** Get the sample playing on a channel
*/
CSample *GetChannelSample(int channel)
Mix_Chunk *GetChannelSample(int channel)
{
if (channel < 0 || channel >= MaxChannels) {
return NULL;
if (Mix_Playing(channel)) {
return Mix_GetChunk(channel);
}
return Channels[channel].Sample;
return NULL;
}
/**
@ -511,13 +170,7 @@ CSample *GetChannelSample(int channel)
*/
void StopChannel(int channel)
{
SDL_LockMutex(Audio.Lock);
if (channel >= 0 && channel < MaxChannels) {
if (Channels[channel].Playing) {
ChannelFinished(channel);
}
}
SDL_UnlockMutex(Audio.Lock);
Mix_HaltChannel(channel);
}
/**
@ -525,43 +178,57 @@ void StopChannel(int channel)
*/
void StopAllChannels()
{
SDL_LockMutex(Audio.Lock);
for (int i = 0; i < MaxChannels; ++i) {
if (Channels[i].Playing) {
ChannelFinished(i);
}
}
SDL_UnlockMutex(Audio.Lock);
Mix_HaltChannel(-1);
}
static CSample *LoadSample(const char *name, enum _play_audio_flags_ flag)
static Mix_Music *LoadMusic(const char *name)
{
CSample *sampleWav = LoadWav(name, flag);
Mix_Music *r = Mix_LoadMUS(name);
if (r) {
return r;
}
if (sampleWav) {
return sampleWav;
CFile *f = new CFile;
if (f->open(name, CL_OPEN_READ) == -1) {
printf("Can't open file '%s'\n", name);
delete f;
return NULL;
}
#ifdef USE_VORBIS
CSample *sampleVorbis = LoadVorbis(name, flag);
if (sampleVorbis) {
return sampleVorbis;
}
#endif
#ifdef USE_MIKMOD
CSample *sampleMikMod = LoadMikMod(name, flag);
if (sampleMikMod) {
return sampleMikMod;
}
#endif
#ifdef USE_FLUIDSYNTH
CSample *sampleFluidSynth = LoadFluidSynth(name, flag);
if (sampleFluidSynth) {
return sampleFluidSynth;
}
#endif
return NULL;
return Mix_LoadMUS_RW(f->as_SDL_RWops(), 0);
}
static Mix_Chunk *LoadSample(const char *name)
{
Mix_Chunk *r = Mix_LoadWAV(name);
if (r) {
return r;
}
CFile *f = new CFile;
if (f->open(name, CL_OPEN_READ) == -1) {
printf("Can't open file '%s'\n", name);
delete f;
return NULL;
}
return Mix_LoadWAV_RW(f->as_SDL_RWops(), 0);
}
/**
** Load a music file
**
** @param name File name
**
** @return Mix_Music pointer
*/
Mix_Music *LoadMusic(const std::string &name)
{
const std::string filename = LibraryFileName(name.c_str());
Mix_Music *music = LoadMusic(filename.c_str());
if (music == NULL) {
fprintf(stderr, "Can't load the music '%s'\n", name.c_str());
}
return music;
}
/**
** Load a sample
@ -572,13 +239,13 @@ static CSample *LoadSample(const char *name, enum _play_audio_flags_ flag)
**
** @todo Add streaming, caching support.
*/
CSample *LoadSample(const std::string &name)
Mix_Chunk *LoadSample(const std::string &name)
{
const std::string filename = LibraryFileName(name.c_str());
CSample *sample = LoadSample(filename.c_str(), PlayAudioLoadInMemory);
Mix_Chunk *sample = LoadSample(filename.c_str());
if (sample == NULL) {
fprintf(stderr, "Can't load the sound '%s'\n", name.c_str());
fprintf(stderr, "Can't load the sound '%s': %s\n", name.c_str(), Mix_GetError());
}
return sample;
}
@ -590,34 +257,23 @@ CSample *LoadSample(const std::string &name)
**
** @return Channel number, -1 for error
*/
int PlaySample(CSample *sample, Origin *origin)
int PlaySample(Mix_Chunk *sample, Origin *origin)
{
int channel = -1;
SDL_LockMutex(Audio.Lock);
if (SoundEnabled() && EffectsEnabled && sample && NextFreeChannel != MaxChannels) {
channel = FillChannel(sample, EffectsVolume, 0, origin);
DebugPrint("play sample %d\n" _C_ sample->volume);
if (SoundEnabled() && EffectsEnabled && sample) {
channel = Mix_PlayChannel(-1, sample, 0);
Channels[channel].FinishedCallback = NULL;
if (origin && origin->Base) {
Origin *source = new Origin;
source->Base = origin->Base;
source->Id = origin->Id;
Channels[channel].Unit = source;
}
}
SDL_UnlockMutex(Audio.Lock);
return channel;
}
/**
** Play a sound file
**
** @param name Filename of a sound to play
**
** @return Channel number the sound is playing on, -1 for error
*/
int PlaySoundFile(const std::string &name)
{
CSample *sample = LoadSample(name);
if (sample) {
return PlaySample(sample);
}
return -1;
}
/**
** Set the global sound volume.
**
@ -625,8 +281,7 @@ int PlaySoundFile(const std::string &name)
*/
void SetEffectsVolume(int volume)
{
clamp(&volume, 0, MaxVolume);
EffectsVolume = volume;
Mix_Volume(-1, volume);
}
/**
@ -634,7 +289,7 @@ void SetEffectsVolume(int volume)
*/
int GetEffectsVolume()
{
return EffectsVolume;
return Mix_Volume(-1, -1);
}
/**
@ -662,7 +317,7 @@ bool IsEffectsEnabled()
*/
void SetMusicFinishedCallback(void (*callback)())
{
MusicChannel.FinishedCallback = callback;
Mix_HookMusicFinished(callback);
}
/**
@ -672,12 +327,10 @@ void SetMusicFinishedCallback(void (*callback)())
**
** @return 0 if music is playing, -1 if not.
*/
int PlayMusic(CSample *sample)
int PlayMusic(Mix_Music *sample)
{
if (sample) {
StopMusic();
MusicChannel.Sample = sample;
MusicPlaying = true;
Mix_PlayMusic(sample, 0);
return 0;
} else {
DebugPrint("Could not play sample\n");
@ -697,14 +350,11 @@ int PlayMusic(const std::string &file)
if (!SoundEnabled() || !IsMusicEnabled()) {
return -1;
}
const std::string name = LibraryFileName(file.c_str());
DebugPrint("play music %s\n" _C_ name.c_str());
CSample *sample = LoadSample(name.c_str(), PlayAudioStream);
DebugPrint("play music %s\n" _C_ file.c_str());
Mix_Music *music = LoadMusic(file);
if (sample) {
StopMusic();
MusicChannel.Sample = sample;
MusicPlaying = true;
if (music) {
Mix_FadeInMusic(music, 0, 200);
return 0;
} else {
DebugPrint("Could not play %s\n" _C_ file.c_str());
@ -717,15 +367,7 @@ int PlayMusic(const std::string &file)
*/
void StopMusic()
{
if (MusicPlaying) {
MusicPlaying = false;
if (MusicChannel.Sample) {
SDL_LockMutex(Audio.Lock);
delete MusicChannel.Sample;
MusicChannel.Sample = NULL;
SDL_UnlockMutex(Audio.Lock);
}
}
Mix_FadeOutMusic(200);
}
/**
@ -735,8 +377,9 @@ void StopMusic()
*/
void SetMusicVolume(int volume)
{
clamp(&volume, 0, MaxVolume);
MusicVolume = volume;
// due to left-right separation, sound effect volume is effectively halfed,
// so we adjust the music
Mix_VolumeMusic(volume / 2);
}
/**
@ -744,7 +387,7 @@ void SetMusicVolume(int volume)
*/
int GetMusicVolume()
{
return MusicVolume;
return Mix_VolumeMusic(-1);
}
/**
@ -773,7 +416,7 @@ bool IsMusicEnabled()
*/
bool IsMusicPlaying()
{
return MusicPlaying;
return Mix_PlayingMusic();
}
/*----------------------------------------------------------------------------
@ -796,30 +439,13 @@ bool SoundEnabled()
**
** @return True if failure, false if everything ok.
*/
static int InitSdlSound(int freq, int size)
static int InitSdlSound()
{
SDL_AudioSpec wanted;
wanted.freq = freq;
if (size == 8) {
wanted.format = AUDIO_U8;
} else if (size == 16) {
wanted.format = AUDIO_S16SYS;
} else {
DebugPrint("Unexpected sample size %d\n" _C_ size);
wanted.format = AUDIO_S16SYS;
}
wanted.channels = 2;
wanted.samples = 4096;
wanted.callback = FillAudio;
wanted.userdata = NULL;
// Open the audio device, forcing the desired format
if (SDL_OpenAudio(&wanted, &Audio.Format) < 0) {
Mix_Init(MIX_INIT_MID | MIX_INIT_MOD | MIX_INIT_MP3 | MIX_INIT_OGG | MIX_INIT_FLAC | MIX_INIT_OPUS);
if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 4096)) {
fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
return -1;
}
SDL_PauseAudio(0);
return 0;
}
@ -833,30 +459,17 @@ int InitSound()
//
// Open sound device, 8bit samples, stereo.
//
if (InitSdlSound(44100, 16)) {
if (InitSdlSound()) {
SoundInitialized = false;
return 1;
}
SoundInitialized = true;
Mix_AllocateChannels(MaxChannels);
Mix_ChannelFinished(ChannelFinished);
// ARI: The following must be done here to allow sound to work in
// pre-start menus!
// initialize channels
for (int i = 0; i < MaxChannels; ++i) {
Channels[i].Point = i + 1;
}
// Create mutex and cond for FillThread
Audio.MixerBuffer = new int[Audio.Format.samples * Audio.Format.channels];
memset(Audio.MixerBuffer, 0, Audio.Format.samples * Audio.Format.channels * sizeof(int));
Audio.Buffer = new Uint8[Audio.Format.size];
memset(Audio.Buffer, 0, Audio.Format.size);
Audio.Lock = SDL_CreateMutex();
Audio.Cond = SDL_CreateCond();
Audio.Running = true;
// Create thread to fill sdl audio buffer
Audio.Thread = SDL_CreateThread(FillThread, "stratagus-snd", NULL);
// Now we're ready for the callback to run
Mix_ResumeMusic();
Mix_Resume(-1);
return 0;
}
@ -865,18 +478,9 @@ int InitSound()
*/
void QuitSound()
{
Audio.Running = false;
// Join with the FillThread
SDL_WaitThread(Audio.Thread, NULL);
Mix_CloseAudio();
Mix_Quit();
SoundInitialized = false;
delete[] Audio.MixerBuffer;
Audio.MixerBuffer = NULL;
delete[] Audio.Buffer;
Audio.Buffer = NULL;
#ifdef USE_FLUIDSYNTH
CleanFluidSynth();
#endif
}
//@}

View file

@ -1,394 +0,0 @@
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
/**@name wav.cpp - wav support */
//
// (c) Copyright 2003-2005 by Lutz Sammer, Fabrice Rossi and Nehal Mistry
//
// 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; only version 2 of the License.
//
// 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, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
//@{
/*----------------------------------------------------------------------------
-- Includes
----------------------------------------------------------------------------*/
#include "stratagus.h"
#include "SDL.h"
#include "SDL_endian.h"
#include "iolib.h"
#include "sound_server.h"
#include "wav.h"
/*----------------------------------------------------------------------------
-- Declaration
----------------------------------------------------------------------------*/
/**
** Private wav data structure to handle wav streaming.
*/
struct WavData {
CFile *WavFile; /// Wav file handle
int ChunkRem; /// Bytes remaining in chunk
};
class CSampleWav : public CSample
{
public:
~CSampleWav();
int Read(void *buf, int len);
WavData Data;
};
class CSampleWavStream : public CSample
{
public:
~CSampleWavStream();
int Read(void *buf, int len);
WavData Data;
};
/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/
static void swapEndianness(WavHeader *wavHeader)
{
wavHeader->MagicRiff = SDL_SwapLE32(wavHeader->MagicRiff);
wavHeader->Length = SDL_SwapLE32(wavHeader->Length);
wavHeader->MagicWave = SDL_SwapLE32(wavHeader->MagicWave);
}
static bool Check(const WavHeader &wavHeader)
{
if (wavHeader.MagicRiff != RIFF) {
return false;
}
if (wavHeader.MagicWave != WAVE) {
printf("Wrong magic %x (not %x)\n", wavHeader.MagicWave, WAVE);
return false;
}
return true;
}
static void swapEndianness(WavChunk *chunk)
{
chunk->Magic = SDL_SwapLE32(chunk->Magic);
chunk->Length = SDL_SwapLE32(chunk->Length);
}
static void swapEndianness(WavFMT *wavfmt)
{
wavfmt->Encoding = SDL_SwapLE16(wavfmt->Encoding);
wavfmt->Channels = SDL_SwapLE16(wavfmt->Channels);
wavfmt->Frequency = SDL_SwapLE32(wavfmt->Frequency);
wavfmt->ByteRate = SDL_SwapLE32(wavfmt->ByteRate);
wavfmt->SampleSize = SDL_SwapLE16(wavfmt->SampleSize);
wavfmt->BitsPerSample = SDL_SwapLE16(wavfmt->BitsPerSample);
}
static bool IsWavFormatSupported(const WavFMT &wavfmt)
{
if (wavfmt.Encoding != WAV_PCM_CODE) {
printf("Unsupported encoding %d\n", wavfmt.Encoding);
return false;
}
if (wavfmt.Channels != WAV_MONO && wavfmt.Channels != WAV_STEREO) {
printf("Unsupported channels %d\n", wavfmt.Channels);
return false;
}
if (wavfmt.SampleSize != 1 && wavfmt.SampleSize != 2 && wavfmt.SampleSize != 4) {
printf("Unsupported sample size %d\n", wavfmt.SampleSize);
return false;
}
if (wavfmt.BitsPerSample != 8 && wavfmt.BitsPerSample != 16) {
printf("Unsupported bits per sample %d\n", wavfmt.BitsPerSample);
return false;
}
Assert(wavfmt.Frequency == 44100 || wavfmt.Frequency == 22050 || wavfmt.Frequency == 11025);
return true;
}
int CSampleWavStream::Read(void *buf, int len)
{
WavChunk chunk;
unsigned char *sndbuf;
int comp; // number of compressed bytes actually read
int i;
int read;
int bufrem;
if (this->Pos > SOUND_BUFFER_SIZE / 2) {
memcpy(this->Buffer, this->Buffer + this->Pos, this->Len);
this->Pos = 0;
}
while (this->Len < SOUND_BUFFER_SIZE / 4) {
if (!this->Data.ChunkRem) {
// read next chunk
comp = this->Data.WavFile->read(&chunk, sizeof(chunk));
if (!comp) {
// EOF
this->Data.ChunkRem = 0;
break;
}
swapEndianness(&chunk);
if (chunk.Magic != DATA) {
this->Data.WavFile->seek(chunk.Length, SEEK_CUR);
continue;
}
this->Data.ChunkRem = chunk.Length;
}
bufrem = SOUND_BUFFER_SIZE - (this->Pos + this->Len);
read = std::min(bufrem, this->Data.ChunkRem);
this->Data.ChunkRem -= read;
sndbuf = this->Buffer + this->Pos + this->Len;
comp = this->Data.WavFile->read(sndbuf, read);
if (!comp) {
break;
}
read >>= 1;
for (i = 0; i < read; ++i) {
((unsigned short *)sndbuf)[i] = SDL_SwapLE16(((unsigned short *)sndbuf)[i]);
}
this->Len += comp;
}
len = std::min(this->Len, len);
memcpy(buf, this->Buffer + this->Pos, len);
this->Pos += len;
this->Len -= len;
return len;
}
CSampleWavStream::~CSampleWavStream()
{
this->Data.WavFile->close();
delete this->Data.WavFile;
delete[] this->Buffer;
}
int CSampleWav::Read(void *buf, int len)
{
len = std::min(this->Len, len);
memcpy(buf, this->Buffer + this->Pos, len);
this->Pos += len;
this->Len -= len;
return len;
}
CSampleWav::~CSampleWav()
{
delete[] this->Buffer;
}
/**
** Load wav.
**
** @param name File name.
** @param flags Load flags.
**
** @return Returns the loaded sample.
**
** @todo Add ADPCM loading support!
*/
CSample *LoadWav(const char *name, int flags)
{
CFile *f = new CFile;
if (f->open(name, CL_OPEN_READ) == -1) {
printf("Can't open file '%s'\n", name);
delete f;
return NULL;
}
WavHeader wavHeader;
if (f->read(&wavHeader, sizeof(wavHeader)) != sizeof(wavHeader)) {
f->close();
delete f;
return NULL;
}
// Convert to native format
swapEndianness(&wavHeader);
if (Check(wavHeader) == false) {
f->close();
delete f;
return NULL;
}
WavChunk chunk;
if (f->read(&chunk, sizeof(chunk)) != sizeof(chunk)) {
f->close();
delete f;
return NULL;
}
// Convert to native format
swapEndianness(&chunk);
while (chunk.Magic != FMT) {
printf("Discard wavChunk '%x'\n", chunk.Magic);
std::vector<char> buffer;
buffer.resize(chunk.Length);
if (f->read(&buffer[0], chunk.Length) != static_cast<int>(chunk.Length)
|| f->read(&chunk, sizeof(chunk)) != sizeof(chunk)) {
f->close();
delete f;
return NULL;
}
// Convert to native format
swapEndianness(&chunk);
}
if (chunk.Length < 16) {
printf("Wrong length %d (not %d)\n", chunk.Length, 16);
f->close();
delete f;
return NULL;
}
WavFMT wavfmt;
if (f->read(&wavfmt, sizeof(wavfmt)) != sizeof(wavfmt)) {
f->close();
delete f;
return NULL;
}
// Convert to native format
swapEndianness(&wavfmt);
if (chunk.Length != 16) {
std::vector<char> buffer;
const int extraSize = chunk.Length - 16;
buffer.resize(extraSize);
if (f->read(&buffer[0], extraSize) != extraSize) {
f->close();
delete f;
return NULL;
}
}
// Check if supported
if (IsWavFormatSupported(wavfmt) == false) {
f->close();
delete f;
return NULL;
}
CSample *sample;
WavData *data;
//
// Read sample
//
if (flags & PlayAudioStream) {
CSampleWavStream *sampleWavStream = new CSampleWavStream;
sample = sampleWavStream;
sampleWavStream->Data.WavFile = f;
data = &sampleWavStream->Data;
} else {
CSampleWav *sampleWav = new CSampleWav;
sample = sampleWav;
sampleWav->Data.WavFile = f;
data = &sampleWav->Data;
}
sample->Channels = wavfmt.Channels;
sample->SampleSize = wavfmt.SampleSize * 8 / sample->Channels;
sample->Frequency = wavfmt.Frequency;
sample->BitsPerSample = wavfmt.BitsPerSample;
sample->Len = 0;
sample->Pos = 0;
if (flags & PlayAudioStream) {
data->ChunkRem = 0;
sample->Buffer = new unsigned char[SOUND_BUFFER_SIZE];
} else {
char sndbuf[SOUND_BUFFER_SIZE];
sample->Buffer = NULL;
int read = 0;
int rem = 0;
while (1) {
if (!rem) {
// read next chunk
const int comp = f->read(&chunk, sizeof(chunk));
if (!comp) {
// EOF
break;
}
swapEndianness(&chunk);
if (chunk.Magic != DATA) {
f->seek(chunk.Length, SEEK_CUR);
continue;
}
rem = chunk.Length;
}
const int bufrem = SOUND_BUFFER_SIZE;
read = std::min(bufrem, rem);
rem -= read;
unsigned char *b = new unsigned char[sample->Len + read];
Assert(b);
memcpy(b, sample->Buffer, sample->Len);
delete[] sample->Buffer;
sample->Buffer = b;
const int comp = data->WavFile->read(sndbuf, read);
Assert(comp == read);
if (sample->SampleSize == 16) {
read >>= 1;
for (int i = 0; i < read; ++i) {
((unsigned short *)(sample->Buffer + sample->Pos + sample->Len))[i] =
SDL_SwapLE16(((unsigned short *)sndbuf)[i]);
}
} else {
memcpy((sample->Buffer + sample->Pos + sample->Len), sndbuf, comp);
}
sample->Len += comp;
}
data->WavFile->close();
delete data->WavFile;
}
return sample;
}
//@}

View file

@ -44,6 +44,8 @@
#include "parameters.h"
#include "util.h"
#include "SDL.h"
#include <stdarg.h>
#include <stdio.h>
@ -191,6 +193,41 @@ int CFile::printf(const char *format, ...)
return ret;
}
static Sint64 sdl_size(SDL_RWops * context) {
return -1;
}
static Sint64 sdl_seek(SDL_RWops * context, Sint64 offset, int whence) {
CFile *self = reinterpret_cast<CFile*>(context->hidden.unknown.data1);
return self->seek(offset, whence);
}
static size_t sdl_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum) {
CFile *self = reinterpret_cast<CFile*>(context->hidden.unknown.data1);
return self->read(ptr, size * maxnum) / size;
}
static size_t sdl_write(SDL_RWops * context, const void *ptr, size_t size, size_t num) {
return 0;
}
static int sdl_close(SDL_RWops * context) {
return -1;
}
SDL_RWops * CFile::as_SDL_RWops()
{
SDL_RWops *ops = (SDL_RWops *) calloc(1, sizeof(SDL_RWops));
ops->type = SDL_RWOPS_UNKNOWN;
ops->hidden.unknown.data1 = this;
ops->size = sdl_size;
ops->seek = sdl_seek;
ops->read = sdl_read;
ops->write = sdl_write;
return ops;
}
//
// Implementation.
//

View file

@ -762,11 +762,6 @@ int stratagusMain(int argc, char **argv)
// Initialise AI module
InitAiModule();
LoadCcl(parameters.luaStartFilename, parameters.luaScriptArguments);
PrintHeader();
PrintLicense();
// Setup video display
InitVideo();
@ -775,6 +770,11 @@ int stratagusMain(int argc, char **argv)
InitMusic();
}
LoadCcl(parameters.luaStartFilename, parameters.luaScriptArguments);
PrintHeader();
PrintLicense();
#ifndef DEBUG // For debug it's better not to have:
srand(time(NULL)); // Random counter = random each start
#endif

View file

@ -20,6 +20,6 @@ extern void StopMusic(void);
extern bool IsMusicPlaying();
extern int SetChannelVolume(int channel, int volume);
extern int SetChannelStereo(int channel, int stereo);
extern void SetChannelStereo(int channel, int stereo);
extern void StopChannel(int channel);
extern void StopAllChannels();

View file

@ -242,16 +242,8 @@ int PlayMovie(const std::string &name)
}
StopMusic();
CSample *sample = LoadVorbis(filename.c_str(), PlayAudioStream);
Mix_Music *sample = LoadMusic(filename);
if (sample) {
if ((sample->Channels != 1 && sample->Channels != 2) || sample->SampleSize != 16) {
fprintf(stderr, "Unsupported sound format in movie\n");
delete sample;
SDL_DestroyTexture(yuv_overlay);
OggFree(&data);
f.close();
return 0;
}
PlayMusic(sample);
}