void Sound::PlaySound(const boost::filesystem::path& path, bool is_ui_sound/* = false*/)
{
if (!GetOptionsDB().Get<bool>("UI.sound.enabled") || (is_ui_sound && UISoundsTemporarilyDisabled()))
return;
std::string filename = path.string();
ALuint current_buffer;
ALenum source_state;
ALsizei ogg_freq;
FILE *file = 0;
int m_i;
bool found_buffer = true;
bool found_source = false;
#ifdef FREEORION_WIN32
ov_callbacks callbacks = {
(size_t (*)(void *, size_t, size_t, void *)) fread,
(int (*)(void *, ogg_int64_t, int)) _fseek64_wrap,
(int (*)(void *)) fclose,
(long (*)(void *)) ftell
};
#endif
if (alcGetCurrentContext() != 0)
{
/* First check if the sound data of the file we want to play is already buffered somewhere */
std::map<std::string, ALuint>::iterator it = m_buffers.find(filename);
if (it != m_buffers.end())
current_buffer = it->second;
else {
if ((file = fopen(filename.c_str(), "rb")) != 0) // make sure we CAN open it
{
OggVorbis_File ogg_file;
vorbis_info *vorbis_info;
ALenum ogg_format;
#ifdef FREEORION_WIN32
if (!(ov_test_callbacks(file, &ogg_file, 0, 0, callbacks))) // check if it's a proper ogg
#else
if (!(ov_test(file, &ogg_file, 0, 0))) // check if it's a proper ogg
#endif
{
ov_test_open(&ogg_file); // it is, now fully open the file
/* now we need to take some info we will need later */
vorbis_info = ov_info(&ogg_file, -1);
if (vorbis_info->channels == 1)
ogg_format = AL_FORMAT_MONO16;
else
ogg_format = AL_FORMAT_STEREO16;
ogg_freq = vorbis_info->rate;
ogg_int64_t byte_size = ov_pcm_total(&ogg_file, -1) * vorbis_info->channels * 2;
if (byte_size <= 1024 * 1024 * 1024) {
/* fill up the buffers and queue them up for the first time */
ALuint sound_handle;
alGenBuffers(1, &sound_handle);
int loop = 0;
RefillBuffer(&ogg_file, ogg_format, ogg_freq, sound_handle, byte_size, loop);
current_buffer = sound_handle;
m_buffers.insert(std::make_pair(filename, sound_handle));
}
else
{
ErrorLogger() << "PlaySound: unable to open file " << filename.c_str() << " too big to buffer. Aborting\n";
}
ov_clear(&ogg_file);
}
else
{
ErrorLogger() << "PlaySound: unable to open file " << filename.c_str() << " possibly not a .ogg vorbis file. Aborting\n";
}
}
}
if (found_buffer) {
/* Now that we have the buffer, we need to find a source to send it to */
for (m_i = 1; m_i < NUM_SOURCES; ++m_i) { // as we're playing sounds we start at 1. 0 is reserved for music
alGetSourcei(m_sources[m_i],AL_SOURCE_STATE,&source_state);
if ((source_state != AL_PLAYING) && (source_state != AL_PAUSED)) {
found_source = true;
alSourcei(m_sources[m_i], AL_BUFFER, current_buffer);
alSourcePlay(m_sources[m_i]);
break; // so that the sound won't block all the sources
}
}
if (!found_source)
ErrorLogger() << "PlaySound: Could not find aviable source - playback aborted\n";
}
source_state = alGetError();
if (source_state != AL_NONE)
ErrorLogger() << "PlaySound: OpenAL ERROR: " << alGetString(source_state);
/* it's important to check for errors, as some functions won't work properly if
* they're called when there is a unchecked previous error. */
}
}
int main(){
OggVorbis_File ov;
int i,ret;
ogg_int64_t pcmlength;
double timelength;
char *bigassbuffer;
int dummy;
int hs=0;
#ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */
_setmode( _fileno( stdin ), _O_BINARY );
#endif
/* open the file/pipe on stdin */
if(ov_open_callbacks(stdin,&ov,NULL,-1,OV_CALLBACKS_NOCLOSE)<0){
fprintf(stderr,"Could not open input as an OggVorbis file.\n\n");
exit(1);
}
#if 0 /*enable this code to test seeking with halfrate decode */
if(ov_halfrate(&ov,1)){
fprintf(stderr,"Sorry; unable to set half-rate decode.\n\n");
exit(1);
}else
hs=1;
#endif
if(ov_seekable(&ov)){
/* to simplify our own lives, we want to assume the whole file is
stereo. Verify this to avoid potentially mystifying users
(pissing them off is OK, just don't confuse them) */
for(i=0;i<ov.links;i++){
vorbis_info *vi=ov_info(&ov,i);
if(vi->channels!=2){
fprintf(stderr,"Sorry; right now seeking_test can only use Vorbis files\n"
"that are entirely stereo.\n\n");
exit(1);
}
}
/* because we want to do sample-level verification that the seek
does what it claimed, decode the entire file into memory */
pcmlength=ov_pcm_total(&ov,-1);
timelength=ov_time_total(&ov,-1);
bigassbuffer=malloc((pcmlength>>hs)*2); /* w00t */
i=0;
while(i<(pcmlength>>hs)*2){
int ret=ov_read(&ov,bigassbuffer+i,((pcmlength>>hs)*2)-i,1,1,1,&dummy);
if(ret<0){
fprintf(stderr,"Error reading file.\n");
exit(1);
}
if(ret){
i+=ret;
}else{
pcmlength=(i/2)<<hs;
}
fprintf(stderr,"\rloading.... [%ld left] ",
(long)((pcmlength>>hs)*2-i));
}
{
ogg_int64_t length=ov.end;
fprintf(stderr,"\rtesting raw seeking to random places in %ld bytes....\n",
(long)length);
for(i=0;i<1000;i++){
ogg_int64_t val=(double)rand()/RAND_MAX*length;
fprintf(stderr,"\r\t%d [raw position %ld]... ",i,(long)val);
ret=ov_raw_seek(&ov,val);
if(ret<0){
fprintf(stderr,"seek failed: %d\n",ret);
exit(1);
}
_verify(&ov,val,-1,-1.,pcmlength,bigassbuffer);
}
}
fprintf(stderr,"\r");
{
fprintf(stderr,"testing pcm page seeking to random places in %ld samples....\n",
(long)pcmlength);
for(i=0;i<1000;i++){
ogg_int64_t val= i==0?(ogg_int64_t)0:(double)rand()/RAND_MAX*pcmlength;
fprintf(stderr,"\r\t%d [pcm position %ld]... ",i,(long)val);
ret=ov_pcm_seek_page(&ov,val);
if(ret<0){
fprintf(stderr,"seek failed: %d\n",ret);
exit(1);
}
_verify(&ov,-1,val,-1.,pcmlength,bigassbuffer);
}
}
//.........这里部分代码省略.........
int ov_raw_seek(OggVorbis_File *vf,long pos){
ogg_stream_state work_os;
if(vf->ready_state<OPENED)return(OV_EINVAL);
if(!vf->seekable)
return(OV_ENOSEEK); /* don't dump machine if we can't seek */
if(pos<0 || pos>vf->offsets[vf->links])return(OV_EINVAL);
/* clear out decoding machine state */
vf->pcm_offset=-1;
_decode_clear(vf);
_seek_helper(vf,pos);
/* we need to make sure the pcm_offset is set, but we don't want to
advance the raw cursor past good packets just to get to the first
with a granulepos. That's not equivalent behavior to beginning
decoding as immediately after the seek position as possible.
So, a hack. We use two stream states; a local scratch state and
a the shared vf->os stream state. We use the local state to
scan, and the shared state as a buffer for later decode.
Unfortuantely, on the last page we still advance to last packet
because the granulepos on the last page is not necessarily on a
packet boundary, and we need to make sure the granpos is
correct.
*/
{
ogg_page og;
ogg_packet op;
int lastblock=0;
int accblock=0;
int thisblock=-1;
int eosflag=0;
memset(&work_os,0,sizeof(work_os));/* so that it's safe to clear
it later even if we don't
init it */
while(1){
if(vf->ready_state==STREAMSET){
/* snarf/scan a packet if we can */
int result=ogg_stream_packetout(&work_os,&op);
if(result>0){
if(vf->vi[vf->current_link].codec_setup)
thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
if(eosflag)
ogg_stream_packetout(&vf->os,NULL);
else
if(lastblock)accblock+=(lastblock+thisblock)>>2;
if(op.granulepos!=-1){
int i,link=vf->current_link;
ogg_int64_t granulepos=op.granulepos;
for(i=0;i<link;i++)
granulepos+=vf->pcmlengths[i];
vf->pcm_offset=granulepos-accblock;
break;
}
lastblock=thisblock;
continue;
}
}
if(!lastblock){
if(_get_next_page(vf,&og,-1)<0){
vf->pcm_offset=ov_pcm_total(vf,-1);
break;
}
}else{
/* huh? Bogus stream with packets but no granulepos */
vf->pcm_offset=-1;
break;
}
/* has our decoding just traversed a bitstream boundary? */
if(vf->ready_state==STREAMSET)
if(vf->current_serialno!=ogg_page_serialno(&og)){
_decode_clear(vf); /* clear out stream state */
ogg_stream_clear(&work_os);
}
if(vf->ready_state<STREAMSET){
int link;
vf->current_serialno=ogg_page_serialno(&og);
for(link=0;link<vf->links;link++)
if(vf->serialnos[link]==vf->current_serialno)break;
if(link==vf->links)goto seek_error; /* sign of a bogus stream.
error out, leave
machine uninitialized */
vf->current_link=link;
ogg_stream_init(&vf->os,vf->current_serialno);
//.........这里部分代码省略.........
static void ogg_load_vorbis(const char *filename, WAVEFORMATEX *pwf, std::vector<char> *data)
{
SafePtr<FS::Stream> s = g_fs->Open(filename)->QueryStream();
ov_callbacks cb;
cb.read_func = read_func;
cb.seek_func = seek_func;
cb.close_func = NULL;
cb.tell_func = tell_func;
OggVorbis_File vf;
if( int result = ov_open_callbacks(GetRawPtr(s), &vf, NULL, 0, cb) )
{
switch( result )
{
case OV_EREAD: throw std::runtime_error("A read from media returned an error");
case OV_ENOTVORBIS: throw std::runtime_error("Bitstream does not contain any Vorbis data");
case OV_EVERSION: throw std::runtime_error("Vorbis version mismatch");
case OV_EBADHEADER: throw std::runtime_error("Invalid Vorbis bitstream header");
case OV_EFAULT: throw std::runtime_error("Internal logic fault; indicates a bug or heap/stack corruption");
}
throw std::runtime_error("unknown error opening ov stream");
}
try
{
vorbis_info *pinfo = ov_info(&vf, -1);
if( NULL == pinfo )
{
throw std::runtime_error("could not get info from ov stream");
}
pwf->wFormatTag = WAVE_FORMAT_PCM;
pwf->nChannels = pinfo->channels;
pwf->nSamplesPerSec = pinfo->rate;
pwf->nAvgBytesPerSec = pinfo->rate * pinfo->channels * 2;
pwf->nBlockAlign = pinfo->channels * 2;
pwf->wBitsPerSample = 16;
pwf->cbSize = 0;
size_t size = ov_pcm_total(&vf, -1) * pwf->nBlockAlign;
data->resize(size);
int bitstream = 0;
size_t total = 0;
while( total < size )
{
long ret = ov_read(&vf, &data->at(total), size - total, 0, 2, 1, &bitstream);
if( 0 == ret )
{
break; // eof
}
if( ret < 0 )
{
// error in stream
switch( ret )
{
case OV_HOLE:
throw std::runtime_error("garbage between pages, loss of sync followed by recapture, or a corrupt page");
case OV_EBADLINK:
throw std::runtime_error("invalid stream section or the requested link is corrupt");
case OV_EINVAL:
throw std::runtime_error("initial file headers couldn't be read or are corrupt");
}
throw std::runtime_error("unknown error in ov stream");
}
else
{
total += ret;
}
}
}
catch(...)
{
ov_clear(&vf);
throw;
}
ov_clear(&vf);
}
请发表评论