/* =========================================================================== ARX FATALIS GPL Source Code Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company. This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code'). Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Arx Fatalis Source Code 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 Arx Fatalis Source Code. If not, see . In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include #include #include "Athena_Global.h" #include "Athena_Sample.h" #include "Athena_Ambiance.h" #include "Athena_FileIO.h" #define _CRTDBG_MAP_ALLOC #include namespace ATHENA { static const aalULong AMBIANCE_FILE_SIGNATURE(0x424d4147); //'GAMB' static const aalULong AMBIANCE_FILE_VERSION_1000(0x01000000); static const aalULong AMBIANCE_FILE_VERSION_1001(0x01000001); static const aalULong AMBIANCE_FILE_VERSION_1002(0x01000002); static const aalULong AMBIANCE_FILE_VERSION_1003(0x01000003); static const aalULong AMBIANCE_FILE_VERSION(AMBIANCE_FILE_VERSION_1003); static const aalULong FADE_INTERVAL(50); static const aalULong KEY_CONTINUE(0xffffffff); enum aalTrackFlag { TRACK_3D = 0x00000001, TRACK_REVERB = 0x00000002, TRACK_MASTER = 0x00000004, TRACK_MUTED = 0x00000008, TRACK_PAUSED = 0x00000010, TRACK_PREFETCHED = 0x00000020 }; enum aalAmbianceFlag { IS_PLAYING = 0x00000001, IS_PAUSED = 0x00000002, IS_LOOPED = 0x00000004, IS_FADED_UP = 0x00000008, IS_FADED_DOWN = 0x00000010 }; static aalVoid FreeTrack(Track & track); static aalVoid ResetSetting(KeySetting & setting); static aalFloat UpdateSetting(KeySetting & setting, const aalSLong & timez = 0); static aalVoid UpdateKeySynch(TrackKey & key); static aalVoid KeyPlay(Track & track, TrackKey & key); static aalVoid OnAmbianceSampleStart(aalVoid * inst, const aalSLong &, aalVoid * data); static aalVoid OnAmbianceSampleStop(aalVoid *, const aalSLong &, aalVoid * data); /////////////////////////////////////////////////////////////////////////////// // // // Constructor and destructor // // // /////////////////////////////////////////////////////////////////////////////// Ambiance::Ambiance() : flags(0), start(0), time(0), track_c(0), track_l(NULL) { *name = 0; channel.flags = 0; } Ambiance::~Ambiance() { if (track_l) { Track * track = &track_l[track_c]; while (track > track_l) FreeTrack(*(--track)); free(track_l); } } static aalError LoadAmbianceFileVersion_1000(FILE * file, Ambiance & amb) { //Read tracks configs for (aalULong i(0); i < amb.track_c; ++i) { char text[256]; Track * track = &amb.track_l[i]; TrackKey * key; Sample * sample = NULL; aalULong j(0); track->s_id = AAL_SFALSE; //Get track sample name do { if (!FileRead(&text[j], 1, 1, file)) return AAL_ERROR_FILEIO; } while (text[j++]); sample = new Sample; if (sample->Load(text)) { delete sample; return AAL_ERROR_FILEIO; } if ((track->s_id = _sample.Add(sample)) == AAL_SFALSE) { delete sample; return AAL_ERROR_MEMORY; } sample->Catch(); track->key_c = 1; key = track->key_l = (TrackKey *)malloc(sizeof(TrackKey)); if (!track->key_l) return AAL_ERROR_MEMORY; memset(track->key_l, 0, sizeof(TrackKey)); if (!FileRead(&key->start, 4, 1, file) || !FileRead(&key->loop, 4, 1, file) || !FileRead(&key->delay_min, 4, 1, file) || !FileRead(&key->delay_max, 4, 1, file) || !FileRead(&key->volume.min, 4, 1, file) || !FileRead(&key->volume.max, 4, 1, file) || !FileRead(&key->volume.interval, 4, 1, file) || !FileRead(&key->volume.flags, 4, 1, file) || !FileRead(&key->pitch.min, 4, 1, file) || !FileRead(&key->pitch.max, 4, 1, file) || !FileRead(&key->pitch.interval, 4, 1, file) || !FileRead(&key->pitch.flags, 4, 1, file) || !FileRead(&key->pan.min, 4, 1, file) || !FileRead(&key->pan.max, 4, 1, file) || !FileRead(&key->pan.interval, 4, 1, file) || !FileRead(&key->pan.flags, 4, 1, file) || !FileRead(&key->x.min, 4, 1, file) || !FileRead(&key->x.max, 4, 1, file) || !FileRead(&key->x.interval, 4, 1, file) || !FileRead(&key->x.flags, 4, 1, file) || !FileRead(&key->y.min, 4, 1, file) || !FileRead(&key->y.max, 4, 1, file) || !FileRead(&key->y.interval, 4, 1, file) || !FileRead(&key->y.flags, 4, 1, file) || !FileRead(&key->z.min, 4, 1, file) || !FileRead(&key->z.max, 4, 1, file) || !FileRead(&key->z.interval, 4, 1, file) || !FileRead(&key->z.flags, 4, 1, file)) return AAL_ERROR_FILEIO; if (!FileRead(&track->flags, 4, 1, file)) return AAL_ERROR_FILEIO; track->flags &= ~(TRACK_MUTED | TRACK_PAUSED | TRACK_PREFETCHED); } return AAL_OK; } static aalError LoadAmbianceFileVersion_1001(FILE * file, Ambiance & amb) { for (aalULong i(0); i < amb.track_c; ++i) { char text[256]; Track * track = &amb.track_l[i]; Sample * sample = NULL; aalULong j(0); track->s_id = AAL_SFALSE; //Get track sample name do { if (!FileRead(&text[j], 1, 1, file)) return AAL_ERROR_FILEIO; } while (text[j++]); sample = new Sample; if (sample->Load(text)) { delete sample; return AAL_ERROR_FILEIO; } if ((track->s_id = _sample.Add(sample)) == AAL_SFALSE) { delete sample; return AAL_ERROR_MEMORY; } sample->Catch(); //Read flags and key count if (!FileRead(&track->flags, 4, 1, file) || !FileRead(&track->key_c, 4, 1, file)) return AAL_ERROR_FILEIO; track->flags &= ~(TRACK_MUTED | TRACK_PAUSED | TRACK_PREFETCHED); if (track->key_c) { track->key_l = (TrackKey *)malloc(track->key_c * sizeof(TrackKey)); if (!track->key_l) return AAL_ERROR_MEMORY; memset(track->key_l, 0, sizeof(TrackKey) * track->key_c); } //Read settings for each key for (j = 0; j < track->key_c; j++) { TrackKey * key = &track->key_l[j]; if (!FileRead(&key->flags, 4, 1, file) || !FileRead(&key->start, 4, 1, file) || !FileRead(&key->loop, 4, 1, file) || !FileRead(&key->delay_min, 4, 1, file) || !FileRead(&key->delay_max, 4, 1, file) || !FileRead(&key->volume.min, 4, 1, file) || !FileRead(&key->volume.max, 4, 1, file) || !FileRead(&key->volume.interval, 4, 1, file) || !FileRead(&key->volume.flags, 4, 1, file) || !FileRead(&key->pitch.min, 4, 1, file) || !FileRead(&key->pitch.max, 4, 1, file) || !FileRead(&key->pitch.interval, 4, 1, file) || !FileRead(&key->pitch.flags, 4, 1, file) || !FileRead(&key->pan.min, 4, 1, file) || !FileRead(&key->pan.max, 4, 1, file) || !FileRead(&key->pan.interval, 4, 1, file) || !FileRead(&key->pan.flags, 4, 1, file) || !FileRead(&key->x.min, 4, 1, file) || !FileRead(&key->x.max, 4, 1, file) || !FileRead(&key->x.interval, 4, 1, file) || !FileRead(&key->x.flags, 4, 1, file) || !FileRead(&key->y.min, 4, 1, file) || !FileRead(&key->y.max, 4, 1, file) || !FileRead(&key->y.interval, 4, 1, file) || !FileRead(&key->y.flags, 4, 1, file) || !FileRead(&key->z.min, 4, 1, file) || !FileRead(&key->z.max, 4, 1, file) || !FileRead(&key->z.interval, 4, 1, file) || !FileRead(&key->z.flags, 4, 1, file)) return AAL_ERROR_FILEIO; } } return AAL_OK; } static aalError LoadAmbianceFileVersion_1002(FILE * file, Ambiance & amb) { //Read tracks configs for (aalULong i(0); i < amb.track_c; i++) { char text[256]; Sample * sample = NULL; Track * track = &amb.track_l[i]; aalULong j(0); track->s_id = AAL_SFALSE; //Get track sample name do { if (!FileRead(&text[j], 1, 1, file)) return AAL_ERROR_FILEIO; } while (text[j++]); //Open sample sample = new Sample; if (sample->Load(text)) { delete sample; return AAL_ERROR_FILEIO; } if ((track->s_id = _sample.Add(sample)) == AAL_SFALSE) { delete sample; return AAL_ERROR_MEMORY; } sample->Catch(); //Get track name (!= sample name) j = 0; do { if (!FileRead(&text[j], 1, 1, file)) return AAL_ERROR_FILEIO; } while (text[j++]); if (j - 1) { track->name = (char *)malloc(j); if (!track->name) return AAL_ERROR_FILEIO; memcpy(track->name, text, j); } //Read flags and key count if (!FileRead(&track->flags, 4, 1, file) || !FileRead(&track->key_c, 4, 1, file)) return AAL_ERROR_FILEIO; track->flags &= ~(TRACK_MUTED | TRACK_PAUSED | TRACK_PREFETCHED); if (track->key_c) { track->key_l = (TrackKey *)malloc(track->key_c * sizeof(TrackKey)); if (!track->key_l) return AAL_ERROR_MEMORY; memset(track->key_l, 0, sizeof(TrackKey) * track->key_c); } //Read settings for each key for (j = 0; j < track->key_c; j++) { TrackKey * key = &track->key_l[j]; if (!FileRead(&key->flags, 4, 1, file) || !FileRead(&key->start, 4, 1, file) || !FileRead(&key->loop, 4, 1, file) || !FileRead(&key->delay_min, 4, 1, file) || !FileRead(&key->delay_max, 4, 1, file) || !FileRead(&key->volume.min, 4, 1, file) || !FileRead(&key->volume.max, 4, 1, file) || !FileRead(&key->volume.interval, 4, 1, file) || !FileRead(&key->volume.flags, 4, 1, file) || !FileRead(&key->pitch.min, 4, 1, file) || !FileRead(&key->pitch.max, 4, 1, file) || !FileRead(&key->pitch.interval, 4, 1, file) || !FileRead(&key->pitch.flags, 4, 1, file) || !FileRead(&key->pan.min, 4, 1, file) || !FileRead(&key->pan.max, 4, 1, file) || !FileRead(&key->pan.interval, 4, 1, file) || !FileRead(&key->pan.flags, 4, 1, file) || !FileRead(&key->x.min, 4, 1, file) || !FileRead(&key->x.max, 4, 1, file) || !FileRead(&key->x.interval, 4, 1, file) || !FileRead(&key->x.flags, 4, 1, file) || !FileRead(&key->y.min, 4, 1, file) || !FileRead(&key->y.max, 4, 1, file) || !FileRead(&key->y.interval, 4, 1, file) || !FileRead(&key->y.flags, 4, 1, file) || !FileRead(&key->z.min, 4, 1, file) || !FileRead(&key->z.max, 4, 1, file) || !FileRead(&key->z.interval, 4, 1, file) || !FileRead(&key->z.flags, 4, 1, file)) return AAL_ERROR_FILEIO; } } return AAL_OK; } static aalError LoadAmbianceFileVersion_1003(FILE * file, Ambiance & amb) { Track * track = &amb.track_l[amb.track_c]; //Read tracks configs while (track > amb.track_l) { char text[256]; Sample * sample = NULL; aalULong j(0); --track; track->s_id = AAL_SFALSE; //Get track sample name do { if (!FileRead(&text[j], 1, 1, file)) return AAL_ERROR_FILEIO; } while (text[j++]); //Open sample sample = new Sample; if (sample->Load(text)) { delete sample; return AAL_ERROR_FILEIO; } if ((track->s_id = _sample.Add(sample)) == AAL_SFALSE) { delete sample; return AAL_ERROR_MEMORY; } sample->Catch(); //Get track name (!= sample name) j = 0; do { if (!FileRead(&text[j], 1, 1, file)) return AAL_ERROR_FILEIO; } while (text[j++]); if (j - 1) { track->name = (char *)malloc(j); if (!track->name) return AAL_ERROR_FILEIO; memcpy(track->name, text, j); } //Read flags and key count if (!FileRead(&track->flags, 4, 1, file) || !FileRead(&track->key_c, 4, 1, file)) return AAL_ERROR_FILEIO; track->flags &= ~(TRACK_MUTED | TRACK_PAUSED | TRACK_PREFETCHED); if (track->key_c) { track->key_l = (TrackKey *)malloc(track->key_c * sizeof(TrackKey)); if (!track->key_l) return AAL_ERROR_MEMORY; memset(track->key_l, 0, sizeof(TrackKey) * track->key_c); } //Read settings for each key TrackKey * key = &track->key_l[track->key_c]; while (key > track->key_l) { --key; if (!FileRead(&key->flags, 4, 1, file) || !FileRead(&key->start, 4, 1, file) || !FileRead(&key->loop, 4, 1, file) || !FileRead(&key->delay_min, 4, 1, file) || !FileRead(&key->delay_max, 4, 1, file) || !FileRead(&key->volume.min, 4, 1, file) || !FileRead(&key->volume.max, 4, 1, file) || !FileRead(&key->volume.interval, 4, 1, file) || !FileRead(&key->volume.flags, 4, 1, file) || !FileRead(&key->pitch.min, 4, 1, file) || !FileRead(&key->pitch.max, 4, 1, file) || !FileRead(&key->pitch.interval, 4, 1, file) || !FileRead(&key->pitch.flags, 4, 1, file) || !FileRead(&key->pan.min, 4, 1, file) || !FileRead(&key->pan.max, 4, 1, file) || !FileRead(&key->pan.interval, 4, 1, file) || !FileRead(&key->pan.flags, 4, 1, file) || !FileRead(&key->x.min, 4, 1, file) || !FileRead(&key->x.max, 4, 1, file) || !FileRead(&key->x.interval, 4, 1, file) || !FileRead(&key->x.flags, 4, 1, file) || !FileRead(&key->y.min, 4, 1, file) || !FileRead(&key->y.max, 4, 1, file) || !FileRead(&key->y.interval, 4, 1, file) || !FileRead(&key->y.flags, 4, 1, file) || !FileRead(&key->z.min, 4, 1, file) || !FileRead(&key->z.max, 4, 1, file) || !FileRead(&key->z.interval, 4, 1, file) || !FileRead(&key->z.flags, 4, 1, file)) return AAL_ERROR_FILEIO; } } return AAL_OK; } /////////////////////////////////////////////////////////////////////////////// // // // File input/output // // // /////////////////////////////////////////////////////////////////////////////// aalError Ambiance::Load(const char * _name) { FILE * file = NULL; aalULong sign, version; aalError error; Track * track = NULL; if (track_l) track = &track_l[track_c]; flags = start = time = 0; *name = 0; while (track > track_l) FreeTrack(*(--track)); free(track_l), track_l = NULL, track_c = 0; file = OpenResource(_name, ambiance_path); if (!file) return AAL_ERROR_FILEIO; //Read file signature and version if (!FileRead(&sign, 4, 1, file) || !FileRead(&version, 4, 1, file)) { FileClose(file); return AAL_ERROR_FILEIO; } //Check file signature if (sign != AMBIANCE_FILE_SIGNATURE || version > AMBIANCE_FILE_VERSION) { FileClose(file); return AAL_ERROR_FORMAT; } //Read track count and initialize track structures FileRead(&track_c, 4, 1, file); track_l = (Track *)malloc(sizeof(Track) * track_c); if (!track_l) { FileClose(file); return AAL_ERROR_MEMORY; } memset(track_l, 0, sizeof(Track) * track_c); switch (version) { case AMBIANCE_FILE_VERSION_1000 : error = LoadAmbianceFileVersion_1000(file, *this); break; case AMBIANCE_FILE_VERSION_1001 : error = LoadAmbianceFileVersion_1001(file, *this); break; case AMBIANCE_FILE_VERSION_1002 : error = LoadAmbianceFileVersion_1002(file, *this); break; case AMBIANCE_FILE_VERSION_1003 : error = LoadAmbianceFileVersion_1003(file, *this); break; default : error = AAL_ERROR; } FileClose(file); strcpy(name, _name); return error; } /////////////////////////////////////////////////////////////////////////////// // // // Key status // // // /////////////////////////////////////////////////////////////////////////////// aalError Ambiance::GetTrackKeyLength(const aalSLong & t_id, const aalSLong & k_id, aalULong & length) { aalFloat f_lgt(0.0F); aalSLong s_id; Sample * sample; if (IsNotTrackKey(t_id, k_id)) return AAL_ERROR_HANDLE; s_id = GetSampleID(track_l[t_id].s_id); if (_sample.IsNotValid(s_id)) { length = 0; return AAL_OK; } sample = _sample[s_id]; TrackKey * key = &track_l[t_id].key_l[k_id]; if (key->pitch.interval) { aalFloat min, max, cur; aalFloat size; length = sample->length; size = aalFloat(length) * (key->loop + 1); min = key->pitch.min * sample->format.frequency * key->pitch.interval * 0.001F; max = key->pitch.max * sample->format.frequency * key->pitch.interval * 0.001F; cur = max; while (size > 0.0F) { f_lgt += key->pitch.interval; size -= cur = cur == min ? max : min; } if (size < 0.0F) f_lgt += key->pitch.interval * (size / cur); f_lgt *= 0.5F; } else { aalULong size, loop; sample->GetLength(size); loop = (key->loop + 1) / 2; f_lgt = (size * loop) * (1.0F / key->pitch.max); loop = (key->loop + 1) - loop; f_lgt += (size * loop) * (1.0F / key->pitch.min); } length = key->start + (key->loop + 1) * key->delay_max; length += aalULong(f_lgt); return AAL_OK; } /////////////////////////////////////////////////////////////////////////////// // // // Track status // // // /////////////////////////////////////////////////////////////////////////////// aalError Ambiance::GetTrackLength(const aalSLong & t_id, aalULong & length) { if (aalULong(t_id) >= track_c) return AAL_ERROR_HANDLE; length = 0; for (aalULong i(0); i < track_l[t_id].key_c; i++) { aalULong k_lgt; GetTrackKeyLength(t_id, i, k_lgt); length += k_lgt; } return AAL_OK; } /////////////////////////////////////////////////////////////////////////////// // // // Setup // // // /////////////////////////////////////////////////////////////////////////////// aalError Ambiance::SetUserData(aalVoid * _data) { data = _data; return AAL_OK; } aalError Ambiance::SetVolume(const aalFloat & _volume) { if (!(channel.flags & AAL_FLAG_VOLUME)) return AAL_ERROR_INIT; channel.volume = (_volume > 1.0F) ? 1.0F : (_volume < 0.0F) ? 0.0F : _volume; if (flags & IS_PLAYING) { Track * track = &track_l[track_c]; while (track > track_l) { --track; aalSLong s_id(GetSampleID(track->s_id)); aalSLong i_id(GetInstanceID(track->s_id)); if (_sample.IsValid(s_id) && _inst.IsValid(i_id) && _inst[i_id]->sample == _sample[s_id]) _inst[i_id]->SetVolume(track->key_l[track->key_i].volume.cur * channel.volume); } } return AAL_OK; } aalError Ambiance::MuteTrack(const aalSLong & t_id, const aalUBool & mute) { Track * track = &track_l[t_id]; if (IsNotTrack(t_id)) return AAL_ERROR_HANDLE; if (mute) { track->flags |= TRACK_MUTED; if (flags & IS_PLAYING) { aalSLong s_id(GetSampleID(track->s_id)); if (_sample.IsValid(s_id)) { aalSLong i_id(GetInstanceID(track->s_id)); if (_inst.IsValid(i_id) && _inst[i_id]->sample == _sample[s_id]) _inst[i_id]->Stop(); } } } else { track->flags &= ~TRACK_MUTED; if (flags & IS_PLAYING) KeyPlay(*track, track->key_l[track->key_i = 0]); } return AAL_OK; } /////////////////////////////////////////////////////////////////////////////// // // // Status // // // /////////////////////////////////////////////////////////////////////////////// aalError Ambiance::GetName(char * _name, const aalULong & max_char) { if (max_char < strlen(name) + 1) return AAL_ERROR_MEMORY; strcpy(_name, name); return AAL_OK; } aalError Ambiance::GetUserData(aalVoid ** _data) { *_data = data; return AAL_OK; } aalError Ambiance::GetTrackID(const char * _name, aalSLong & t_id) { if (!track_c) { t_id = AAL_SFALSE; return AAL_OK; } Track * track = &track_l[track_c]; while (track > track_l) { --track; if (track->name && !_stricmp(track->name, _name)) break; else if (!_stricmp(_sample[GetSampleID(track->s_id)]->name, _name)) break; } t_id = track < track_l ? AAL_SFALSE : track - track_l; return AAL_OK; } aalError Ambiance::GetVolume(aalFloat & volume) { if (!(channel.flags & AAL_FLAG_VOLUME)) return AAL_ERROR_INIT; volume = channel.volume; return AAL_OK; } aalUBool Ambiance::IsPlaying() { return flags & IS_PLAYING ? AAL_UTRUE : AAL_UFALSE; } aalUBool Ambiance::IsPaused() { return flags & IS_PAUSED ? AAL_UTRUE : AAL_UFALSE; } aalUBool Ambiance::IsLooped() { return flags & IS_LOOPED ? AAL_UTRUE : AAL_UFALSE; } /////////////////////////////////////////////////////////////////////////////// // // // Control // // // /////////////////////////////////////////////////////////////////////////////// aalError Ambiance::Play(const aalChannel & _channel, const aalULong & play_count, const aalULong & _fade_interval) { channel = _channel; if (flags & (IS_PLAYING | IS_PAUSED)) Stop(); if (!play_count) flags |= IS_LOOPED; else flags &= ~IS_LOOPED; fade_interval = (aalFloat)_fade_interval; if (fade_interval) { flags |= IS_FADED_UP, flags &= ~IS_FADED_DOWN; fade_max = channel.volume; channel.volume = 0.0F; fade_time = 0.0F; } Track * track = &track_l[track_c]; while (track > track_l) { aalSLong s_id; --track; s_id = GetSampleID(track->s_id); if (_sample.IsValid(s_id)) { Sample * sample = _sample[s_id]; if (!sample->callb_c) { sample->SetCallback(OnAmbianceSampleStart, track, 0, AAL_UNIT_BYTES); sample->SetCallback(OnAmbianceSampleStop, track, sample->length, AAL_UNIT_BYTES); } } //Init track keys TrackKey * key = &track->key_l[track->key_c]; while (key > track->key_l) { --key; key->delay = key->delay_max; UpdateKeySynch(*key); key->n_start = key->start + key->delay; key->loopc = key->loop; ResetSetting(key->volume); ResetSetting(key->pitch); ResetSetting(key->pan); ResetSetting(key->x); ResetSetting(key->y); ResetSetting(key->z); } track->key_i = 0; } start = session_time; flags |= IS_PLAYING; const Mixer * mixer = _mixer[channel.mixer]; while (mixer) { if (mixer->IsPaused()) { flags |= IS_PAUSED; flags &= ~IS_PLAYING; break; } mixer = mixer->parent; } return AAL_OK; } aalError Ambiance::Stop(const aalULong & _fade_interval) { if (!(flags & IS_PLAYING)) return AAL_OK; if (flags & IS_PAUSED) Resume(); else { fade_interval = (aalFloat)_fade_interval; if (fade_interval) { flags |= IS_FADED_DOWN, flags &= ~IS_FADED_UP; fade_time = 0; return AAL_OK; } } flags &= ~IS_PLAYING; time = 0; Track * track = &track_l[track_c]; while (track > track_l) { --track; aalSLong s_id(GetSampleID(track->s_id)); if (_sample.IsValid(s_id)) { aalSLong i_id(GetInstanceID(track->s_id)); if (_inst.IsValid(i_id) && _inst[i_id]->sample == _sample[s_id]) _inst[i_id]->Stop(); } s_id |= 0xffff0000; } return AAL_OK; } aalError Ambiance::Pause() { if (!(flags & IS_PLAYING)) return AAL_ERROR; time = session_time; flags &= ~IS_PLAYING; flags |= IS_PAUSED; Track * track = &track_l[track_c]; while (track > track_l) { --track; aalSLong s_id(GetSampleID(track->s_id)); if (_sample.IsValid(s_id)) { aalSLong i_id(GetInstanceID(track->s_id)); if (_inst.IsValid(i_id)) { Instance * instance = _inst[i_id]; if (instance->sample == _sample[s_id] && instance->IsPlaying()) { instance->Pause(); track->flags |= TRACK_PAUSED; } } } } return AAL_OK; } aalError Ambiance::Resume() { if (!(flags & IS_PAUSED)) return AAL_ERROR; Track * track = &track_l[track_c]; while (track > track_l) { --track; if (track->flags & TRACK_PAUSED) { aalSLong s_id(GetSampleID(track->s_id)); aalSLong i_id(GetInstanceID(track->s_id)); if (_inst.IsValid(i_id) && _inst[i_id]->sample == _sample[s_id]) _inst[i_id]->Resume(); track->flags &= ~TRACK_PAUSED; } } flags &= ~IS_PAUSED; flags |= IS_PLAYING; start += session_time - time; time = session_time - start; return AAL_OK; } aalError Ambiance::Update() { aalULong interval; if (!(flags & IS_PLAYING)) return AAL_OK; time += interval = session_time - start - time; //Fading if (fade_interval) { fade_time += interval; if (flags & IS_FADED_UP) { channel.volume = fade_max * (aalFloat(fade_time) / fade_interval); if (channel.volume >= fade_max) { channel.volume = fade_max; fade_interval = 0.0F; } channel.volume = LinearToLogVolume(channel.volume); } else if (flags & IS_FADED_DOWN) { channel.volume = fade_max - fade_max * fade_time / fade_interval; if (channel.volume <= 0.0F) { Stop(); return AAL_OK; } channel.volume = LinearToLogVolume(channel.volume); } } //Update tracks Track * track = &track_l[track_c]; while (track > track_l) { --track; aalSLong s_id(GetSampleID(track->s_id)); if (_sample.IsNotValid(s_id) || track->flags & TRACK_MUTED) continue; if (track->key_i < track->key_c) { TrackKey * key = &track->key_l[track->key_i]; //Run / update keys if (key->n_start <= interval) KeyPlay(*track, *key); else { key->n_start -= interval; aalSLong i_id(GetInstanceID(track->s_id)); if (_inst.IsValid(i_id) && _inst[i_id]->sample == _sample[s_id]) { Instance * instance = _inst[i_id]; if (key->volume.interval) { aalFloat value(UpdateSetting(key->volume, time)); if (channel.flags & AAL_FLAG_VOLUME) value *= channel.volume; instance->SetVolume(value); } else instance->SetVolume(key->volume.cur * channel.volume); if (key->pitch.interval) instance->SetPitch(UpdateSetting(key->pitch, time)); if (track->flags & TRACK_3D) { aalVector position; position.x = key->x.interval ? UpdateSetting(key->x, time) : key->x.cur; position.y = key->y.interval ? UpdateSetting(key->y, time) : key->y.cur; position.z = key->z.interval ? UpdateSetting(key->z, time) : key->z.cur; if (channel.flags & AAL_FLAG_POSITION) position += channel.position; instance->SetPosition(position); } else instance->SetPan(UpdateSetting(key->pan, time)); } } } } return AAL_OK; } /////////////////////////////////////////////////////////////////////////////// // // // Macros! // // // /////////////////////////////////////////////////////////////////////////////// inline aalUBool Ambiance::IsNotTrack(const aalSLong & t_id) { return aalULong(t_id) >= track_c ? AAL_UTRUE : AAL_UFALSE; } inline aalUBool Ambiance::IsNotTrackKey(const aalSLong & t_id, const aalSLong & k_id) { return aalULong(t_id) >= track_c || aalULong(k_id) >= track_l[t_id].key_c ? AAL_UTRUE : AAL_UFALSE; } /////////////////////////////////////////////////////////////////////////////// // // // Static // // // /////////////////////////////////////////////////////////////////////////////// static aalVoid FreeTrack(Track & track) { _sample.Delete(GetSampleID(track.s_id)); free(track.name); free(track.key_l); } static aalVoid ResetSetting(KeySetting & setting) { setting.update = 0; if (setting.min != setting.max && setting.flags & AAL_KEY_SETTING_FLAG_RANDOM) setting.cur = aalFloat(setting.min + fmodf(ARX_CLEAN_WARN_CAST_FLOAT(Random()), setting.max - setting.min)); else setting.cur = setting.min; setting.from = setting.min; setting.to = setting.max; } static aalFloat UpdateSetting(KeySetting & setting, const aalSLong & timez) { if (setting.min != setting.max) { aalSLong elapsed(timez - setting.update); if (elapsed >= aalSLong(setting.interval)) { elapsed = 0; setting.update += setting.interval; if (setting.flags & AAL_KEY_SETTING_FLAG_RANDOM) { setting.from = setting.to; setting.to = aalFloat(setting.min + fmod(FRandom(), setting.max - setting.min)); } else { if (setting.from == setting.min) setting.from = setting.max, setting.to = setting.min; else setting.from = setting.min, setting.to = setting.max; } setting.cur = setting.from; } if (setting.flags & AAL_KEY_SETTING_FLAG_INTERPOLATE) setting.cur = setting.from + aalFloat(elapsed) / setting.interval * (setting.to - setting.from); } return setting.cur; } static aalVoid UpdateKeySynch(TrackKey & key) { if (key.delay_min != key.delay_max) { key.delay = key.delay_max - key.delay; key.delay += key.delay_min + Random() % (key.delay_max - key.delay_min); } else key.delay = key.delay_min; } static aalVoid KeyPlay(Track & track, TrackKey & key) { aalSLong s_id(GetSampleID(track.s_id)); aalSLong i_id(GetInstanceID(track.s_id)); Instance * inst = NULL; if (_inst.IsNotValid(i_id) || _inst[i_id]->sample != _sample[s_id]) { Ambiance * ambiance = _amb[track.a_id]; aalChannel channel; channel.mixer = ambiance->channel.mixer; channel.flags = AAL_FLAG_CALLBACK | AAL_FLAG_VOLUME | AAL_FLAG_PITCH | AAL_FLAG_RELATIVE; channel.flags |= ambiance->channel.flags; channel.volume = key.volume.cur; if (ambiance->channel.flags & AAL_FLAG_VOLUME) channel.volume *= ambiance->channel.volume; channel.pitch = key.pitch.cur; if (track.flags & TRACK_3D) { channel.flags |= AAL_FLAG_POSITION; channel.position.x = key.x.cur; channel.position.y = key.y.cur; channel.position.z = key.z.cur; if (ambiance->channel.flags & AAL_FLAG_POSITION) channel.position += ambiance->channel.position; channel.flags |= ambiance->channel.flags & AAL_FLAG_REVERBERATION; } else { channel.flags |= AAL_FLAG_PAN; channel.pan = key.pan.cur; } inst = new Instance; if (inst->Init(_sample[s_id], channel) || (i_id = _inst.Add(inst)) == AAL_SFALSE) { delete inst; track.s_id |= 0xffff0000; return; } track.s_id = inst->id = (i_id << 16) | s_id; } else inst = _inst[i_id]; if (!key.delay_min && !key.delay_max) inst->Play(key.loopc + 1); else inst->Play(); key.n_start = KEY_CONTINUE; } static aalVoid OnAmbianceSampleStart(aalVoid * inst, const aalSLong &, aalVoid * data) { Instance * instance = (Instance *)inst; Track * track = (Track *)data; TrackKey * key = &track->key_l[track->key_i]; aalFloat value; //aalUBool prefetch_done(AAL_UFALSE); if (track->flags & TRACK_PREFETCHED) { // prefetch_done = AAL_UTRUE; track->flags &= ~TRACK_PREFETCHED; if (track->key_i == track->key_c) key = &track->key_l[track->key_i = 0]; track->key_l[track->key_i].n_start = KEY_CONTINUE; track->key_l[track->key_i].loopc = track->key_l[track->key_i].loop; } //Prefetch if (!key->loopc && _amb[track->a_id]->flags & IS_LOOPED) { aalULong i(track->key_i + 1); if (i == track->key_c) i = 0; if (!track->key_l[i].start && !track->key_l[i].delay_min && !track->key_l[i].delay_max) { instance->Play(track->key_l[i].loop + 1); track->flags |= TRACK_PREFETCHED; } } value = UpdateSetting(key->volume); if (_amb[track->a_id]->channel.flags & AAL_FLAG_VOLUME) value *= _amb[track->a_id]->channel.volume; instance->SetVolume(value); instance->SetPitch(UpdateSetting(key->pitch)); if (instance->channel.flags & AAL_FLAG_POSITION) { aalVector position; position.x = UpdateSetting(key->x); position.y = UpdateSetting(key->y); position.z = UpdateSetting(key->z); if (_amb[track->a_id]->channel.flags & AAL_FLAG_POSITION) position += _amb[track->a_id]->channel.position; instance->SetPosition(position); } else instance->SetPan(UpdateSetting(key->pan)); } static aalVoid OnAmbianceSampleStop(aalVoid *, const aalSLong &, aalVoid * data) { Track * track = (Track *)data; Ambiance * ambiance = _amb[track->a_id]; TrackKey * key = &track->key_l[track->key_i]; if (!key->loopc--) { //Key end key->delay = key->delay_max; UpdateKeySynch(*key); key->n_start = key->start + key->delay; key->loopc = key->loop; key->pitch.update -= ambiance->time; if (++track->key_i == track->key_c) { //Track end if (track->flags & TRACK_MASTER) { //Ambiance end ambiance->time = 0; if (ambiance->flags & IS_LOOPED) { Track * track2 = &ambiance->track_l[ambiance->track_c]; while (track2 > ambiance->track_l) { --track2; if (!(track2->flags & TRACK_PREFETCHED)) track2->key_i = 0; } ambiance->start = session_time; } else ambiance->flags &= ~IS_PLAYING; } } } else if (key->delay_min || key->delay_max) { UpdateKeySynch(*key); key->n_start = key->delay; } } }//ATHENA::