mirror of https://github.com/OpenRCT2/OpenRCT2.git
refactor mixer, add support for streamed music
This commit is contained in:
parent
3b416a646e
commit
71e21c2a6c
|
@ -1555,7 +1555,7 @@ void start_title_music()
|
|||
&& RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TITLE_DEMO) {
|
||||
if (!RCT2_GLOBAL(0x009AF600, uint8)) {
|
||||
#ifdef USE_MIXER
|
||||
gTitleMusicChannel = Mixer_Play_Music(musicPathId);
|
||||
gTitleMusicChannel = Mixer_Play_Music(musicPathId, true);
|
||||
#else
|
||||
RCT2_GLOBAL(0x014241BC, uint32) = 1;
|
||||
int result = sound_channel_load_file2(3, (char*)get_file_path(musicPathId), 0);
|
||||
|
|
|
@ -31,21 +31,64 @@ extern "C" {
|
|||
|
||||
Mixer gMixer;
|
||||
|
||||
Sample::Sample()
|
||||
Source::~Source()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
unsigned long Source::GetSome(unsigned long offset, const uint8** data, unsigned long length)
|
||||
{
|
||||
if (offset >= Length()) {
|
||||
return 0;
|
||||
}
|
||||
unsigned long size = length;
|
||||
if (offset + length > Length()) {
|
||||
size = Length() - offset;
|
||||
}
|
||||
return Read(offset, data, size);
|
||||
}
|
||||
|
||||
unsigned long Source::Length()
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
const AudioFormat& Source::Format()
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
Source_Null::Source_Null()
|
||||
{
|
||||
length = 0;
|
||||
}
|
||||
|
||||
unsigned long Source_Null::Read(unsigned long offset, const uint8** data, unsigned long length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Source_Sample::Source_Sample()
|
||||
{
|
||||
data = 0;
|
||||
length = 0;
|
||||
issdlwav = false;
|
||||
}
|
||||
|
||||
Sample::~Sample()
|
||||
Source_Sample::~Source_Sample()
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
|
||||
bool Sample::Load(const char* filename)
|
||||
unsigned long Source_Sample::Read(unsigned long offset, const uint8** data, unsigned long length)
|
||||
{
|
||||
log_verbose("Sample::Load(%s)", filename);
|
||||
*data = &Source_Sample::data[offset];
|
||||
return length;
|
||||
}
|
||||
|
||||
bool Source_Sample::LoadWAV(const char* filename)
|
||||
{
|
||||
log_verbose("Source_Sample::LoadWAV(%s)", filename);
|
||||
|
||||
utf8 utf8filename[512];
|
||||
win1252_to_utf8(utf8filename, filename, sizeof(utf8filename));
|
||||
|
@ -75,9 +118,9 @@ bool Sample::Load(const char* filename)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Sample::LoadCSS1(const char* filename, unsigned int offset)
|
||||
bool Source_Sample::LoadCSS1(const char* filename, unsigned int offset)
|
||||
{
|
||||
log_verbose("Sample::LoadCSS1(%s, %d)", filename, offset);
|
||||
log_verbose("Source_Sample::LoadCSS1(%s, %d)", filename, offset);
|
||||
|
||||
utf8 utf8filename[512];
|
||||
win1252_to_utf8(utf8filename, filename, sizeof(utf8filename));
|
||||
|
@ -107,13 +150,18 @@ bool Sample::LoadCSS1(const char* filename, unsigned int offset)
|
|||
format.freq = waveformat.nSamplesPerSec;
|
||||
format.format = AUDIO_S16LSB;
|
||||
format.channels = waveformat.nChannels;
|
||||
data = new uint8[length];
|
||||
data = new (std::nothrow) uint8[length];
|
||||
if (!data) {
|
||||
log_verbose("Unable to allocate data");
|
||||
SDL_RWclose(rw);
|
||||
return false;
|
||||
}
|
||||
SDL_RWread(rw, data, length, 1);
|
||||
SDL_RWclose(rw);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sample::Unload()
|
||||
void Source_Sample::Unload()
|
||||
{
|
||||
if (data) {
|
||||
if (issdlwav) {
|
||||
|
@ -127,16 +175,11 @@ void Sample::Unload()
|
|||
length = 0;
|
||||
}
|
||||
|
||||
bool Sample::Loaded()
|
||||
bool Source_Sample::Convert(AudioFormat format)
|
||||
{
|
||||
return data != 0;
|
||||
}
|
||||
|
||||
bool Sample::Convert(AudioFormat format)
|
||||
{
|
||||
if(Sample::format.format != format.format || Sample::format.channels != format.channels || Sample::format.freq != format.freq){
|
||||
if(Source_Sample::format.format != format.format || Source_Sample::format.channels != format.channels || Source_Sample::format.freq != format.freq){
|
||||
SDL_AudioCVT cvt;
|
||||
if (SDL_BuildAudioCVT(&cvt, Sample::format.format, Sample::format.channels, Sample::format.freq, format.format, format.channels, format.freq) < 0) {
|
||||
if (SDL_BuildAudioCVT(&cvt, Source_Sample::format.format, Source_Sample::format.channels, Source_Sample::format.freq, format.format, format.channels, format.freq) < 0) {
|
||||
return false;
|
||||
}
|
||||
cvt.len = length;
|
||||
|
@ -149,69 +192,147 @@ bool Sample::Convert(AudioFormat format)
|
|||
Unload();
|
||||
data = cvt.buf;
|
||||
length = cvt.len_cvt;
|
||||
Sample::format = format;
|
||||
Source_Sample::format = format;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8* Sample::Data()
|
||||
Source_SampleStream::Source_SampleStream()
|
||||
{
|
||||
return data;
|
||||
length = 0;
|
||||
rw = NULL;
|
||||
buffer = 0;
|
||||
buffersize = 0;
|
||||
}
|
||||
|
||||
unsigned long Sample::Length()
|
||||
Source_SampleStream::~Source_SampleStream()
|
||||
{
|
||||
return length;
|
||||
Unload();
|
||||
}
|
||||
|
||||
Stream::Stream()
|
||||
unsigned long Source_SampleStream::Read(unsigned long offset, const uint8** data, unsigned long length)
|
||||
{
|
||||
sourcetype = SOURCE_NONE;
|
||||
if (length > buffersize) {
|
||||
if (buffer) {
|
||||
delete[] buffer;
|
||||
}
|
||||
buffer = new (std::nothrow) uint8[length];
|
||||
if (!buffer) {
|
||||
return 0;
|
||||
}
|
||||
buffersize = length;
|
||||
}
|
||||
Sint64 currentposition = SDL_RWtell(rw);
|
||||
if (currentposition == -1) {
|
||||
return 0;
|
||||
}
|
||||
if (currentposition - databegin != offset) {
|
||||
Sint64 newposition = SDL_RWseek(rw, databegin + offset, SEEK_SET);
|
||||
if (newposition == -1) {
|
||||
return 0;
|
||||
}
|
||||
currentposition = newposition;
|
||||
}
|
||||
*data = buffer;
|
||||
int read = SDL_RWread(rw, buffer, 1, length);
|
||||
if (read == -1) {
|
||||
return 0;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
unsigned long Stream::GetSome(unsigned long offset, const uint8** data, unsigned long length)
|
||||
bool Source_SampleStream::LoadWAV(SDL_RWops* rw)
|
||||
{
|
||||
unsigned long size = length;
|
||||
switch(sourcetype) {
|
||||
case SOURCE_SAMPLE:
|
||||
if (offset >= sample->Length()) {
|
||||
return 0;
|
||||
}
|
||||
if (offset + length > sample->Length()) {
|
||||
size = sample->Length() - offset;
|
||||
}
|
||||
*data = &sample->Data()[offset];
|
||||
return size;
|
||||
break;
|
||||
Unload();
|
||||
if (rw == NULL) {
|
||||
return false;
|
||||
}
|
||||
Source_SampleStream::rw = rw;
|
||||
Uint32 chunk_id = SDL_ReadLE32(rw);
|
||||
const Uint32 RIFF = 0x46464952;
|
||||
if (chunk_id != RIFF) {
|
||||
log_verbose("Not a WAV file");
|
||||
return false;
|
||||
}
|
||||
Uint32 chunk_size = SDL_ReadLE32(rw);
|
||||
Uint32 chunk_format = SDL_ReadLE32(rw);
|
||||
const Uint32 WAVE = 0x45564157;
|
||||
if (chunk_format != WAVE) {
|
||||
log_verbose("Not in WAVE format");
|
||||
return false;
|
||||
}
|
||||
const Uint32 FMT = 0x20746D66;
|
||||
Uint32 fmtchunk_size = FindChunk(rw, FMT);
|
||||
if (!fmtchunk_size) {
|
||||
log_verbose("Could not find FMT chunk");
|
||||
return false;
|
||||
}
|
||||
PCMWAVEFORMAT waveformat;
|
||||
SDL_RWread(rw, &waveformat, sizeof(waveformat), 1);
|
||||
if (waveformat.wf.wFormatTag != WAVE_FORMAT_PCM) {
|
||||
log_verbose("Not in proper format");
|
||||
return false;
|
||||
}
|
||||
format.freq = waveformat.wf.nSamplesPerSec;
|
||||
switch (waveformat.wBitsPerSample) {
|
||||
case 8:
|
||||
format.format = AUDIO_U8;
|
||||
break;
|
||||
case 16:
|
||||
format.format = AUDIO_S16LSB;
|
||||
break;
|
||||
default:
|
||||
log_verbose("Invalid bits per sample");
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
format.channels = waveformat.wf.nChannels;
|
||||
const Uint32 DATA = 0x61746164;
|
||||
Uint32 datachunk_size = FindChunk(rw, DATA);
|
||||
if (!datachunk_size) {
|
||||
log_verbose("Could not find DATA chunk");
|
||||
return false;
|
||||
}
|
||||
length = datachunk_size;
|
||||
databegin = SDL_RWtell(rw);
|
||||
return true;
|
||||
}
|
||||
|
||||
Uint32 Source_SampleStream::FindChunk(SDL_RWops* rw, Uint32 wanted_id)
|
||||
{
|
||||
Uint32 subchunk_id = SDL_ReadLE32(rw);
|
||||
Uint32 subchunk_size = SDL_ReadLE32(rw);
|
||||
if (subchunk_id == wanted_id) {
|
||||
return subchunk_size;
|
||||
}
|
||||
const Uint32 FACT = 0x74636166;
|
||||
const Uint32 LIST = 0x5453494c;
|
||||
const Uint32 BEXT = 0x74786562;
|
||||
const Uint32 JUNK = 0x4B4E554A;
|
||||
while (subchunk_id == FACT || subchunk_id == LIST || subchunk_id == BEXT || subchunk_id == JUNK) {
|
||||
SDL_RWseek(rw, subchunk_size, RW_SEEK_CUR);
|
||||
subchunk_id = SDL_ReadLE32(rw);
|
||||
subchunk_size = SDL_ReadLE32(rw);
|
||||
if (subchunk_id == wanted_id) {
|
||||
return subchunk_size;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long Stream::Length()
|
||||
void Source_SampleStream::Unload()
|
||||
{
|
||||
switch(sourcetype) {
|
||||
case SOURCE_SAMPLE:
|
||||
return sample->Length();
|
||||
break;
|
||||
if (rw) {
|
||||
SDL_RWclose(rw);
|
||||
rw = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Stream::SetSource_Sample(Sample& sample)
|
||||
{
|
||||
sourcetype = SOURCE_SAMPLE;
|
||||
Stream::sample = &sample;
|
||||
}
|
||||
|
||||
const AudioFormat* Stream::Format()
|
||||
{
|
||||
switch(sourcetype) {
|
||||
case SOURCE_SAMPLE:
|
||||
return &sample->format;
|
||||
break;
|
||||
length = 0;
|
||||
if (buffer) {
|
||||
delete[] buffer;
|
||||
buffer = 0;
|
||||
}
|
||||
return 0;
|
||||
buffersize = 0;
|
||||
}
|
||||
|
||||
Channel::Channel()
|
||||
|
@ -223,7 +344,8 @@ Channel::Channel()
|
|||
SetPan(0.5f);
|
||||
done = true;
|
||||
stopping = false;
|
||||
stream = 0;
|
||||
source = 0;
|
||||
deletesourceondone = false;
|
||||
}
|
||||
|
||||
Channel::~Channel()
|
||||
|
@ -232,11 +354,14 @@ Channel::~Channel()
|
|||
speex_resampler_destroy(resampler);
|
||||
resampler = 0;
|
||||
}
|
||||
if (deletesourceondone) {
|
||||
delete source;
|
||||
}
|
||||
}
|
||||
|
||||
void Channel::Play(Stream& stream, int loop = MIXER_LOOP_NONE)
|
||||
void Channel::Play(Source& source, int loop = MIXER_LOOP_NONE)
|
||||
{
|
||||
Channel::stream = &stream;
|
||||
Channel::source = &source;
|
||||
Channel::loop = loop;
|
||||
offset = 0;
|
||||
done = false;
|
||||
|
@ -286,14 +411,25 @@ unsigned long Channel::GetOffset()
|
|||
|
||||
bool Channel::SetOffset(unsigned long offset)
|
||||
{
|
||||
if (stream && offset < stream->Length()) {
|
||||
int samplesize = stream->Format()->channels * stream->Format()->BytesPerSample();
|
||||
if (source && offset < source->Length()) {
|
||||
int samplesize = source->Format().channels * source->Format().BytesPerSample();
|
||||
Channel::offset = (offset / samplesize) * samplesize;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Mixer::Mixer()
|
||||
{
|
||||
effectbuffer = 0;
|
||||
for (int i = 0; i < countof(css1sources); i++) {
|
||||
css1sources[i] = 0;
|
||||
}
|
||||
for (int i = 0; i < countof(musicsources); i++) {
|
||||
musicsources[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Mixer::Init(const char* device)
|
||||
{
|
||||
Close();
|
||||
|
@ -310,10 +446,15 @@ void Mixer::Init(const char* device)
|
|||
format.channels = have.channels;
|
||||
format.freq = have.freq;
|
||||
const char* filename = get_file_path(PATH_ID_CSS1);
|
||||
for (int i = 0; i < SOUND_MAXID; i++) {
|
||||
css1samples[i].LoadCSS1(filename, i);
|
||||
css1samples[i].Convert(format); // convert to audio output format, saves some cpu usage but requires a bit more memory, optional
|
||||
css1streams[i].SetSource_Sample(css1samples[i]);
|
||||
for (int i = 0; i < countof(css1sources); i++) {
|
||||
Source_Sample* source_sample = new Source_Sample;
|
||||
if (source_sample->LoadCSS1(filename, i)) {
|
||||
source_sample->Convert(format); // convert to audio output format, saves some cpu usage but requires a bit more memory, optional
|
||||
css1sources[i] = source_sample;
|
||||
} else {
|
||||
css1sources[i] = &source_null;
|
||||
delete source_sample;
|
||||
}
|
||||
}
|
||||
effectbuffer = new uint8[(have.samples * format.BytesPerSample() * format.channels)];
|
||||
SDL_PauseAudioDevice(deviceid, 0);
|
||||
|
@ -328,7 +469,22 @@ void Mixer::Close()
|
|||
}
|
||||
Unlock();
|
||||
SDL_CloseAudioDevice(deviceid);
|
||||
delete[] effectbuffer;
|
||||
for (int i = 0; i < countof(css1sources); i++) {
|
||||
if (css1sources[i] && css1sources[i] != &source_null) {
|
||||
delete css1sources[i];
|
||||
css1sources[i] = 0;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < countof(musicsources); i++) {
|
||||
if (musicsources[i] && musicsources[i] != &source_null) {
|
||||
delete musicsources[i];
|
||||
musicsources[i] = 0;
|
||||
}
|
||||
}
|
||||
if (effectbuffer) {
|
||||
delete[] effectbuffer;
|
||||
effectbuffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Mixer::Lock()
|
||||
|
@ -341,13 +497,14 @@ void Mixer::Unlock()
|
|||
SDL_UnlockAudioDevice(deviceid);
|
||||
}
|
||||
|
||||
Channel* Mixer::Play(Stream& stream, int loop, bool deleteondone)
|
||||
Channel* Mixer::Play(Source& source, int loop, bool deleteondone, bool deletesourceondone)
|
||||
{
|
||||
Lock();
|
||||
Channel* newchannel = new (std::nothrow) Channel();
|
||||
Channel* newchannel = new (std::nothrow) Channel;
|
||||
if (newchannel) {
|
||||
newchannel->Play(stream, loop);
|
||||
newchannel->Play(source, loop);
|
||||
newchannel->deleteondone = deleteondone;
|
||||
newchannel->deletesourceondone = deletesourceondone;
|
||||
newchannel->stopping = false;
|
||||
channels.push_back(newchannel);
|
||||
}
|
||||
|
@ -364,13 +521,20 @@ void Mixer::Stop(Channel& channel)
|
|||
|
||||
bool Mixer::LoadMusic(int pathid)
|
||||
{
|
||||
if (pathid >= PATH_ID_END) {
|
||||
if (pathid >= countof(musicsources)) {
|
||||
return false;
|
||||
}
|
||||
if (!musicsamples[pathid].Loaded()) {
|
||||
if (!musicsources[pathid]) {
|
||||
const char* filename = get_file_path(pathid);
|
||||
musicstreams[pathid].SetSource_Sample(musicsamples[pathid]);
|
||||
return musicsamples[pathid].Load(filename);
|
||||
Source_Sample* source_sample = new Source_Sample;
|
||||
if (source_sample->LoadWAV(filename)) {
|
||||
musicsources[pathid] = source_sample;
|
||||
return true;
|
||||
} else {
|
||||
delete source_sample;
|
||||
musicsources[pathid] = &source_null;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
@ -394,8 +558,8 @@ void SDLCALL Mixer::Callback(void* arg, uint8* stream, int length)
|
|||
|
||||
void Mixer::MixChannel(Channel& channel, uint8* data, int length)
|
||||
{
|
||||
if (channel.stream && !channel.done) {
|
||||
AudioFormat streamformat = *channel.stream->Format();
|
||||
if (channel.source && channel.source->Length() > 0 && !channel.done) {
|
||||
AudioFormat streamformat = channel.source->Format();
|
||||
int loaded = 0;
|
||||
SDL_AudioCVT cvt;
|
||||
cvt.len_ratio = 1;
|
||||
|
@ -409,9 +573,9 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length)
|
|||
}
|
||||
int samplestoread = (int)((samples - samplesloaded) * rate);
|
||||
int lengthloaded = 0;
|
||||
if (channel.offset < channel.stream->Length()) {
|
||||
if (channel.offset < channel.source->Length()) {
|
||||
bool mustconvert = false;
|
||||
if (MustConvert(*channel.stream)) {
|
||||
if (MustConvert(*channel.source)) {
|
||||
if (SDL_BuildAudioCVT(&cvt, streamformat.format, streamformat.channels, streamformat.freq, Mixer::format.format, Mixer::format.channels, Mixer::format.freq) == -1) {
|
||||
break;
|
||||
}
|
||||
|
@ -420,7 +584,7 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length)
|
|||
|
||||
const uint8* datastream = 0;
|
||||
int toread = (int)(samplestoread / cvt.len_ratio) * samplesize;
|
||||
int readfromstream = (channel.stream->GetSome(channel.offset, &datastream, toread));
|
||||
int readfromstream = (channel.source->GetSome(channel.offset, &datastream, toread));
|
||||
if (readfromstream == 0) {
|
||||
break;
|
||||
}
|
||||
|
@ -519,7 +683,7 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length)
|
|||
|
||||
loaded += lengthloaded;
|
||||
|
||||
if (channel.loop != 0 && channel.offset >= channel.stream->Length()) {
|
||||
if (channel.loop != 0 && channel.offset >= channel.source->Length()) {
|
||||
if (channel.loop != -1) {
|
||||
channel.loop--;
|
||||
}
|
||||
|
@ -528,7 +692,7 @@ void Mixer::MixChannel(Channel& channel, uint8* data, int length)
|
|||
} while(loaded < length && channel.loop != 0 && !channel.stopping);
|
||||
|
||||
channel.oldvolume = channel.volume;
|
||||
if (channel.loop == 0 && channel.offset >= channel.stream->Length()) {
|
||||
if (channel.loop == 0 && channel.offset >= channel.source->Length()) {
|
||||
channel.done = true;
|
||||
}
|
||||
}
|
||||
|
@ -574,13 +738,10 @@ void Mixer::EffectFadeU8(uint8* data, int length, int startvolume, int endvolume
|
|||
}
|
||||
}
|
||||
|
||||
bool Mixer::MustConvert(Stream& stream)
|
||||
bool Mixer::MustConvert(Source& source)
|
||||
{
|
||||
const AudioFormat* streamformat = stream.Format();
|
||||
if (!streamformat) {
|
||||
return false;
|
||||
}
|
||||
if (streamformat->format != format.format || streamformat->channels != format.channels || streamformat->freq != format.freq) {
|
||||
const AudioFormat sourceformat = source.Format();
|
||||
if (sourceformat.format != format.format || sourceformat.channels != format.channels || sourceformat.freq != format.freq) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -609,11 +770,11 @@ void Mixer_Init(const char* device)
|
|||
|
||||
void* Mixer_Play_Effect(int id, int loop, int volume, float pan, double rate, int deleteondone)
|
||||
{
|
||||
if (id >= SOUND_MAXID) {
|
||||
if (id >= countof(gMixer.css1sources)) {
|
||||
return 0;
|
||||
}
|
||||
gMixer.Lock();
|
||||
Channel* channel = gMixer.Play(gMixer.css1streams[id], loop, deleteondone != 0);
|
||||
Channel* channel = gMixer.Play(*gMixer.css1sources[id], loop, deleteondone != 0, false);
|
||||
if (channel) {
|
||||
channel->SetVolume(volume);
|
||||
channel->SetPan(pan);
|
||||
|
@ -664,10 +825,33 @@ int Mixer_Channel_SetOffset(void* channel, unsigned long offset)
|
|||
return ((Channel*)channel)->SetOffset(offset);
|
||||
}
|
||||
|
||||
void* Mixer_Play_Music(int pathid)
|
||||
void* Mixer_Play_Music(int pathid, int streaming)
|
||||
{
|
||||
if (gMixer.LoadMusic(pathid)) {
|
||||
return gMixer.Play(gMixer.musicstreams[pathid], MIXER_LOOP_INFINITE, false);
|
||||
if (streaming) {
|
||||
const char* filename = get_file_path(pathid);
|
||||
|
||||
utf8 utf8filename[512];
|
||||
win1252_to_utf8(utf8filename, filename, sizeof(utf8filename));
|
||||
|
||||
SDL_RWops* rw = SDL_RWFromFile(utf8filename, "rb");
|
||||
if (rw == NULL) {
|
||||
return 0;
|
||||
}
|
||||
Source_SampleStream* source_samplestream = new Source_SampleStream;
|
||||
if (source_samplestream->LoadWAV(rw)) {
|
||||
Channel* channel = gMixer.Play(*source_samplestream, MIXER_LOOP_INFINITE, false, true);
|
||||
if (!channel) {
|
||||
delete source_samplestream;
|
||||
}
|
||||
return channel;
|
||||
} else {
|
||||
delete source_samplestream;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (gMixer.LoadMusic(pathid)) {
|
||||
return gMixer.Play(*gMixer.musicsources[pathid], MIXER_LOOP_INFINITE, false, false);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -43,45 +43,73 @@ struct AudioFormat {
|
|||
int channels;
|
||||
};
|
||||
|
||||
class Sample
|
||||
class Source
|
||||
{
|
||||
public:
|
||||
Sample();
|
||||
~Sample();
|
||||
bool Load(const char* filename);
|
||||
bool LoadCSS1(const char* filename, unsigned int offset);
|
||||
void Unload();
|
||||
bool Loaded();
|
||||
bool Convert(AudioFormat format);
|
||||
const uint8* Data();
|
||||
unsigned long Length();
|
||||
|
||||
friend class Stream;
|
||||
|
||||
private:
|
||||
AudioFormat format;
|
||||
uint8* data;
|
||||
unsigned long length;
|
||||
bool issdlwav;
|
||||
};
|
||||
|
||||
class Stream
|
||||
{
|
||||
public:
|
||||
Stream();
|
||||
virtual ~Source();
|
||||
unsigned long GetSome(unsigned long offset, const uint8** data, unsigned long length);
|
||||
unsigned long Length();
|
||||
void SetSource_Sample(Sample& sample);
|
||||
const AudioFormat* Format();
|
||||
const AudioFormat& Format();
|
||||
|
||||
friend class Mixer;
|
||||
|
||||
protected:
|
||||
virtual unsigned long Read(unsigned long offset, const uint8** data, unsigned long length) = 0;
|
||||
|
||||
AudioFormat format;
|
||||
unsigned long length;
|
||||
};
|
||||
|
||||
class Source_Null : public Source
|
||||
{
|
||||
public:
|
||||
Source_Null();
|
||||
|
||||
protected:
|
||||
unsigned long Read(unsigned long offset, const uint8** data, unsigned long length);
|
||||
};
|
||||
|
||||
class Source_Sample : public Source
|
||||
{
|
||||
public:
|
||||
Source_Sample();
|
||||
~Source_Sample();
|
||||
bool LoadWAV(const char* filename);
|
||||
bool LoadCSS1(const char* filename, unsigned int offset);
|
||||
|
||||
friend class Mixer;
|
||||
|
||||
protected:
|
||||
bool Convert(AudioFormat format);
|
||||
|
||||
private:
|
||||
enum {
|
||||
SOURCE_NONE = 0,
|
||||
SOURCE_SAMPLE
|
||||
} sourcetype;
|
||||
Sample* sample;
|
||||
void Unload();
|
||||
|
||||
uint8* data;
|
||||
bool issdlwav;
|
||||
|
||||
protected:
|
||||
unsigned long Read(unsigned long offset, const uint8** data, unsigned long length);
|
||||
};
|
||||
|
||||
class Source_SampleStream : public Source
|
||||
{
|
||||
public:
|
||||
Source_SampleStream();
|
||||
~Source_SampleStream();
|
||||
bool LoadWAV(SDL_RWops* rw);
|
||||
|
||||
private:
|
||||
Uint32 FindChunk(SDL_RWops* rw, Uint32 wanted_id);
|
||||
void Unload();
|
||||
|
||||
SDL_RWops* rw;
|
||||
Uint64 databegin;
|
||||
uint8* buffer;
|
||||
unsigned long buffersize;
|
||||
|
||||
protected:
|
||||
unsigned long Read(unsigned long offset, const uint8** data, unsigned long length);
|
||||
};
|
||||
|
||||
class Channel
|
||||
|
@ -89,7 +117,7 @@ class Channel
|
|||
public:
|
||||
Channel();
|
||||
~Channel();
|
||||
void Play(Stream& stream, int loop);
|
||||
void Play(Source& source, int loop);
|
||||
void SetRate(double rate);
|
||||
void SetVolume(int volume);
|
||||
void SetPan(float pan);
|
||||
|
@ -108,25 +136,27 @@ private:
|
|||
float pan;
|
||||
bool done;
|
||||
bool deleteondone;
|
||||
bool deletesourceondone;
|
||||
bool stopping;
|
||||
int oldvolume;
|
||||
SpeexResamplerState* resampler;
|
||||
Stream* stream;
|
||||
Source* source;
|
||||
};
|
||||
|
||||
class Mixer
|
||||
{
|
||||
public:
|
||||
Mixer();
|
||||
void Init(const char* device);
|
||||
void Close();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
Channel* Play(Stream& stream, int loop, bool deleteondone);
|
||||
Channel* Play(Source& source, int loop, bool deleteondone, bool deletesourceondone);
|
||||
void Stop(Channel& channel);
|
||||
bool LoadMusic(int pathid);
|
||||
|
||||
Stream css1streams[SOUND_MAXID];
|
||||
Stream musicstreams[PATH_ID_END];
|
||||
Source* css1sources[SOUND_MAXID];
|
||||
Source* musicsources[PATH_ID_END];
|
||||
|
||||
private:
|
||||
static void SDLCALL Callback(void* arg, uint8* data, int length);
|
||||
|
@ -135,14 +165,13 @@ private:
|
|||
void EffectPanU8(Channel& channel, uint8* data, int length);
|
||||
void EffectFadeS16(sint16* data, int length, int startvolume, int endvolume);
|
||||
void EffectFadeU8(uint8* data, int length, int startvolume, int endvolume);
|
||||
bool MustConvert(Stream& stream);
|
||||
bool MustConvert(Source& source);
|
||||
bool Convert(SDL_AudioCVT& cvt, const uint8* data, unsigned long length, uint8** dataout);
|
||||
SDL_AudioDeviceID deviceid;
|
||||
AudioFormat format;
|
||||
uint8* effectbuffer;
|
||||
Sample css1samples[SOUND_MAXID];
|
||||
Sample musicsamples[PATH_ID_END];
|
||||
std::list<Channel*> channels;
|
||||
Source_Null source_null;
|
||||
};
|
||||
|
||||
extern "C"
|
||||
|
@ -158,7 +187,7 @@ void Mixer_Channel_Rate(void* channel, double rate);
|
|||
int Mixer_Channel_IsPlaying(void* channel);
|
||||
unsigned long Mixer_Channel_GetOffset(void* channel);
|
||||
int Mixer_Channel_SetOffset(void* channel, unsigned long offset);
|
||||
void* Mixer_Play_Music(int pathid);
|
||||
void* Mixer_Play_Music(int pathid, int streaming);
|
||||
|
||||
static int DStoMixerVolume(int volume) { return (int)(SDL_MIX_MAXVOLUME * (SDL_pow(10, (float)volume / 2000))); };
|
||||
static float DStoMixerPan(int pan) { return (((float)pan + -DSBPAN_LEFT) / DSBPAN_RIGHT) / 2; };
|
||||
|
|
|
@ -192,6 +192,8 @@ enum {
|
|||
STR_LOCATE_SUBJECT_TIP = 1027,
|
||||
STR_OFF_EDGE_OF_MAP = 1028,
|
||||
|
||||
STR_CAN_ONLY_BUILD_THIS_ON_LAND = 1034,
|
||||
|
||||
STR_LOAD_GAME_DIALOG_TITLE = 1036,
|
||||
STR_LOAD_LANDSCAPE_DIALOG_TITLE = 1037,
|
||||
STR_CONVERT_SAVED_GAME_TO_SCENARIO_1038 = 1038,
|
||||
|
@ -378,6 +380,7 @@ enum {
|
|||
STR_SHOPS_STALLS_TIP = 1228,
|
||||
|
||||
STR_ROTATE_OBJECTS_90 = 1327,
|
||||
STR_LEVEL_LAND_REQUIRED = 1328,
|
||||
STR_LAUNCH_SPEED = 1329,
|
||||
STR_LAUNCH_SPEED_TIP = 1330,
|
||||
|
||||
|
|
|
@ -4577,7 +4577,7 @@ void peep_update_crowd_noise()
|
|||
// Load and play crowd noise
|
||||
#ifdef USE_MIXER
|
||||
if (!gCrowdSoundChannel) {
|
||||
gCrowdSoundChannel = Mixer_Play_Music(PATH_ID_CSS2);
|
||||
gCrowdSoundChannel = Mixer_Play_Music(PATH_ID_CSS2, false);
|
||||
}
|
||||
if (gCrowdSoundChannel) {
|
||||
Mixer_Channel_Volume(gCrowdSoundChannel, DStoMixerVolume(volume));
|
||||
|
|
|
@ -2662,7 +2662,7 @@ void ride_music_update_final()
|
|||
rct_ride_music_info* ride_music_info = &RCT2_GLOBAL(0x009AF1C8, rct_ride_music_info*)[ride_music_params->tuneid];
|
||||
#ifdef USE_MIXER
|
||||
rct_ride_music* ride_music = &gRideMusicList[ebx];
|
||||
ride_music->sound_channel = Mixer_Play_Music(ride_music_info->pathid);
|
||||
ride_music->sound_channel = Mixer_Play_Music(ride_music_info->pathid, true);
|
||||
if (ride_music->sound_channel) {
|
||||
ride_music->volume = ride_music_params->volume;
|
||||
ride_music->pan = ride_music_params->pan;
|
||||
|
@ -2678,7 +2678,7 @@ void ride_music_update_final()
|
|||
}
|
||||
Mixer_Channel_SetOffset(ride_music->sound_channel, offset);
|
||||
} else {
|
||||
RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_MUSIC, uint8) = 0;
|
||||
//RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_MUSIC, uint8) = 0;
|
||||
}
|
||||
#else
|
||||
const char* filename = get_file_path(ride_music_info->pathid);
|
||||
|
|
|
@ -1416,11 +1416,11 @@ static void window_top_toolbar_scenery_tool_down(short x, short y, rct_window* w
|
|||
|
||||
int cost;
|
||||
{
|
||||
int esi = 0, eax = grid_x, ecx = grid_y, edx = grid_z;
|
||||
game_do_command_p(GAME_COMMAND_PLACE_BANNER, &eax, &ebx, &ecx, &edx, &esi, &banner_id, &ebp);
|
||||
int esi = 0, eax = grid_x, ecx = grid_y, edx = parameter_2, ebx = (parameter_1 & 0xFF00) | 1;
|
||||
cost = game_do_command_p(GAME_COMMAND_PLACE_BANNER, &eax, &ebx, &ecx, &edx, &esi, &banner_id, &ebp);
|
||||
}
|
||||
|
||||
if (ebx == MONEY32_UNDEFINED)return;
|
||||
if (cost == MONEY32_UNDEFINED)return;
|
||||
|
||||
sound_play_panned(SOUND_PLACE_ITEM, 0x8001, RCT2_GLOBAL(0x009DEA5E, uint16), RCT2_GLOBAL(0x009DEA60, uint16), RCT2_GLOBAL(0x009DEA62, uint16));
|
||||
|
||||
|
|
|
@ -48,5 +48,6 @@ void footpath_provisional_remove();
|
|||
void footpath_provisional_update();
|
||||
void footpath_get_coordinates_from_pos(int screenX, int screenY, int *x, int *y, int *direction, rct_map_element **mapElement);
|
||||
void footpath_bridge_get_info_from_pos(int screenX, int screenY, int *x, int *y, int *direction, rct_map_element **mapElement);
|
||||
void sub_673883(int x, int y, int z);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "../scenario.h"
|
||||
#include "banner.h"
|
||||
#include "climate.h"
|
||||
#include "footpath.h"
|
||||
#include "map.h"
|
||||
#include "map_animation.h"
|
||||
#include "park.h"
|
||||
|
@ -905,7 +906,7 @@ void game_command_set_fence_colour(int* eax, int* ebx, int* ecx, int* edx, int*
|
|||
return;
|
||||
}
|
||||
if(*ebx & GAME_COMMAND_FLAG_APPLY){
|
||||
rct_scenery_entry* scenery_entry = RCT2_ADDRESS(RCT2_ADDRESS_WALL_SCENERY_ENTRIES, rct_scenery_entry*)[map_element->properties.fence.slope];
|
||||
rct_scenery_entry* scenery_entry = RCT2_ADDRESS(RCT2_ADDRESS_WALL_SCENERY_ENTRIES, rct_scenery_entry*)[map_element->properties.fence.type];
|
||||
map_element->properties.fence.item[1] &= 0xE0;
|
||||
map_element->properties.fence.item[1] |= color1;
|
||||
map_element->flags &= 0x9F;
|
||||
|
@ -1655,7 +1656,7 @@ void game_command_remove_fence(int* eax, int* ebx, int* ecx, int* edx, int* esi,
|
|||
return;
|
||||
}
|
||||
}
|
||||
rct_scenery_entry* scenery_entry = RCT2_ADDRESS(RCT2_ADDRESS_WALL_SCENERY_ENTRIES, rct_scenery_entry*)[map_element->properties.fence.slope];
|
||||
rct_scenery_entry* scenery_entry = RCT2_ADDRESS(RCT2_ADDRESS_WALL_SCENERY_ENTRIES, rct_scenery_entry*)[map_element->properties.fence.type];
|
||||
if(scenery_entry->wall.var_0D != 0xFF){
|
||||
rct_banner* banner = &gBanners[map_element->properties.fence.item[0]];
|
||||
if(banner->type != BANNER_NULL){
|
||||
|
|
|
@ -178,7 +178,7 @@ void scenery_remove_ghost_tool_placement(){
|
|||
105 | (RCT2_GLOBAL(0x00F64EC0, uint8) << 8),
|
||||
y,
|
||||
z,
|
||||
GAME_COMMAND_44,
|
||||
GAME_COMMAND_REMOVE_LARGE_SCENERY,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ void scenery_remove_ghost_tool_placement(){
|
|||
105,
|
||||
y,
|
||||
z | (RCT2_GLOBAL(0x00F64EC0, uint8) << 8),
|
||||
GAME_COMMAND_51,
|
||||
GAME_COMMAND_REMOVE_BANNER,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
|
|
|
@ -56,6 +56,12 @@ typedef enum {
|
|||
SMALL_SCENERY_FLAG18 = (1 << 17), // 0x20000
|
||||
SMALL_SCENERY_FLAG19 = (1 << 18), // 0x40000
|
||||
SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR = (1 << 19), // 0x80000
|
||||
SMALL_SCENERY_FLAG20 = (1 << 20), // 0x100000
|
||||
SMALL_SCENERY_FLAG21 = (1 << 21), // 0x200000
|
||||
SMALL_SCENERY_FLAG22 = (1 << 22), // 0x400000
|
||||
SMALL_SCENERY_FLAG23 = (1 << 23), // 0x800000
|
||||
SMALL_SCENERY_FLAG24 = (1 << 24), // 0x1000000
|
||||
SMALL_SCENERY_FLAG25 = (1 << 25), // 0x2000000
|
||||
} SMALL_SCENERY_FLAGS;
|
||||
|
||||
typedef struct {
|
||||
|
|
Loading…
Reference in New Issue