716 lines
19 KiB
C++
716 lines
19 KiB
C++
/*
|
|
* ldp-vldp-audio.cpp
|
|
*
|
|
* Copyright (C) 2001 Matt Ownby
|
|
*
|
|
* This file is part of DAPHNE, a laserdisc arcade game emulator
|
|
*
|
|
* DAPHNE 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* DAPHNE 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
|
|
*/
|
|
|
|
// LDP-VLDP-AUDIO.CPP
|
|
// by Matt Ownby
|
|
|
|
// handles the audio portion of VLDP (using Ogg Vorbis)
|
|
|
|
#ifdef WIN32
|
|
#pragma warning (disable:4100) // disable the warning about unreferenced formal parameters (MSVC++)
|
|
#endif
|
|
|
|
#ifdef UNIX
|
|
//#define TRY_MMAP 1 // NOTE : this seems to fail on read-only filesystems (such as NTFS mounted from linux)
|
|
#endif
|
|
|
|
#include "../timer/timer.h"
|
|
#include "../io/conout.h"
|
|
#include "../io/mpo_fileio.h"
|
|
#include "../sound/sound.h"
|
|
#include "ldp-vldp.h"
|
|
|
|
#ifdef DEBUG
|
|
#include <assert.h> // this may include an extra .DLL in windows that I don't want to rely on
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifndef GP2X
|
|
#include <vorbis/codec.h> // OGG VORBIS specific headers
|
|
#include <vorbis/vorbisfile.h>
|
|
#else
|
|
// gp2x ogg vorbis decoding files
|
|
#include <tremor/ivorbiscodec.h>
|
|
#include <tremor/ivorbisfile.h>
|
|
#endif
|
|
|
|
#ifdef TRY_MMAP
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
// how much uncompressed audio we deal with at a time
|
|
#define AUDIO_BUF_CHUNK 4096
|
|
|
|
// Macros to lock and unlock the mutex for the audio to make sure we aren't playing audio while
|
|
// we are loading or seeking
|
|
#define OGG_LOCK SDL_mutexP(g_ogg_mutex)
|
|
#define OGG_UNLOCK SDL_mutexV(g_ogg_mutex)
|
|
|
|
/////////////////////////////////////////
|
|
|
|
typedef void *(*audiocopyproc)(void *dest, const void *src, size_t bytes_to_copy);
|
|
audiocopyproc paudiocopy = memcpy; // pointer to the audio copy procedure (defaults to memcpy)
|
|
|
|
SDL_mutex *g_ogg_mutex = NULL;
|
|
mpo_io *g_pIOAudioHandle = NULL;
|
|
OggVorbis_File s_ogg;
|
|
|
|
Uint32 g_audio_filesize = 0; // total size of the audio stream
|
|
Uint32 g_audio_filepos = 0; // the position in the file of our audio stream
|
|
Uint8 *g_big_buf = NULL; // holds entire Ogg stream in RAM :)
|
|
bool g_audio_ready = false; // whether audio is ready to be parsed
|
|
bool g_audio_playing = false; // whether the audio is to be playing or not
|
|
Uint32 g_playing_timer = 0; // the time at which we began playing audio
|
|
Uint32 g_samples_played = 0; // how many samples have played since we've been timing
|
|
bool g_audio_left_muted = false; // left audio channel enabled
|
|
bool g_audio_right_muted = false; // right audio channel enabled
|
|
|
|
#ifdef AUDIO_DEBUG
|
|
Uint64 g_u64CallbackByteCount = 0;
|
|
unsigned int g_uCallbackFloodTimer = 0;
|
|
unsigned int g_uCallbackDbgTimer = 0;
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// resets mm states
|
|
void mmreset()
|
|
{
|
|
g_audio_filepos = 0; // rewind back to position #0
|
|
}
|
|
|
|
// replaces fread
|
|
size_t mmread (void *ptr, size_t size, size_t nmemb, void *datasource)
|
|
{
|
|
size_t bytes_to_read = size * nmemb; // how many bytes to be read
|
|
Uint8 *src = ((Uint8 *) datasource) + g_audio_filepos; // where to get the data from
|
|
|
|
// printf("mmread being called.. size is %d, nmemb is %d, bytes_to_read is %d\n", size, nmemb, bytes_to_read);
|
|
|
|
if (g_audio_filepos + bytes_to_read > g_audio_filesize)
|
|
{
|
|
bytes_to_read = 0;
|
|
if (g_audio_filepos < g_audio_filesize)
|
|
{
|
|
bytes_to_read = g_audio_filesize - g_audio_filepos;
|
|
}
|
|
}
|
|
|
|
if (bytes_to_read != 0)
|
|
{
|
|
memcpy(ptr, src, bytes_to_read); // copy the memory
|
|
g_audio_filepos += bytes_to_read;
|
|
}
|
|
|
|
return (bytes_to_read);
|
|
}
|
|
|
|
#ifdef WIN32
|
|
#define int64_t __int64
|
|
#endif
|
|
|
|
int mmseek (void *datasource, int64_t offset, int whence)
|
|
{
|
|
int result = -1;
|
|
|
|
// printf("mmseek being called, whence is %d\n", whence);
|
|
|
|
// just to get rid of warnings
|
|
if (datasource)
|
|
{
|
|
}
|
|
|
|
switch (whence)
|
|
{
|
|
case SEEK_SET:
|
|
// bug fix by Arnaud Gibert
|
|
if (offset <= g_audio_filesize)
|
|
{
|
|
// make sure offset is positive so we don't get into trouble
|
|
if (offset >= 0)
|
|
{
|
|
g_audio_filepos = (Uint32) offset;
|
|
}
|
|
else
|
|
{
|
|
printline("mmseek, SEEK_SET used with a negative offset!");
|
|
}
|
|
result = 0;
|
|
}
|
|
break;
|
|
case SEEK_CUR:
|
|
if (offset + g_audio_filepos <= g_audio_filesize)
|
|
{
|
|
g_audio_filepos = (unsigned int) (g_audio_filepos + offset);
|
|
result = 0;
|
|
}
|
|
break;
|
|
case SEEK_END:
|
|
// printf("SEEK_END being called, offset is %x, whence is %d!\n", (Uint32) offset, whence);
|
|
if (g_audio_filesize + offset <= g_audio_filesize)
|
|
{
|
|
g_audio_filepos = (unsigned int) (g_audio_filesize + offset);
|
|
result = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int mmclose (void *datasource)
|
|
{
|
|
// safety check
|
|
if (datasource != g_big_buf)
|
|
{
|
|
printline("ldp-vldp-audio.cpp: datasource != g_bigbuf, this should never happen!");
|
|
}
|
|
|
|
#ifdef TRY_MMAP
|
|
printline("Unmapping audio stream from memory ...");
|
|
munmap(g_big_buf, g_audio_filesize);
|
|
datasource = NULL;
|
|
#else
|
|
printline("Freeing memory used to store audio stream...");
|
|
//free(datasource);
|
|
delete [] g_big_buf;
|
|
g_big_buf = NULL;
|
|
#endif
|
|
|
|
mpo_close(g_pIOAudioHandle);
|
|
g_pIOAudioHandle = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
long mmtell (void *datasource)
|
|
{
|
|
// printf("mmtell being called, filepos is %x\n", (Uint32) g_audio_filepos);
|
|
|
|
if (datasource)
|
|
{
|
|
}
|
|
|
|
return g_audio_filepos;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// public audio stuff
|
|
|
|
void ldp_vldp::enable_audio1()
|
|
{
|
|
g_audio_left_muted = false;
|
|
set_audiocopy_callback();
|
|
}
|
|
|
|
void ldp_vldp::enable_audio2()
|
|
{
|
|
g_audio_right_muted = false;
|
|
set_audiocopy_callback();
|
|
}
|
|
|
|
void ldp_vldp::disable_audio1()
|
|
{
|
|
g_audio_left_muted = true;
|
|
set_audiocopy_callback();
|
|
}
|
|
|
|
void ldp_vldp::disable_audio2()
|
|
{
|
|
g_audio_right_muted = true;
|
|
set_audiocopy_callback();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// mute audio data
|
|
void *audiocopy_mute(void *dest, const void *src, size_t bytes_to_copy)
|
|
{
|
|
#ifdef WIN32
|
|
ZeroMemory(dest, bytes_to_copy);
|
|
#else
|
|
bzero(dest, bytes_to_copy);
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
// copies left-channel audio data to right-channel
|
|
void *audiocopy_left_only(void *dest, const void *src, size_t bytes_to_copy)
|
|
{
|
|
#ifdef DEBUG
|
|
assert(bytes_to_copy % 4 == 0); // stereo 16-bit audio should always be divisible by 4
|
|
#endif
|
|
Uint32 *dst32 = (Uint32 *) dest;
|
|
Uint16 *src16_L = (Uint16 *) src; // point to first bit of left-channel data
|
|
|
|
bytes_to_copy >>= 2; // divide by 4 because we will be advancing 4 bytes per iteration
|
|
for (unsigned int index = 0; index < bytes_to_copy; index++)
|
|
{
|
|
*dst32 = (*src16_L << 16) | *src16_L; // we only care about left-channel
|
|
dst32++;
|
|
src16_L += 2; // skip over right channel data
|
|
}
|
|
return NULL; // for compatiblity reasons, we don't use this value
|
|
}
|
|
|
|
// copies right-channel audio data to left-channel
|
|
void *audiocopy_right_only(void *dest, const void *src, size_t bytes_to_copy)
|
|
{
|
|
#ifdef DEBUG
|
|
assert(bytes_to_copy % 4 == 0); // stereo 16-bit audio should always be divisible by 4
|
|
#endif
|
|
|
|
Uint32 *dst32 = (Uint32 *) dest;
|
|
Uint16 *src16_R = (Uint16 *) src + 1; // point to the first bit of data that occurs on the right channel
|
|
|
|
bytes_to_copy >>= 2; // divide by 4 because we will be advancing 4 bytes per iteration
|
|
for (unsigned int index = 0; index < bytes_to_copy; index++)
|
|
{
|
|
// fastest method (third attempt)
|
|
*dst32 = (*src16_R << 16) | *src16_R; // we only care about the right-channel
|
|
dst32++;
|
|
src16_R += 2; // skip over left channel data
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// sets the audio copy callbackto be used, based on which audio channels are muted
|
|
// this should only be called when the mute status of the channels is changed
|
|
void ldp_vldp::set_audiocopy_callback()
|
|
{
|
|
if (g_audio_left_muted)
|
|
{
|
|
if (g_audio_right_muted) paudiocopy = audiocopy_mute; // both channels are muted
|
|
else paudiocopy = audiocopy_right_only; // only right channel is on
|
|
}
|
|
else
|
|
{
|
|
if (g_audio_right_muted) paudiocopy = audiocopy_left_only; // only left channel is on
|
|
else paudiocopy = (audiocopyproc) memcpy; // default, both channels on
|
|
}
|
|
}
|
|
|
|
// changes file extension of m2vpath so it ends in .ogg (and add suffix for alt soundtrack if needed)
|
|
void ldp_vldp::oggize_path(string &oggpath, string m2vpath)
|
|
{
|
|
oggpath = m2vpath;
|
|
oggpath.replace(oggpath.length()-4, 4, m_altaudio_suffix); // append optional alternate audio suffix (if this string is blank it should be ok)
|
|
oggpath += ".ogg";
|
|
}
|
|
|
|
// initializes VLDP audio, returns 1 on success or 0 on failure
|
|
bool ldp_vldp::audio_init()
|
|
{
|
|
bool result = false;
|
|
|
|
#ifdef AUDIO_DEBUG
|
|
g_u64CallbackByteCount = 0;
|
|
g_uCallbackFloodTimer = 0;
|
|
g_uCallbackDbgTimer = GET_TICKS();
|
|
#endif
|
|
|
|
// create a mutex to prevent threads from interfering
|
|
g_ogg_mutex = SDL_CreateMutex();
|
|
if (g_ogg_mutex)
|
|
{
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// shuts down VLDP audio
|
|
void ldp_vldp::audio_shutdown()
|
|
{
|
|
// if we have an audio file still open, close it
|
|
if (g_pIOAudioHandle != 0)
|
|
{
|
|
close_audio_stream();
|
|
}
|
|
|
|
// if we successfully created a mutex previously, then destroy it now
|
|
if (g_ogg_mutex)
|
|
{
|
|
SDL_DestroyMutex(g_ogg_mutex);
|
|
g_ogg_mutex = NULL;
|
|
}
|
|
}
|
|
|
|
void ldp_vldp::close_audio_stream()
|
|
{
|
|
OGG_LOCK;
|
|
|
|
g_audio_ready = false;
|
|
g_audio_playing = false;
|
|
ov_clear(&s_ogg);
|
|
|
|
OGG_UNLOCK;
|
|
}
|
|
|
|
bool ldp_vldp::open_audio_stream(const string &strFilename)
|
|
{
|
|
bool result = false;
|
|
ov_callbacks mycallbacks =
|
|
{
|
|
mmread,
|
|
mmseek,
|
|
mmclose,
|
|
mmtell
|
|
};
|
|
|
|
OGG_LOCK; // can't have audio callback running during this
|
|
|
|
// if an audio stream is already open, close it first
|
|
if (g_pIOAudioHandle != 0)
|
|
{
|
|
close_audio_stream();
|
|
}
|
|
|
|
mmreset(); // reset the mm wrappers for new use
|
|
|
|
g_pIOAudioHandle = mpo_open((m_mpeg_path + strFilename).c_str(), MPO_OPEN_READONLY);
|
|
// if audio file was opened successfully
|
|
if (g_pIOAudioHandle)
|
|
{
|
|
g_audio_filesize = static_cast<unsigned int>(g_pIOAudioHandle->size & 0xFFFFFFFF);
|
|
#ifdef TRY_MMAP
|
|
g_big_buf = (Uint8 *) mmap(NULL, g_audio_filesize, PROT_READ, MAP_PRIVATE, fileno(g_pIOAudioHandle->handle), 0);
|
|
if (!g_big_buf)
|
|
{
|
|
printline("ERROR : mmap failed");
|
|
}
|
|
#else
|
|
g_big_buf = new unsigned char[g_audio_filesize];
|
|
if (g_big_buf)
|
|
{
|
|
mpo_read(g_big_buf, g_audio_filesize, NULL, g_pIOAudioHandle); // read entire stream into RAM
|
|
}
|
|
else
|
|
{
|
|
printline("ERROR : out of memory");
|
|
}
|
|
#endif
|
|
if (g_big_buf)
|
|
{
|
|
int open_result = ov_open_callbacks(g_big_buf, &s_ogg, NULL, 0, mycallbacks);
|
|
|
|
// if we opening the .OGG succeeded
|
|
if (open_result == 0)
|
|
{
|
|
// now check to make sure it's stereo and the proper sample rate
|
|
vorbis_info *info = ov_info(&s_ogg, -1);
|
|
|
|
// if they meet the proper specification, let them proceed
|
|
if ((info->channels == 2) && (info->rate == 44100))
|
|
{
|
|
g_audio_ready = true;
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
char s[160];
|
|
printline("OGG ERROR : Your .ogg file needs to have 2 channels and 44100 Hz");
|
|
sprintf(s, "OGG ERROR : Your .ogg file has %u channel(s) and is %ld Hz", info->channels, info->rate);
|
|
printline(s);
|
|
printline("OGG ERROR : Your .ogg file will be ignored (you won't hear any audio)");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char s[160];
|
|
sprintf(s, "ov_open_callbacks failed! Error code is %d\n", open_result);
|
|
printline(s);
|
|
sprintf(s, "OV_EREAD=%d OV_ENOTVORBIS=%d OV_EVERSION=%d OV_EBADHEADER=%d OV_EFAULT=%d\n",
|
|
OV_EREAD, OV_ENOTVORBIS, OV_EVERSION, OV_EBADHEADER, OV_EFAULT);
|
|
printline(s);
|
|
}
|
|
}
|
|
// else we've already printed error messages, so we don't need to do anything else
|
|
|
|
// close file if we got an earlier error
|
|
if (!result)
|
|
{
|
|
mpo_close(g_pIOAudioHandle);
|
|
g_pIOAudioHandle = NULL;
|
|
|
|
// if we have memory allocated, de-allocate it
|
|
if (g_big_buf)
|
|
{
|
|
#ifdef TRY_MMAP
|
|
munmap(g_big_buf, g_audio_filesize);
|
|
#else
|
|
delete [] g_big_buf;
|
|
#endif
|
|
g_big_buf = NULL;
|
|
}
|
|
}
|
|
|
|
} // end if we could open file
|
|
else
|
|
{
|
|
// don't show this message to end-users, a surprising number of them report this as a bug and it's really getting annoying :)
|
|
#ifdef DEBUG
|
|
string s;
|
|
s = "No audio file (" + strFilename + ") was found to go with the opened video file";
|
|
printline(s.c_str());
|
|
printline("NOTE : This is not necessarily a problem, some video doesn't have audio!");
|
|
#endif
|
|
}
|
|
|
|
OGG_UNLOCK;
|
|
|
|
return result;
|
|
}
|
|
|
|
// seeks to a sample position in the audio stream
|
|
// returns true if successful or false if failed
|
|
bool ldp_vldp::seek_audio(Uint64 u64Samples)
|
|
{
|
|
bool result = false;
|
|
|
|
OGG_LOCK; // can't have audio callback running during this
|
|
|
|
if (ov_seekable(&s_ogg))
|
|
{
|
|
ov_pcm_seek(&s_ogg, u64Samples);
|
|
g_audio_playing = false; // audio should not be playing immediately after a seek
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
printline("DOH! OGG stream is not seekable!");
|
|
}
|
|
|
|
OGG_UNLOCK;
|
|
|
|
return result;
|
|
}
|
|
|
|
// starts playing the audio
|
|
void ldp_vldp::audio_play(Uint32 timer)
|
|
{
|
|
OGG_LOCK;
|
|
g_playing_timer = timer;
|
|
g_samples_played = 0;
|
|
g_audio_playing = true;
|
|
OGG_UNLOCK;
|
|
}
|
|
|
|
// pauses the audio at the current position
|
|
void ldp_vldp::audio_pause()
|
|
{
|
|
OGG_LOCK;
|
|
g_audio_playing = false;
|
|
OGG_UNLOCK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
char g_small_buf[AUDIO_BUF_CHUNK] = { 0 };
|
|
Uint8 g_leftover_buf[AUDIO_BUF_CHUNK] = { 0 };
|
|
int g_leftover_samples = 0;
|
|
|
|
// our audio callback
|
|
void ldp_vldp_audio_callback(Uint8 *stream, int len, int unused)
|
|
{
|
|
#ifdef AUDIO_DEBUG
|
|
g_u64CallbackByteCount += len;
|
|
unsigned int uFloodTimer = (GET_TICKS() - g_uCallbackDbgTimer) / 1000;
|
|
if (uFloodTimer != g_uCallbackFloodTimer)
|
|
{
|
|
g_uCallbackFloodTimer = uFloodTimer;
|
|
string s = "audio callback frequency is: " + numstr::ToStr((g_u64CallbackByteCount / uFloodTimer) >> 2);
|
|
printline(s.c_str());
|
|
}
|
|
#endif
|
|
|
|
OGG_LOCK; // make sure nothing changes with any ogg stuff while we decode
|
|
|
|
// if audio is ready to be read and if it is playing
|
|
if (g_audio_ready && g_audio_playing)
|
|
{
|
|
bool audio_caught_up = false;
|
|
int loop_count = 0;
|
|
|
|
// normally we only want to go through this loop once
|
|
// The exception is if we are behind, in which case we want to process audio until we're caught up again
|
|
// we don't want to loop endlessly in here if there is a bug, which is why we have a loop count
|
|
while ((!audio_caught_up) && (loop_count++ < 10))
|
|
{
|
|
long samples_read = 0;
|
|
int samples_copied = 0;
|
|
Uint32 bytes_to_read = 0;
|
|
Uint32 correct_samples = 0; // how many samples we should have played up to this point
|
|
int nop;
|
|
|
|
// if we have some samples from last time for the audio stream
|
|
if (g_leftover_samples)
|
|
{
|
|
if (g_leftover_samples <= len)
|
|
{
|
|
paudiocopy(stream, g_leftover_buf, g_leftover_samples);
|
|
samples_copied += g_leftover_samples;
|
|
g_leftover_samples = 0;
|
|
}
|
|
else
|
|
{
|
|
paudiocopy(stream, g_leftover_buf, len);
|
|
memmove(g_leftover_buf, g_leftover_buf + len, g_leftover_samples - len); // shift remaining buf to front
|
|
// memmove is used because the memory area overlaps
|
|
samples_copied = len;
|
|
g_leftover_samples -= len;
|
|
}
|
|
}
|
|
|
|
while (samples_copied < len)
|
|
{
|
|
#ifndef GP2X
|
|
samples_read = ov_read(&s_ogg, &g_small_buf[0],
|
|
AUDIO_BUF_CHUNK,0,2,1, &nop);
|
|
#else
|
|
// gp2x version
|
|
samples_read = ov_read(&s_ogg, &g_small_buf[0], AUDIO_BUF_CHUNK, &nop);
|
|
#endif
|
|
|
|
if (samples_read > 0)
|
|
{
|
|
bytes_to_read = len - samples_copied; // how much space we have left to fill
|
|
// (samples_copied can and often is 0)
|
|
|
|
// if we have more space to fill than samples available, then we only want to read
|
|
// as many samples as we have available
|
|
if (bytes_to_read > (Uint32) samples_read)
|
|
{
|
|
bytes_to_read = samples_read;
|
|
}
|
|
// else we have to split the buffer
|
|
else
|
|
{
|
|
g_leftover_samples = samples_read - bytes_to_read;
|
|
memcpy(g_leftover_buf, g_small_buf + bytes_to_read, g_leftover_samples);
|
|
}
|
|
|
|
paudiocopy(stream + samples_copied, g_small_buf, bytes_to_read);
|
|
samples_copied += bytes_to_read;
|
|
} // end if samples were read
|
|
|
|
// if we got an error
|
|
else if (samples_read < 0)
|
|
{
|
|
printline("Problem reading samples!");
|
|
g_audio_playing = false;
|
|
break;
|
|
}
|
|
|
|
// else, samples_read == 0 in which case we've come to the end of the stream
|
|
else
|
|
{
|
|
printline("End of audio stream detected!");
|
|
g_audio_playing = false;
|
|
break;
|
|
}
|
|
|
|
} // end while we have not filled the buffer
|
|
|
|
// NOW WE CHECK TO SEE IF THE AUDIO IS LAGGING TOO FAR BEHIND
|
|
// IF IT IS, WE NEED TO SKIP FORWARD
|
|
|
|
g_samples_played += len; // update stats on how many samples have played so we can make sure audio is in sync
|
|
|
|
//unsigned int cur_time = refresh_ms_time();
|
|
unsigned int cur_time = g_ldp->get_elapsed_ms_since_play();
|
|
// if our timer is set to the current time or some previous time
|
|
if (g_playing_timer < cur_time)
|
|
{
|
|
static const Uint64 uBYTES_PER_S = AUDIO_FREQ * AUDIO_BYTES_PER_SAMPLE; // needs to be uint64 to prevent overflow from subsequent math
|
|
correct_samples = (unsigned int) ((uBYTES_PER_S * (cur_time - g_playing_timer)) / 1000);
|
|
// how many samples should have played
|
|
// 176.4 = 44.1 samples per millisecond * 2 for stereo * 2 for 16-bit
|
|
}
|
|
// our timer is set to some time in the future (used with skipping) so we actually
|
|
// should not have played any samples at this point
|
|
else
|
|
{
|
|
// fprintf(stderr, "LDP-VLDP-AUDIO : Timer is in the future\n");
|
|
correct_samples = 0;
|
|
}
|
|
|
|
// if we're ahead instead of behind, don't loop
|
|
if (correct_samples <= g_samples_played)
|
|
{
|
|
audio_caught_up = true;
|
|
|
|
/*
|
|
// warn user if we're too far ahead (this should never happen)
|
|
if ((g_samples_played - correct_samples) > len)
|
|
{
|
|
string s = "ldp-vldp-audio callback: audio is too far ahead! played: " +
|
|
numstr::ToStr(g_samples_played) + ", expected: " + numstr::ToStr(correct_samples);
|
|
printline(s.c_str());
|
|
}
|
|
*/
|
|
}
|
|
|
|
// if we're not too far behind, don't loop
|
|
else if ((Sint32) (correct_samples - g_samples_played) < len)
|
|
{
|
|
audio_caught_up = true;
|
|
}
|
|
|
|
// if we're too far behind, notify the user for testing purposes
|
|
else
|
|
{
|
|
/*
|
|
char s[160];
|
|
sprintf(s, "AUDIO : played %u, expected %u, timer=%u, curtime=%u", g_samples_played, correct_samples, g_playing_timer, g_ldp->get_elapsed_ms_since_play());
|
|
printline(s);
|
|
*/
|
|
audio_caught_up = false;
|
|
SDL_Delay(0); // don't starve other processes while trying to catch up
|
|
}
|
|
} // end while we're not caught up
|
|
|
|
} // end if audio is playing
|
|
|
|
// Either we have no audio file opened OR
|
|
// disc is not playing, pause audio and make sure we don't have any extraneous audio lingering around when
|
|
// we start playing again
|
|
else
|
|
{
|
|
// fill audio stream with silence since it will be expecting to get something back from us
|
|
#ifdef WIN32
|
|
ZeroMemory(stream, len);
|
|
#else
|
|
bzero(stream, len);
|
|
#endif
|
|
|
|
g_leftover_samples = 0;
|
|
}
|
|
|
|
OGG_UNLOCK;
|
|
|
|
}
|