2009-07-27 05:47:50 +00:00
|
|
|
/*
|
|
|
|
Copyright (C) 2009 Jonathon Fowler <jf@jonof.id.au>
|
|
|
|
|
|
|
|
This program 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.
|
|
|
|
|
|
|
|
This program 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.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* OggVorbis source support for MultiVoc
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
# include <vorbis/vorbisfile.h>
|
|
|
|
#else
|
|
|
|
# include "vorbis/vorbisfile.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include "pitch.h"
|
|
|
|
#include "multivoc.h"
|
|
|
|
#include "_multivc.h"
|
|
|
|
|
|
|
|
#define min(x,y) ((x) < (y) ? (x) : (y))
|
|
|
|
#define max(x,y) ((x) > (y) ? (x) : (y))
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
void * ptr;
|
|
|
|
size_t length;
|
|
|
|
size_t pos;
|
|
|
|
|
|
|
|
OggVorbis_File vf;
|
|
|
|
|
|
|
|
char block[0x8000];
|
|
|
|
int32_t blockused;
|
|
|
|
int32_t lastbitstream;
|
|
|
|
} vorbis_data;
|
|
|
|
|
|
|
|
static size_t read_vorbis(void * ptr, size_t size, size_t nmemb, void * datasource)
|
|
|
|
{
|
|
|
|
vorbis_data * vorb = (vorbis_data *) datasource;
|
|
|
|
size_t nread = 0;
|
|
|
|
size_t bytes;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
|
|
if (vorb->length == vorb->pos) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; nmemb > 0; nmemb--, nread++) {
|
|
|
|
bytes = vorb->length - vorb->pos;
|
|
|
|
if (size < bytes) {
|
|
|
|
bytes = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(ptr, vorb->ptr + vorb->pos, bytes);
|
|
|
|
vorb->pos += bytes;
|
|
|
|
ptr += bytes;
|
|
|
|
|
|
|
|
if (vorb->length == vorb->pos) {
|
|
|
|
nread++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nread;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t seek_vorbis(void * datasource, ogg_int64_t offset, int32_t whence)
|
|
|
|
{
|
|
|
|
vorbis_data * vorb = (vorbis_data *) datasource;
|
|
|
|
|
|
|
|
switch (whence) {
|
|
|
|
case SEEK_SET: vorb->pos = 0; break;
|
|
|
|
case SEEK_CUR: break;
|
|
|
|
case SEEK_END: vorb->pos = vorb->length; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
vorb->pos += offset;
|
|
|
|
if (vorb->pos < 0) {
|
|
|
|
vorb->pos = 0;
|
|
|
|
} else if (vorb->pos > vorb->length) {
|
|
|
|
vorb->pos = vorb->length;
|
|
|
|
}
|
|
|
|
|
|
|
|
return vorb->pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t close_vorbis(void * datasource)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-07-27 10:46:42 +00:00
|
|
|
static long tell_vorbis(void * datasource)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
|
|
|
vorbis_data * vorb = (vorbis_data *) datasource;
|
|
|
|
|
|
|
|
return vorb->pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ov_callbacks vorbis_callbacks = {
|
|
|
|
read_vorbis,
|
|
|
|
seek_vorbis,
|
|
|
|
close_vorbis,
|
|
|
|
tell_vorbis
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
|
|
Function: MV_GetNextVorbisBlock
|
|
|
|
|
|
|
|
Controls playback of OggVorbis data
|
|
|
|
---------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
playbackstatus MV_GetNextVorbisBlock
|
|
|
|
(
|
|
|
|
VoiceNode *voice
|
|
|
|
)
|
|
|
|
|
|
|
|
{
|
|
|
|
vorbis_data * vd = (vorbis_data *) voice->extra;
|
|
|
|
int32_t bytes, bytesread;
|
|
|
|
int32_t bitstream, err;
|
|
|
|
|
|
|
|
voice->Playing = TRUE;
|
|
|
|
|
|
|
|
if ( voice->BlockLength > 0 )
|
|
|
|
{
|
|
|
|
voice->position -= voice->length;
|
|
|
|
voice->sound += voice->length >> 16;
|
|
|
|
voice->length = min( voice->BlockLength, 0x8000 );
|
|
|
|
voice->BlockLength -= voice->length;
|
|
|
|
voice->length <<= 16;
|
|
|
|
|
|
|
|
return( KeepPlaying );
|
|
|
|
}
|
|
|
|
|
|
|
|
bytesread = 0;
|
|
|
|
do {
|
|
|
|
bytes = ov_read(&vd->vf, vd->block + bytesread, sizeof(vd->block) - bytesread, 0, 2, 1, &bitstream);
|
|
|
|
//fprintf(stderr, "ov_read = %d\n", bytes);
|
|
|
|
if (bytes == OV_HOLE) continue;
|
|
|
|
if (bytes == 0) {
|
|
|
|
if (voice->LoopStart) {
|
|
|
|
err = ov_pcm_seek_page(&vd->vf, 0);
|
|
|
|
if (err != 0) {
|
|
|
|
fprintf(stderr, "MV_GetNextVorbisBlock ov_pcm_seek_page_lap: err %d\n", err);
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (bytes < 0) {
|
|
|
|
fprintf(stderr, "MV_GetNextVorbisBlock ov_read: err %d\n", bytes);
|
|
|
|
voice->Playing = FALSE;
|
|
|
|
return NoMoreData;
|
|
|
|
}
|
|
|
|
|
|
|
|
bytesread += bytes;
|
|
|
|
} while (bytesread < sizeof(vd->block));
|
|
|
|
|
|
|
|
if (bytesread == 0) {
|
|
|
|
voice->Playing = FALSE;
|
|
|
|
return NoMoreData;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bitstream != vd->lastbitstream) {
|
|
|
|
vorbis_info * vi = 0;
|
|
|
|
|
|
|
|
vi = ov_info(&vd->vf, -1);
|
|
|
|
if (!vi) {
|
|
|
|
voice->Playing = FALSE;
|
|
|
|
return NoMoreData;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vi->channels != 1 && vi->channels != 2) {
|
|
|
|
voice->Playing = FALSE;
|
|
|
|
return NoMoreData;
|
|
|
|
}
|
|
|
|
|
|
|
|
voice->channels = vi->channels;
|
|
|
|
voice->SamplingRate = vi->rate;
|
|
|
|
voice->RateScale = ( voice->SamplingRate * voice->PitchScale ) / MV_MixRate;
|
|
|
|
MV_SetVoiceMixMode( voice );
|
|
|
|
}
|
|
|
|
vd->lastbitstream = bitstream;
|
|
|
|
|
|
|
|
vd->blockused = bytesread;
|
|
|
|
bytesread /= 2 * voice->channels;
|
|
|
|
|
|
|
|
voice->position = 0;
|
|
|
|
voice->sound = vd->block;
|
|
|
|
voice->BlockLength = bytesread;
|
|
|
|
voice->length = min( voice->BlockLength, 0x8000 );
|
|
|
|
voice->BlockLength -= voice->length;
|
|
|
|
voice->length <<= 16;
|
|
|
|
|
|
|
|
return( KeepPlaying );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
|
|
Function: MV_PlayVorbis3D
|
|
|
|
|
|
|
|
Begin playback of sound data at specified angle and distance
|
|
|
|
from listener.
|
|
|
|
---------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
int32_t MV_PlayVorbis3D
|
|
|
|
(
|
|
|
|
char *ptr,
|
|
|
|
uint32_t ptrlength,
|
|
|
|
int32_t pitchoffset,
|
|
|
|
int32_t angle,
|
|
|
|
int32_t distance,
|
|
|
|
int32_t priority,
|
|
|
|
uint32_t callbackval
|
|
|
|
)
|
|
|
|
|
|
|
|
{
|
|
|
|
int32_t left;
|
|
|
|
int32_t right;
|
|
|
|
int32_t mid;
|
|
|
|
int32_t volume;
|
|
|
|
int32_t status;
|
|
|
|
|
|
|
|
if ( !MV_Installed )
|
|
|
|
{
|
|
|
|
MV_SetErrorCode( MV_NotInstalled );
|
|
|
|
return( MV_Error );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( distance < 0 )
|
|
|
|
{
|
|
|
|
distance = -distance;
|
|
|
|
angle += MV_NumPanPositions / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
volume = MIX_VOLUME( distance );
|
|
|
|
|
|
|
|
// Ensure angle is within 0 - 31
|
|
|
|
angle &= MV_MaxPanPosition;
|
|
|
|
|
|
|
|
left = MV_PanTable[ angle ][ volume ].left;
|
|
|
|
right = MV_PanTable[ angle ][ volume ].right;
|
|
|
|
mid = max( 0, 255 - distance );
|
|
|
|
|
|
|
|
status = MV_PlayVorbis( ptr, ptrlength, pitchoffset, mid, left, right, priority,
|
|
|
|
callbackval );
|
|
|
|
|
|
|
|
return( status );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
|
|
Function: MV_PlayVorbis
|
|
|
|
|
|
|
|
Begin playback of sound data with the given sound levels and
|
|
|
|
priority.
|
|
|
|
---------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
int32_t MV_PlayVorbis
|
|
|
|
(
|
|
|
|
char *ptr,
|
|
|
|
uint32_t ptrlength,
|
|
|
|
int32_t pitchoffset,
|
|
|
|
int32_t vol,
|
|
|
|
int32_t left,
|
|
|
|
int32_t right,
|
|
|
|
int32_t priority,
|
|
|
|
uint32_t callbackval
|
|
|
|
)
|
|
|
|
|
|
|
|
{
|
|
|
|
int32_t status;
|
|
|
|
|
|
|
|
status = MV_PlayLoopedVorbis( ptr, ptrlength, -1, -1, pitchoffset, vol, left, right,
|
|
|
|
priority, callbackval );
|
|
|
|
|
|
|
|
return( status );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
|
|
Function: MV_PlayLoopedVorbis
|
|
|
|
|
|
|
|
Begin playback of sound data with the given sound levels and
|
|
|
|
priority.
|
|
|
|
---------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
int32_t MV_PlayLoopedVorbis
|
|
|
|
(
|
|
|
|
char *ptr,
|
|
|
|
uint32_t ptrlength,
|
|
|
|
int32_t loopstart,
|
|
|
|
int32_t loopend,
|
|
|
|
int32_t pitchoffset,
|
|
|
|
int32_t vol,
|
|
|
|
int32_t left,
|
|
|
|
int32_t right,
|
|
|
|
int32_t priority,
|
|
|
|
uint32_t callbackval
|
|
|
|
)
|
|
|
|
|
|
|
|
{
|
|
|
|
VoiceNode *voice;
|
|
|
|
int32_t status;
|
|
|
|
vorbis_data * vd = 0;
|
|
|
|
vorbis_info * vi = 0;
|
|
|
|
|
|
|
|
if ( !MV_Installed )
|
|
|
|
{
|
|
|
|
MV_SetErrorCode( MV_NotInstalled );
|
|
|
|
return( MV_Error );
|
|
|
|
}
|
|
|
|
|
|
|
|
vd = (vorbis_data *) malloc( sizeof(vorbis_data) );
|
|
|
|
if (!vd) {
|
|
|
|
MV_SetErrorCode( MV_InvalidVorbisFile );
|
|
|
|
return MV_Error;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(vd, 0, sizeof(vorbis_data));
|
|
|
|
vd->ptr = ptr;
|
|
|
|
vd->pos = 0;
|
|
|
|
vd->length = ptrlength;
|
|
|
|
vd->blockused = 0;
|
|
|
|
vd->lastbitstream = -1;
|
|
|
|
|
|
|
|
status = ov_open_callbacks((void *) vd, &vd->vf, 0, 0, vorbis_callbacks);
|
|
|
|
if (status < 0) {
|
|
|
|
fprintf(stderr, "MV_PlayLoopedVorbis: err %d\n", status);
|
|
|
|
MV_SetErrorCode( MV_InvalidVorbisFile );
|
|
|
|
return MV_Error;
|
|
|
|
}
|
|
|
|
|
|
|
|
vi = ov_info(&vd->vf, 0);
|
|
|
|
if (!vi) {
|
|
|
|
ov_clear(&vd->vf);
|
|
|
|
free(vd);
|
|
|
|
MV_SetErrorCode( MV_InvalidVorbisFile );
|
|
|
|
return MV_Error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vi->channels != 1 && vi->channels != 2) {
|
|
|
|
ov_clear(&vd->vf);
|
|
|
|
free(vd);
|
|
|
|
MV_SetErrorCode( MV_InvalidVorbisFile );
|
|
|
|
return MV_Error;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Request a voice from the voice pool
|
|
|
|
voice = MV_AllocVoice( priority );
|
|
|
|
if ( voice == NULL )
|
|
|
|
{
|
|
|
|
ov_clear(&vd->vf);
|
|
|
|
free(vd);
|
|
|
|
MV_SetErrorCode( MV_NoVoices );
|
|
|
|
return( MV_Error );
|
|
|
|
}
|
|
|
|
|
|
|
|
voice->wavetype = Vorbis;
|
|
|
|
voice->bits = 16;
|
|
|
|
voice->channels = vi->channels;
|
|
|
|
voice->extra = (void *) vd;
|
|
|
|
voice->GetSound = MV_GetNextVorbisBlock;
|
|
|
|
voice->NextBlock = vd->block;
|
|
|
|
voice->DemandFeed = NULL;
|
|
|
|
voice->LoopCount = 0;
|
|
|
|
voice->BlockLength = 0;
|
|
|
|
voice->PitchScale = PITCH_GetScale( pitchoffset );
|
|
|
|
voice->length = 0;
|
|
|
|
voice->next = NULL;
|
|
|
|
voice->prev = NULL;
|
|
|
|
voice->priority = priority;
|
|
|
|
voice->callbackval = callbackval;
|
|
|
|
voice->LoopStart = (char *) (loopstart >= 0 ? TRUE : FALSE);
|
|
|
|
voice->LoopEnd = 0;
|
|
|
|
voice->LoopSize = 0;
|
|
|
|
|
|
|
|
voice->SamplingRate = vi->rate;
|
|
|
|
voice->RateScale = ( voice->SamplingRate * voice->PitchScale ) / MV_MixRate;
|
|
|
|
MV_SetVoiceMixMode( voice );
|
|
|
|
|
|
|
|
MV_SetVoiceVolume( voice, vol, left, right );
|
|
|
|
MV_PlayVoice( voice );
|
|
|
|
|
|
|
|
return( voice->handle );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MV_ReleaseVorbisVoice( VoiceNode * voice )
|
|
|
|
{
|
|
|
|
vorbis_data * vd = (vorbis_data *) voice->extra;
|
|
|
|
|
|
|
|
if (voice->wavetype != Vorbis) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ov_clear(&vd->vf);
|
|
|
|
free(vd);
|
|
|
|
|
|
|
|
voice->extra = 0;
|
|
|
|
}
|
|
|
|
|