use SDL_mixer to handle all our sound needs, and delete all the custom code
This commit is contained in:
parent
d3e33a8840
commit
6b73fd7b97
15 changed files with 169 additions and 2052 deletions
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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
|
||||
|
||||
//@}
|
|
@ -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
|
||||
|
||||
//@}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
//@}
|
|
@ -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]);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
//@}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
//@}
|
|
@ -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.
|
||||
//
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue