audio.cpp: Split up into smaller parts
This commit is contained in:
parent
e341dca579
commit
f73b0ba4b5
13 changed files with 1830 additions and 1464 deletions
283
src/vorbissource.cpp
Normal file
283
src/vorbissource.cpp
Normal file
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
** vorbissource.cpp
|
||||
**
|
||||
** This file is part of mkxp.
|
||||
**
|
||||
** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
|
||||
**
|
||||
** mkxp 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.
|
||||
**
|
||||
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "aldatasource.h"
|
||||
#include "exception.h"
|
||||
|
||||
#define OV_EXCLUDE_STATIC_CALLBACKS
|
||||
#include <vorbis/vorbisfile.h>
|
||||
#include <vector>
|
||||
|
||||
static size_t vfRead(void *ptr, size_t size, size_t nmemb, void *ops)
|
||||
{
|
||||
return SDL_RWread(static_cast<SDL_RWops*>(ops), ptr, size, nmemb);
|
||||
}
|
||||
|
||||
static int vfSeek(void *ops, ogg_int64_t offset, int whence)
|
||||
{
|
||||
return SDL_RWseek(static_cast<SDL_RWops*>(ops), offset, whence);
|
||||
}
|
||||
|
||||
static long vfTell(void *ops)
|
||||
{
|
||||
return SDL_RWtell(static_cast<SDL_RWops*>(ops));
|
||||
}
|
||||
|
||||
static ov_callbacks OvCallbacks =
|
||||
{
|
||||
vfRead,
|
||||
vfSeek,
|
||||
0,
|
||||
vfTell
|
||||
};
|
||||
|
||||
|
||||
struct VorbisSource : ALDataSource
|
||||
{
|
||||
SDL_RWops &src;
|
||||
|
||||
OggVorbis_File vf;
|
||||
|
||||
uint32_t currentFrame;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t start;
|
||||
uint32_t length;
|
||||
uint32_t end;
|
||||
bool valid;
|
||||
bool requested;
|
||||
} loop;
|
||||
|
||||
struct
|
||||
{
|
||||
int channels;
|
||||
int rate;
|
||||
int frameSize;
|
||||
ALenum alFormat;
|
||||
} info;
|
||||
|
||||
std::vector<int16_t> sampleBuf;
|
||||
|
||||
VorbisSource(SDL_RWops &ops,
|
||||
bool looped)
|
||||
: src(ops),
|
||||
currentFrame(0)
|
||||
{
|
||||
int error = ov_open_callbacks(&src, &vf, 0, 0, OvCallbacks);
|
||||
|
||||
if (error)
|
||||
{
|
||||
SDL_RWclose(&src);
|
||||
throw Exception(Exception::MKXPError,
|
||||
"Vorbisfile: Cannot read ogg file");
|
||||
}
|
||||
|
||||
/* Extract bitstream info */
|
||||
info.channels = vf.vi->channels;
|
||||
info.rate = vf.vi->rate;
|
||||
|
||||
if (info.channels > 2)
|
||||
{
|
||||
ov_clear(&vf);
|
||||
SDL_RWclose(&src);
|
||||
throw Exception(Exception::MKXPError,
|
||||
"Cannot handle audio with more than 2 channels");
|
||||
}
|
||||
|
||||
info.alFormat = chooseALFormat(sizeof(int16_t), info.channels);
|
||||
info.frameSize = sizeof(int16_t) * info.channels;
|
||||
|
||||
sampleBuf.resize(STREAM_BUF_SIZE);
|
||||
|
||||
loop.requested = looped;
|
||||
loop.valid = false;
|
||||
loop.start = loop.length = 0;
|
||||
|
||||
if (!loop.requested)
|
||||
return;
|
||||
|
||||
/* Try to extract loop info */
|
||||
for (int i = 0; i < vf.vc->comments; ++i)
|
||||
{
|
||||
char *comment = vf.vc->user_comments[i];
|
||||
char *sep = strstr(comment, "=");
|
||||
|
||||
/* No '=' found */
|
||||
if (!sep)
|
||||
continue;
|
||||
|
||||
/* Empty value */
|
||||
if (!*(sep+1))
|
||||
continue;
|
||||
|
||||
*sep = '\0';
|
||||
|
||||
if (!strcmp(comment, "LOOPSTART"))
|
||||
loop.start = strtol(sep+1, 0, 10);
|
||||
|
||||
if (!strcmp(comment, "LOOPLENGTH"))
|
||||
loop.length = strtol(sep+1, 0, 10);
|
||||
|
||||
*sep = '=';
|
||||
}
|
||||
|
||||
loop.end = loop.start + loop.length;
|
||||
loop.valid = (loop.start && loop.length);
|
||||
}
|
||||
|
||||
~VorbisSource()
|
||||
{
|
||||
ov_clear(&vf);
|
||||
SDL_RWclose(&src);
|
||||
}
|
||||
|
||||
int sampleRate()
|
||||
{
|
||||
return info.rate;
|
||||
}
|
||||
|
||||
void seekToOffset(float seconds)
|
||||
{
|
||||
currentFrame = seconds * info.rate;
|
||||
|
||||
if (loop.valid && currentFrame > loop.end)
|
||||
currentFrame = loop.start;
|
||||
|
||||
/* If seeking fails, just seek back to start */
|
||||
if (ov_pcm_seek(&vf, currentFrame) != 0)
|
||||
ov_raw_seek(&vf, 0);
|
||||
}
|
||||
|
||||
Status fillBuffer(AL::Buffer::ID alBuffer)
|
||||
{
|
||||
void *bufPtr = sampleBuf.data();
|
||||
int availBuf = sampleBuf.size();
|
||||
int bufUsed = 0;
|
||||
|
||||
int canRead = availBuf;
|
||||
|
||||
Status retStatus = ALDataSource::NoError;
|
||||
|
||||
bool readAgain = false;
|
||||
|
||||
if (loop.valid)
|
||||
{
|
||||
int tilLoopEnd = loop.end * info.frameSize;
|
||||
|
||||
canRead = std::min(availBuf, tilLoopEnd);
|
||||
}
|
||||
|
||||
while (canRead > 16)
|
||||
{
|
||||
long res = ov_read(&vf, static_cast<char*>(bufPtr),
|
||||
canRead, 0, sizeof(int16_t), 1, 0);
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
/* Read error */
|
||||
retStatus = ALDataSource::Error;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (res == 0)
|
||||
{
|
||||
/* EOF */
|
||||
if (loop.requested)
|
||||
{
|
||||
retStatus = ALDataSource::WrapAround;
|
||||
reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
retStatus = ALDataSource::EndOfStream;
|
||||
}
|
||||
|
||||
/* If we sought right to the end of the file,
|
||||
* we might be EOF without actually having read
|
||||
* any data at all yet (which mustn't happen),
|
||||
* so we try to continue reading some data. */
|
||||
if (bufUsed > 0)
|
||||
break;
|
||||
|
||||
if (readAgain)
|
||||
{
|
||||
/* We're still not getting data though.
|
||||
* Just error out to prevent an endless loop */
|
||||
retStatus = ALDataSource::Error;
|
||||
break;
|
||||
}
|
||||
|
||||
readAgain = true;
|
||||
}
|
||||
|
||||
bufUsed += (res / sizeof(int16_t));
|
||||
bufPtr = &sampleBuf[bufUsed];
|
||||
currentFrame += (res / info.frameSize);
|
||||
|
||||
if (loop.valid && currentFrame >= loop.end)
|
||||
{
|
||||
/* Determine how many frames we're
|
||||
* over the loop end */
|
||||
int discardFrames = currentFrame - loop.end;
|
||||
bufUsed -= discardFrames * info.channels;
|
||||
|
||||
retStatus = ALDataSource::WrapAround;
|
||||
|
||||
/* Seek to loop start */
|
||||
currentFrame = loop.start;
|
||||
if (ov_pcm_seek(&vf, currentFrame) != 0)
|
||||
retStatus = ALDataSource::Error;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
canRead -= res;
|
||||
}
|
||||
|
||||
if (retStatus != ALDataSource::Error)
|
||||
AL::Buffer::uploadData(alBuffer, info.alFormat, sampleBuf.data(),
|
||||
bufUsed*sizeof(int16_t), info.rate);
|
||||
|
||||
return retStatus;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
ov_raw_seek(&vf, 0);
|
||||
currentFrame = 0;
|
||||
}
|
||||
|
||||
uint32_t loopStartFrames()
|
||||
{
|
||||
if (loop.valid)
|
||||
return loop.start;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
ALDataSource *createVorbisSource(SDL_RWops &ops,
|
||||
bool looped)
|
||||
{
|
||||
return new VorbisSource(ops, looped);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue