23 #if AUDIO_TK == AUDIO_TK_OPENAL
24 #if defined(__APPLE__)
25 #import <CoreFoundation/CoreFoundation.h>
26 #import <AudioToolbox/AudioToolbox.h>
35 #define alErrorCheck(X) do { X; { ALenum err = alGetError(); if (err) LogF("al error: %s (%x)", #X, err); } } while (0)
72 #if AUDIO_TK == AUDIO_TK_SDL_MIXER
86 const SDL_version * link_version = Mix_Linked_Version();
87 if (link_version->major < 1
88 || (link_version->major == 1 && link_version->minor < 2)
89 || (link_version->major == 1 && link_version->minor == 2 && link_version->patch < 7))
103 LogF(
"SDL_mixer: %s", SDL_GetError());
107 if (Mix_PlayMusic(
Music,
loop? -1 : 1) == -1)
109 LogF(
"SDL_mixer: %s", SDL_GetError());
124 Music = Mix_LoadMUS_RW(SDL_RWFromConstMem(
Data, filesize), 1);
127 LogF(
"SDL_mixer: %s", SDL_GetError());
130 if (Mix_PlayMusic(
Music,
loop? -1 : 1) == -1)
132 LogF(
"SDL_mixer: %s", SDL_GetError());
141 if (fadeout_ms &&
Music)
144 Mix_FadeOutMusic(fadeout_ms);
149 Mix_FreeMusic(
Music);
162 if (!Mix_PlayingMusic())
168 Mix_VolumeMusic((
int) ((iLevel * MIX_MAX_VOLUME) / 100));
171 #elif AUDIO_TK == AUDIO_TK_OPENAL
175 C4MusicFileOgg::C4MusicFileOgg() :
176 last_interruption_time()
178 for (
unsigned int & buffer : buffers)
182 C4MusicFileOgg::~C4MusicFileOgg()
188 void C4MusicFileOgg::Clear()
197 is_loading_from_file =
false;
199 last_source_file_pos = 0;
200 last_playback_pos_sec = 0;
204 bool C4MusicFileOgg::Init(
const char *strFile)
212 memset(&ogg_file, 0,
sizeof(ogg_file));
213 ov_callbacks callbacks;
219 bool is_loading_from_file =
FileExists(strFile);
221 if (!is_loading_from_file)
227 data.SetOwnedData((
BYTE *)file_contents, file_size);
238 if (!source_file.Open(FileName))
249 if (ov_open_callbacks(data_source, &ogg_file,
nullptr, 0, callbacks) != 0)
256 info = ov_info(&ogg_file, -1);
257 if (info->channels == 1)
258 ogg_info.format = AL_FORMAT_MONO16;
260 ogg_info.format = AL_FORMAT_STEREO16;
261 ogg_info.sample_rate = info->rate;
262 ogg_info.sample_length = ov_time_total(&ogg_file, -1) / 1000.0;
265 vorbis_comment *comment = ov_comment(&ogg_file, -1);
266 const char *comment_id =
"COMMENT=";
267 int comment_id_len = strlen(comment_id);
268 for (
int i = 0; i < comment->comments; ++i)
270 if (comment->comment_lengths[i] > comment_id_len)
272 if (
SEqual2NoCase(comment->user_comments[i], comment_id, comment_id_len))
275 const char *categories_string = comment->user_comments[i] + comment_id_len;
278 int delimeter =
SCharPos(
';', categories_string);
280 category.
Copy(categories_string, delimeter >= 0 ? delimeter :
SLen(categories_string));
281 categories.push_back(category);
282 if (delimeter < 0)
break;
283 categories_string += delimeter+1;
290 UnprepareSourceFileReading();
293 return loaded =
true;
296 StdStrBuf C4MusicFileOgg::GetDebugInfo()
const
303 for (
const auto & category : categories)
306 result.
Append(category.getData());
313 void C4MusicFileOgg::UnprepareSourceFileReading()
319 if (is_loading_from_file && source_file.IsOpen())
321 last_source_file_pos = source_file.Tell();
326 bool C4MusicFileOgg::PrepareSourceFileReading()
329 if (!is_loading_from_file)
return true;
331 if (!source_file.IsOpen())
333 if (!source_file.Open(FileName))
return false;
334 if (last_source_file_pos)
if (source_file.Seek(last_source_file_pos, SEEK_SET) < 0)
return false;
339 bool C4MusicFileOgg::Play(
bool loop,
double max_resume_time)
342 if (!loaded)
return false;
346 if (max_resume_time > 0.0)
return true;
350 PrepareSourceFileReading();
352 alGenSources(1, (ALuint*)&channel);
353 if (!channel)
return false;
356 streaming_done =
false;
361 if (max_resume_time > 0)
364 double time_remaining_sec = GetRemainingTime();
365 if (time_remaining_sec < max_resume_time) last_playback_pos_sec = 0.0;
369 last_playback_pos_sec = 0;
376 ogg_info.sound_data.resize(num_buffers * buffer_size);
377 alGenBuffers(num_buffers, buffers);
378 ov_time_seek(&ogg_file, last_playback_pos_sec);
381 for (
size_t i=0; i<num_buffers; ++i)
382 if (!FillBuffer(i))
break;
390 double C4MusicFileOgg::GetRemainingTime()
393 return ov_time_total(&ogg_file, -1) - last_playback_pos_sec;
396 void C4MusicFileOgg::Stop(
int fadeout_ms)
401 ALfloat playback_pos_in_buffer = 0;
402 alErrorCheck(alGetSourcef(channel, AL_SEC_OFFSET, &playback_pos_in_buffer));
403 last_playback_pos_sec += playback_pos_in_buffer;
406 alSourceStop(channel);
409 alErrorCheck(alGetSourcei(channel, AL_BUFFERS_QUEUED, &num_queued));
411 for (
size_t i = 0; i < (size_t)num_queued; ++i)
412 alErrorCheck(alSourceUnqueueBuffers(channel, 1, &buffer));
417 alSourcei(channel, AL_BUFFER, 0);
418 alDeleteBuffers(num_buffers, buffers);
419 alDeleteSources(1, &channel);
424 UnprepareSourceFileReading();
427 void C4MusicFileOgg::CheckIfPlaying()
433 void C4MusicFileOgg::SetVolume(
int iLevel)
435 volume = ((float) iLevel) / 100.0f;
436 if (channel) alSourcef(channel, AL_GAIN, volume);
439 bool C4MusicFileOgg::HasCategory(
const char *szcat)
const
441 if (!szcat)
return false;
443 for (
const auto & category : categories)
449 bool C4MusicFileOgg::FillBuffer(
size_t idx)
453 long bytes_read_total = 0, bytes_read;
454 char uncompressed_data[buffer_size];
456 bytes_read = ov_read(&ogg_file, uncompressed_data+bytes_read_total, (buffer_size-bytes_read_total)*
sizeof(
BYTE), endian, 2, 1, ¤t_section);
457 bytes_read_total += bytes_read;
458 }
while (bytes_read > 0 && bytes_read_total < buffer_size);
460 if (bytes_read_total)
462 byte_pos_total += bytes_read_total;
463 ALuint buffer = buffers[idx];
464 alErrorCheck(alBufferData(buffer, ogg_info.format, uncompressed_data, bytes_read_total, ogg_info.sample_rate));
466 alErrorCheck(alSourceQueueBuffers(channel, 1, &buffer));
469 if (bytes_read_total < buffer_size)
475 ov_raw_seek(&ogg_file, 0);
478 size_t prev_bytes_total = byte_pos_total;
480 if (!bytes_read_total && prev_bytes_total)
return FillBuffer(idx);
496 void C4MusicFileOgg::Execute()
501 ALint num_processed = 0;
502 alErrorCheck(alGetSourcei(channel, AL_BUFFERS_PROCESSED, &num_processed));
504 while (num_processed--)
508 alErrorCheck(alSourceUnqueueBuffers(channel, 1, &buffer));
510 ALint buf_bits = 16, buf_chans = 2, buf_freq = 44100;
512 alErrorCheck(alGetBufferi(buffer, AL_CHANNELS, &buf_chans));
513 alErrorCheck(alGetBufferi(buffer, AL_FREQUENCY, &buf_freq));
514 double buffer_secs = double(buffer_size) / buf_bits / buf_chans / buf_freq * 8;
515 last_playback_pos_sec += buffer_secs;
518 for (buffer_idx=0; buffer_idx<num_buffers; ++buffer_idx)
519 if (buffers[buffer_idx] == buffer)
break;
520 if (!done) done = !FillBuffer(buffer_idx);
522 if (done) streaming_done =
true;
525 alErrorCheck(alGetSourcei(channel, AL_SOURCE_STATE, &state));
526 if (state != AL_PLAYING && streaming_done)
530 last_playback_pos_sec = 0.0;
532 else if (state == AL_STOPPED)
C4Application Application
bool C4Group_CopyItem(const char *source, const char *target, bool no_sorting, bool reset_attributes)
bool C4Group_ReadFile(const char *filename, char **data, size_t *size)
const char * LoadResStr(const char *id)
bool LogF(const char *strMessage,...)
int SCharPos(char cTarget, const char *szInStr, int iIndex)
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
bool SEqual2NoCase(const char *szStr1, const char *szStr2, int iLen)
size_t SLen(const char *sptr)
bool WildcardMatch(const char *szWildcard, const char *szString)
char * GetFilename(char *szPath)
bool FileExists(const char *szFileName)
bool EraseFile(const char *szFileName)
C4MusicSystem MusicSystem
const char * AtTempPath(const char *filename)
char FileName[_MAX_FNAME_LEN]
virtual bool Init(const char *strFile)
bool Play(bool loop=false, double max_resume_time=0.0) override
~C4MusicFileSDL() override
void CheckIfPlaying() override
void SetVolume(int) override
void Stop(int fadeout_ms=0) override
static long file_tell_func(void *datasource)
static long mem_tell_func(void *datasource)
static int mem_close_func(void *datasource)
static int mem_seek_func(void *datasource, ogg_int64_t offset, int whence)
static int file_close_func(void *datasource)
static size_t file_read_func(void *ptr, size_t byte_size, size_t size_to_read, void *datasource)
static size_t mem_read_func(void *ptr, size_t byte_size, size_t size_to_read, void *datasource)
static int file_seek_func(void *datasource, ogg_int64_t offset, int whence)
static C4TimeMilliseconds Now()
void AppendFormat(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
void AppendChar(char cChar)
void Append(const char *pnData, size_t iChars)