Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
43 KB
Referenced Files
None
Subscribers
None
diff --git a/util/audio.cpp b/util/audio.cpp
index 405cda7e..0ddebb8a 100644
--- a/util/audio.cpp
+++ b/util/audio.cpp
@@ -1,191 +1,261 @@
#include <string.h>
#include "audio.h"
#include "debug.h"
namespace Util{
#ifdef USE_SDL1
AudioConverter::AudioConverter(Encoding inputEncoding, int inputChannels, int inputFrequency,
Encoding outputEncoding, int outputChannels, int outputFrequency){
SDL_BuildAudioCVT(&conversion, inputEncoding, inputChannels, inputFrequency,
outputEncoding, outputChannels, outputFrequency);
}
AudioConverter::~AudioConverter(){
}
int AudioConverter::convertedLength(int length){
return length;
}
int AudioConverter::convert(void * input, int length){
if (conversion.needed){
conversion.buf = (Uint8*) input;
conversion.len = length;
/* then convert to whatever the real output wants */
SDL_ConvertAudio(&conversion);
return conversion.len_cvt;
} else {
return length;
}
}
#else
AudioConverter::AudioConverter(Encoding inputEncoding, int inputChannels, int inputFrequency,
Encoding outputEncoding, int outputChannels, int outputFrequency):
buffer(NULL),
bufferSize(0){
input.bytes = inputEncoding;
input.channels = inputChannels;
input.frequency = inputFrequency;
output.bytes = outputEncoding;
output.channels = outputChannels;
output.frequency = outputFrequency;
sizeRatio = (double) byteSize(output) * output.frequency / ((double) byteSize(input) * input.frequency);
}
int AudioConverter::byteSize(const Format & what){
return encodingBytes(what.bytes) * what.channels;
}
/* how many bytes an encoding takes up */
int AudioConverter::encodingBytes(Encoding what){
switch (what){
+ case Unsigned8: return 1;
case Signed16: return 2;
+ case Unsigned16: return 2;
case Float32: return 4;
}
return 1;
}
int AudioConverter::convertedLength(int length){
// return length * sizeRatio;
int total = length * sizeRatio;
/* make sure we get an even number of samples */
if (total % byteSize(output) != 0){
total -= total % byteSize(output);
}
return total;
}
-double CubicInterpolate(double y0,double y1,
- double y2,double y3,
+double CubicInterpolate(double y0, double y1,
+ double y2, double y3,
double mu){
double a0,a1,a2,a3,mu2;
mu2 = mu*mu;
a0 = y3 - y2 - y0 + y1;
a1 = y0 - y1 - a0;
a2 = y2 - y0;
a3 = y1;
return (a0*mu*mu2+a1*mu2+a2*mu+a3);
}
-template <class Size>
-Size clamp(double input){
- Size top = (1 << (sizeof(Size) * 8 - 1)) - 1;
- Size bottom = -(1 << (sizeof(Size) * 8 - 1));
+template <class Input, class Output>
+Output clamp(double input){
+ Output top = (1 << (sizeof(Output) * 8 - 1)) - 1;
+ Output bottom = -(1 << (sizeof(Output) * 8 - 1));
if (input > top){
return top;
}
if (input < bottom){
return bottom;
}
return input;
}
template <>
-float clamp(double input){
+float clamp<float>(double input){
return input;
}
-template <class Size>
-void doConvertRate(Size * input, int inputSamples, int inputChannels, Size * buffer, int outputSamples, int outputChannels, double ratio){
+template <>
+unsigned short clamp<signed short>(double input){
+ return (int) clamp<signed short, signed short>(input) + (int) (1 << (sizeof(signed short) * 8 - 1));
+}
+
+template <>
+unsigned char clamp<signed short>(double input){
+ double out = input / (1 << (sizeof(signed short) * 8 - 1));
+ if (out > 1){
+ return 1;
+ }
+ if (out < -1){
+ return -1;
+ }
+ /* -1,1 -> 0,255 */
+ return (unsigned char)((out + 1) * 128);
+}
+
+template <>
+float clamp<signed short>(double input){
+ double out = input / (1 << (sizeof(signed short) * 8 - 1));
+ if (out > 1){
+ return 1;
+ }
+ if (out < -1){
+ return -1;
+ }
+ return out;
+}
+
+template <class SizeInput, class SizeOutput>
+void doConvertRate(SizeInput * input, int inputSamples, int inputChannels, SizeOutput * buffer, int outputSamples, int outputChannels, double ratio){
for (int sample = 0; sample < outputSamples; sample += 1){
double inputSample = sample / ratio;
for (int channel = 0; channel < outputChannels; channel += 1){
int inputChannel = inputChannels > channel ? channel : inputChannels - 1;
int sample0 = ((int) inputSample - 1) * inputChannels + inputChannel;
int sample1 = ((int) inputSample + 0) * inputChannels + inputChannel;
int sample2 = ((int) inputSample + 1) * inputChannels + inputChannel;
int sample3 = ((int) inputSample + 2) * inputChannels + inputChannel;
if (sample0 < 0){
sample0 = sample1;
}
if (sample2 >= inputSamples * inputChannels){
sample2 = sample1;
}
if (sample3 >= inputSamples * inputChannels){
sample3 = sample2;
}
- buffer[sample * outputChannels + channel] = clamp<Size>(CubicInterpolate(input[sample0], input[sample1], input[sample2], input[sample3], inputSample - (int) inputSample));
+ buffer[sample * outputChannels + channel] = clamp<SizeInput, SizeOutput>(CubicInterpolate(input[sample0], input[sample1], input[sample2], input[sample3], inputSample - (int) inputSample));
// Global::debug(0) << "Input[" << sample << "] " << channel << ": " << input[sample1] << " Output: " << buffer[sample * 2 + channel] << std::endl;
}
}
}
int AudioConverter::convert(void * input, int length){
/* no conversion needed */
if (this->input == this->output){
return length;
}
int total = convertedLength(length);
/*
if (total % byteSize(output) != 0){
total -= total % byteSize(output);
}
*/
/* cache the buffer for future use */
if (total > bufferSize){
delete[] buffer;
bufferSize = total;
buffer = new char[bufferSize];
}
double frequencyRatio = (double) output.frequency / (double) this->input.frequency;
+ switch (this->input.bytes){
+ case Signed16: {
+ switch (this->output.bytes){
+ case Signed16: doConvertRate<signed short, signed short>(
+ (signed short*) input, length / sizeof(signed short) / this->input.channels, this->input.channels,
+ (signed short*) buffer, total / sizeof(signed short) / output.channels, output.channels,
+ frequencyRatio); break;
+ case Unsigned8: doConvertRate<signed short, unsigned char>(
+ (signed short*) input, length / sizeof(signed short) / this->input.channels, this->input.channels,
+ (unsigned char*) buffer, total / sizeof(unsigned char) / output.channels, output.channels,
+ frequencyRatio); break;
+ case Unsigned16: doConvertRate<signed short, unsigned short>(
+ (signed short*) input, length / sizeof(signed short) / this->input.channels, this->input.channels,
+ (unsigned short*) buffer, total / sizeof(unsigned short) / output.channels, output.channels,
+ frequencyRatio); break;
+ case Float32: doConvertRate<signed short, float>(
+ (signed short*) input, length / sizeof(signed short) / this->input.channels, this->input.channels,
+ (float*) buffer, total / sizeof(float) / output.channels, output.channels,
+ frequencyRatio); break;
+
+ }
+ break;
+ }
+ case Float32: {
+ switch (this->output.bytes){
+ case Float32: doConvertRate<float, float>(
+ (float*) input, length / sizeof(float) / this->input.channels, this->input.channels,
+ (float*) buffer, total / sizeof(float) / output.channels, output.channels,
+ frequencyRatio); break;
+ default: break;
+ }
+ break;
+ }
+ default: break;
+ }
+
+ /*
if (this->input.bytes == output.bytes){
switch (this->input.bytes){
- case Signed16: doConvertRate<signed short>((signed short*) input, length / sizeof(signed short) / this->input.channels, this->input.channels,
+ case Signed16: doConvertRate<signed short, signed short>((signed short*) input, length / sizeof(signed short) / this->input.channels, this->input.channels,
(signed short*) buffer, total / sizeof(signed short) / output.channels, output.channels,
frequencyRatio); break;
- case Float32: doConvertRate<float>((float*) input, length / sizeof(float) / this->input.channels, this->input.channels,
+ case Float32: doConvertRate<float, float>((float*) input, length / sizeof(float) / this->input.channels, this->input.channels,
(float*) buffer, total / sizeof(float) / output.channels, output.channels, frequencyRatio); break;
}
}
+ */
memcpy(input, buffer, total);
return total;
}
AudioConverter::~AudioConverter(){
delete[] buffer;
}
bool AudioConverter::Format::operator==(const AudioConverter::Format & him) const {
return this->bytes == him.bytes &&
this->channels == him.channels &&
this->frequency == him.frequency;
}
#endif
}
diff --git a/util/audio.h b/util/audio.h
index 585adcb8..11694a8f 100644
--- a/util/audio.h
+++ b/util/audio.h
@@ -1,63 +1,65 @@
#ifndef _paintown_audio_h
#define _paintown_audio_h
#ifdef USE_SDL
#include <SDL.h>
#endif
/* Deals with audio conversion between any source format and any destination format.
* A format consists of
* byte encoding (8/16/32 bit, signed/unsigned, floating point/integer)
* number of channels (mono/stereo)
* frequency (22050hz, 44100hz, arbitrary hz)
*/
namespace Util{
/* endianness is always native */
enum Encoding{
+ Unsigned8,
Signed16,
+ Unsigned16,
Float32
};
class AudioConverter{
public:
AudioConverter(Encoding inputEncoding, int inputChannels, int inputFrequency,
Encoding outputEncoding, int outputChannels, int outputFrequency);
/* given some input length, return how long the converted output will be */
int convertedLength(int length);
/* convert the audio, put the output in the same buffer passed in -- 'input'
* and returns the number of converted samples.
* 'length' is the number of input samples in *bytes*
* 'input' should be large enough to hold convertedLength(length) samples
*/
int convert(void * input, int length);
virtual ~AudioConverter();
protected:
#ifdef USE_SDL
SDL_AudioCVT conversion;
#endif
struct Format{
Encoding bytes;
int channels;
int frequency;
bool operator==(const Format & him) const;
};
int byteSize(const Format & what);
int encodingBytes(Encoding what);
Format input, output;
double sizeRatio;
char * buffer;
int bufferSize;
};
}
#endif
diff --git a/util/music-player.cpp b/util/music-player.cpp
index ce2c6d5a..fc4ca076 100644
--- a/util/music-player.cpp
+++ b/util/music-player.cpp
@@ -1,970 +1,971 @@
#ifdef USE_ALLEGRO
#include <allegro.h>
#endif
#include "music-player.h"
#include "globals.h"
#include "util/debug.h"
#include <iostream>
#include "configuration.h"
#include "sound.h"
#include "dumb/include/dumb.h"
#include "gme/Music_Emu.h"
#include "exceptions/exception.h"
#include <sstream>
#include <stdio.h>
#ifdef USE_ALLEGRO5
#include <allegro5/allegro_audio.h>
#endif
#ifdef USE_ALLEGRO
#include "dumb/include/aldumb.h"
#ifdef _WIN32
/* what do we need winalleg for?
* reason: ...
*/
#include <winalleg.h>
#endif
#endif
#ifdef HAVE_MP3_MPG123
#include <mpg123.h>
#endif
#ifdef HAVE_MP3_MAD
#include <mad.h>
#endif
#ifdef USE_SDL
#include "sdl/mixer/SDL_mixer.h"
#endif
using std::string;
namespace Util{
class MusicException: public Exception::Base {
public:
MusicException(const std::string & file, int line, const std::string & reason):
Exception::Base(file, line),
reason(reason){
}
MusicException(const MusicException & copy):
Exception::Base(copy),
reason(copy.reason){
}
virtual ~MusicException() throw(){
}
protected:
virtual const std::string getReason() const {
return reason;
}
virtual Exception::Base * copy() const {
return new MusicException(*this);
}
std::string reason;
};
static double scaleVolume(double start){
return start;
}
/* 1 for big endian (most significant byte)
* 0 for little endian (least significant byte)
*/
/* FIXME: move this to global or something and find a better #ifdef */
int bigEndian(){
#if defined(PS3) || defined(WII)
return 1;
#else
return 0;
#endif
}
#ifdef USE_ALLEGRO5
const int DUMB_SAMPLES = 1024;
MusicRenderer::MusicRenderer(){
create(Sound::Info.frequency, 2);
}
MusicRenderer::MusicRenderer(int frequency, int channels){
create(frequency, channels);
}
void MusicRenderer::create(int frequency, int channels){
ALLEGRO_CHANNEL_CONF configuration = ALLEGRO_CHANNEL_CONF_2;
switch (channels){
case 1: configuration = ALLEGRO_CHANNEL_CONF_1; break;
case 2: configuration = ALLEGRO_CHANNEL_CONF_2; break;
case 3: configuration = ALLEGRO_CHANNEL_CONF_3; break;
case 4: configuration = ALLEGRO_CHANNEL_CONF_4; break;
case 5: configuration = ALLEGRO_CHANNEL_CONF_5_1; break;
case 6: configuration = ALLEGRO_CHANNEL_CONF_6_1; break;
case 7: configuration = ALLEGRO_CHANNEL_CONF_7_1; break;
default: configuration = ALLEGRO_CHANNEL_CONF_2; break;
}
stream = al_create_audio_stream(4, DUMB_SAMPLES, frequency, ALLEGRO_AUDIO_DEPTH_INT16, configuration);
if (!stream){
throw MusicException(__FILE__, __LINE__, "Could not create allegro5 audio stream");
}
queue = al_create_event_queue();
al_register_event_source(queue, al_get_audio_stream_event_source(stream));
}
void MusicRenderer::play(MusicPlayer & player){
al_attach_audio_stream_to_mixer(stream, al_get_default_mixer());
}
void MusicRenderer::pause(){
al_detach_audio_stream(stream);
}
MusicRenderer::~MusicRenderer(){
al_destroy_audio_stream(stream);
al_destroy_event_queue(queue);
}
void MusicRenderer::poll(MusicPlayer & player){
ALLEGRO_EVENT event;
while (al_get_next_event(queue, &event)){
if (event.type == ALLEGRO_EVENT_AUDIO_STREAM_FRAGMENT) {
ALLEGRO_AUDIO_STREAM * stream = (ALLEGRO_AUDIO_STREAM *) event.any.source;
void * data = al_get_audio_stream_fragment(stream);
if (data != NULL){
player.render(data, al_get_audio_stream_length(stream));
al_set_audio_stream_fragment(stream, data);
}
}
}
}
#elif USE_SDL
static const int BUFFER_SIZE = 1024 * 16;
// static const int BUFFER_SIZE = 65536 * 2;
Encoding formatType(int sdlFormat){
switch (sdlFormat){
+ case AUDIO_U8: return Unsigned8;
case AUDIO_S16SYS: return Signed16;
#if SDL_VERSION_ATLEAST(1, 3, 0)
case AUDIO_F32MSB: return Float32;
case AUDIO_F32LSB: return Float32;
#endif
}
std::ostringstream out;
out << "Don't know how to deal with SDL format " << sdlFormat << std::endl;
throw MusicException(__FILE__, __LINE__, out.str());
/*
if (bigEndian()){
switch (Sound::Info.format){
case AUDIO_S16MSB: return Signed16;
}
return Signed16;
} else {
switch (Sound::Info.format){
case AUDIO_S16LSB: return Signed16;
}
return Signed16;
}
*/
}
MusicRenderer::MusicRenderer():
convert(formatType(AUDIO_S16SYS), Sound::Info.channels, Sound::Info.frequency,
formatType(Sound::Info.format), Sound::Info.channels, Sound::Info.frequency){
create(Sound::Info.frequency, Sound::Info.channels);
}
MusicRenderer::MusicRenderer(int frequency, int channels):
convert(formatType(AUDIO_S16SYS), channels, frequency,
formatType(Sound::Info.format), Sound::Info.channels, Sound::Info.frequency){
create(frequency, channels);
}
void MusicRenderer::create(int frequency, int channels){
// Global::debug(1) << "Convert between " << format << ", " << channels << ", " << frequency << " to " << Sound::Info.format << ", " << Sound::Info.channels << ", " << Sound::Info.frequency << std::endl;
/*
SDL_BuildAudioCVT(&convert, format, channels, frequency,
Sound::Info.format, Sound::Info.channels,
Sound::Info.frequency);
*/
int size = convert.convertedLength(BUFFER_SIZE);
data = new Uint8[size < BUFFER_SIZE ? BUFFER_SIZE : size];
position = 0;
converted = 0;
}
static int sampleSize(){
int size = 1;
switch (Sound::Info.format){
case AUDIO_U8:
case AUDIO_S8: size = 1; break;
case AUDIO_U16LSB:
case AUDIO_S16LSB:
case AUDIO_U16MSB:
case AUDIO_S16MSB: size = 2; break;
#if SDL_VERSION_ATLEAST(1, 3, 0)
case AUDIO_S32LSB:
case AUDIO_S32MSB:
case AUDIO_F32LSB:
case AUDIO_F32MSB: size = 2; break;
#endif
default: size = 2; break;
}
return size * Sound::Info.channels;
}
void MusicRenderer::fill(MusicPlayer * player){
position = 0;
/* read samples in dual-channel, 16-bit, signed form */
player->render(data, BUFFER_SIZE / 4);
converted = convert.convert(data, BUFFER_SIZE);
/* sort of a hack, but we need exactly a multiple of 4 */
/*
int totalSample = sampleSize();
if (converted % totalSample != 0){
converted -= converted % totalSample;
}
*/
// Global::debug(0) << "Filled " << converted << " bytes" << std::endl;
#if 0
if (convert.needed){
convert.buf = data;
convert.len = BUFFER_SIZE;
/* then convert to whatever the real output wants */
SDL_ConvertAudio(&convert);
converted = convert.len_cvt;
} else {
converted = BUFFER_SIZE;
}
#endif
}
void MusicRenderer::read(MusicPlayer * player, Uint8 * stream, int bytes){
// Global::debug(0) << "Need " << bytes << " bytes. Have " << (converted - position) << std::endl;
while (bytes > 0){
int length = bytes;
if (length + position >= converted){
length = converted - position;
}
/*
if (length % 4 != 0){
length -= length % 4;
if (length == 0){
fill(player);
continue;
}
}
*/
// Global::debug(0) << "Copy " << length << " bytes" << std::endl;
/* data contains samples in the same format as the output */
memcpy(stream, data + position, length);
stream += length;
position += length;
bytes -= length;
if (position >= converted){
fill(player);
}
}
}
void MusicRenderer::mixer(void * arg, Uint8 * stream, int bytes){
MusicPlayer * player = (MusicPlayer*) arg;
player->getRenderer()->read(player, stream, bytes);
/*
int size = (int)((float) bytes / player->getRenderer()->convert.len_ratio / (float) player->getRenderer()->convert.len_mult);
Global::debug(2) << "Incoming " << bytes << " render " << size << std::endl;
player->getRenderer()->convert.buf = player->getRenderer()->data;
player->getRenderer()->convert.len = size;
// player->render(stream, bytes / 4);
player->render(player->getRenderer()->data, size / 4);
SDL_ConvertAudio(&player->getRenderer()->convert);
memcpy(stream, player->getRenderer()->data, bytes);
*/
}
void MusicRenderer::play(MusicPlayer & player){
Mix_HookMusic(mixer, &player);
}
void MusicRenderer::pause(){
Mix_HookMusic(NULL, NULL);
}
void MusicRenderer::poll(MusicPlayer & player){
}
MusicRenderer::~MusicRenderer(){
Mix_HookMusic(NULL, NULL);
delete[] data;
}
#elif USE_ALLEGRO
int BUFFER_SIZE = 1 << 11;
static int ALLEGRO_MONO = 0;
static int ALLEGRO_STEREO = 1;
MusicRenderer::MusicRenderer(){
create(Sound::Info.frequency, 2);
}
MusicRenderer::MusicRenderer(int frequency, int channels){
create(frequency, channels);
}
void MusicRenderer::create(int frequency, int channels){
int configuration = ALLEGRO_STEREO;
if (channels == 1){
configuration = ALLEGRO_MONO;
}
stream = play_audio_stream(BUFFER_SIZE, 16, configuration, frequency, 255, 128);
if (!stream){
throw MusicException(__FILE__, __LINE__, "Could not create Allegro stream");
}
if (stream->len != BUFFER_SIZE){
throw MusicException(__FILE__, __LINE__, "Buffer size mismatch");
}
voice_set_priority(stream->voice, 255);
}
void MusicRenderer::play(MusicPlayer & player){
voice_start(stream->voice);
}
void MusicRenderer::pause(){
voice_stop(stream->voice);
}
void MusicRenderer::poll(MusicPlayer & player){
short * buffer = (short*) get_audio_stream_buffer(stream);
if (buffer){
player.render(buffer, BUFFER_SIZE);
/* allegro wants unsigned data but gme produces signed so to convert
* signed samples to unsigned samples we have to raise each value
* by half the maximum value of a short (0xffff+1)/2 = 0x8000
*/
for (int i = 0; i < BUFFER_SIZE * 2; i++){
buffer[i] += 0x8000;
}
free_audio_stream_buffer(stream);
}
}
MusicRenderer::~MusicRenderer(){
stop_audio_stream(stream);
}
#endif
MusicPlayer::MusicPlayer():
volume(1.0),
out(new MusicRenderer()){
}
MusicPlayer::~MusicPlayer(){
}
void MusicPlayer::setRenderer(const ReferenceCount<MusicRenderer> & what){
this->out = what;
}
void MusicPlayer::play(){
out->play(*this);
}
void MusicPlayer::pause(){
out->pause();
}
void MusicPlayer::poll(){
out->poll(*this);
}
static const char * typeToExtension( int i ){
switch (i){
case 0 : return ".xm";
case 1 : return ".s3m";
case 2 : return ".it";
case 3 : return ".mod";
default : return "";
}
}
/* expects each sample to be 4 bytes, 2 bytes per sample * 2 channels */
DumbPlayer::DumbPlayer(string path){
music_file = loadDumbFile(path);
if (music_file == NULL){
std::ostringstream error;
error << "Could not load DUMB file " << path;
throw MusicException(__FILE__, __LINE__, error.str());
}
int n_channels = 2;
int position = 0;
renderer = duh_start_sigrenderer(music_file, 0, n_channels, position);
if (!renderer){
Global::debug(0) << "Could not create renderer" << std::endl;
throw Exception::Base(__FILE__, __LINE__);
}
}
void DumbPlayer::render(void * data, int samples){
double delta = 65536.0 / Sound::Info.frequency;
/* FIXME: use global music volume to scale the output here */
int n = duh_render(renderer, 16, 0, volume, delta, samples, data);
}
void DumbPlayer::setVolume(double volume){
this->volume = volume;
}
DumbPlayer::~DumbPlayer(){
duh_end_sigrenderer(renderer);
unload_duh(music_file);
}
DUH * DumbPlayer::loadDumbFile(string path){
DUH * what;
for (int i = 0; i < 4; i++){
/* the order of trying xm/s3m/it/mod matters because mod could be
* confused with one of the other formats, so load it last.
*/
switch (i){
case 0 : {
what = dumb_load_xm_quick(path.c_str());
break;
}
case 1 : {
what = dumb_load_s3m_quick(path.c_str());
break;
}
case 2 : {
what = dumb_load_it_quick(path.c_str());
break;
}
case 3 : {
what = dumb_load_mod_quick(path.c_str());
break;
}
}
if (what != NULL){
Global::debug(0) << "Loaded " << path << " type " << typeToExtension(i) << "(" << i << ")" << std::endl;
return what;
}
}
return NULL;
}
GMEPlayer::GMEPlayer(string path):
emulator(NULL){
gme_err_t fail = gme_open_file(path.c_str(), &emulator, Sound::Info.frequency);
if (fail != NULL){
Global::debug(0) << "GME load error for " << path << ": " << fail << std::endl;
throw MusicException(__FILE__, __LINE__, "Could not load GME file");
}
emulator->start_track(0);
Global::debug(0) << "Loaded GME file " << path << std::endl;
}
void GMEPlayer::render(void * stream, int length){
/* length/2 to convert bytes to short */
emulator->play(length * 2, (short*) stream);
if (emulator->track_ended()){
gme_info_t * info;
gme_track_info(emulator, &info, 0);
int intro = info->intro_length;
emulator->start_track(0);
// Global::debug(0) << "Seeking " << intro << "ms. Track length " << info->length << "ms" << std::endl;
/* skip past the intro if there is a loop */
if (info->loop_length != 0){
emulator->seek(intro);
}
}
/* scale for volume */
for (int i = 0; i < length * 2; i++){
short & sample = ((short *) stream)[i];
sample *= volume;
}
/*
short large = 0;
short small = 0;
for (int i = 0; i < length / 2; i++){
// ((short *) stream)[i] *= 2;
short z = ((short *) stream)[i];
if (z < small){
small = z;
}
if (z > large){
large = z;
}
}
Global::debug(0) << "Largest " << large << " Smallest " << small << std::endl;
*/
}
void GMEPlayer::setVolume(double volume){
this->volume = volume;
}
GMEPlayer::~GMEPlayer(){
delete emulator;
}
#ifdef HAVE_MP3_MPG123
/* initialize the mpg123 library and open up an mp3 file for reading */
static void initializeMpg123(mpg123_handle ** mp3, string path){
/* Initialize */
if (mpg123_init() != MPG123_OK){
throw MusicException(__FILE__, __LINE__, "Could not initialize mpg123");
}
try{
*mp3 = mpg123_new(NULL, NULL);
if (*mp3 == NULL){
throw MusicException(__FILE__,__LINE__, "Could not allocate mpg handle");
}
mpg123_format_none(*mp3);
/* allegro wants unsigned samples but mpg123 can't actually provide unsigned
* samples even though it has an enum for it, MPG123_ENC_UNSIGNED_16. this
* was rectified in 1.13.0 or something, but for now signed samples are ok.
*/
int error = mpg123_format(*mp3, Sound::Info.frequency, MPG123_STEREO, MPG123_ENC_SIGNED_16);
if (error != MPG123_OK){
Global::debug(0) << "Could not set format for mpg123 handle" << std::endl;
}
/* FIXME workaround for libmpg issues with "generic" decoder frequency not being set */
error = mpg123_open(*mp3, (char*) path.c_str());
if (error == -1){
std::ostringstream error;
error << "Could not open mpg123 file " << path << " error code " << error;
throw MusicException(__FILE__,__LINE__, error.str());
}
/* reading a frame is the only surefire way to get mpg123 to set the
* sampling_frequency which it needs to set the decoder a few lines below
*/
size_t dont_care;
unsigned char tempBuffer[4096];
error = mpg123_read(*mp3, tempBuffer, sizeof(tempBuffer), &dont_care);
if (!(error == MPG123_OK || error == MPG123_NEW_FORMAT)){
std::ostringstream error;
error << "Could not read mpg123 file " << path << " error code " << error;
throw MusicException(__FILE__,__LINE__, error.str());
}
mpg123_close(*mp3);
/* stream has progressed a little bit so reset it by opening it again */
error = mpg123_open(*mp3, (char*) path.c_str());
if (error == -1){
std::ostringstream error;
error << "Could not open mpg123 file " << path << " error code " << error;
throw MusicException(__FILE__,__LINE__, error.str());
}
/* FIXME end */
/* some of the native decoders aren't stable in older versions of mpg123
* so just use generic for now. 1.13.1 should work better
*/
error = mpg123_decoder(*mp3, "generic");
if (error != MPG123_OK){
std::ostringstream error;
error << "Could not use 'generic' mpg123 decoder for " << path << " error code " << error;
throw MusicException(__FILE__,__LINE__, error.str());
}
// Global::debug(0) << "mpg support " << mpg123_format_support(mp3, Sound::FREQUENCY, MPG123_ENC_SIGNED_16) << std::endl;
/*
double base, really, rva;
mpg123_getvolume(*mp3, &base, &really, &rva);
// Global::debug(0) << "mpg volume base " << base << " really " << really << " rva " << rva << std::endl;
base_volume = base;
long rate;
int channels, encoding;
mpg123_getformat(*mp3, &rate, &channels, &encoding);
// Global::debug(0) << path << " rate " << rate << " channels " << channels << " encoding " << encoding << std::endl;
*/
} catch (const MusicException & fail){
if (*mp3 != NULL){
mpg123_close(*mp3);
mpg123_delete(*mp3);
*mp3 = NULL;
}
mpg123_exit();
throw;
}
}
static const int MPG123_BUFFER_SIZE = 1 << 11;
Mp3Player::Mp3Player(string path):
mp3(NULL){
initializeMpg123(&mp3, path);
long rate = 0;
int channels = 0, encoding = 0;
mpg123_getformat(mp3, &rate, &channels, &encoding);
}
void Mp3Player::render(void * data, int samples){
/* buffer * 4 for 16 bits per sample * 2 samples for stereo */
size_t out = 0;
mpg123_read(mp3, (unsigned char *) data, samples * 4, &out);
/*
long rate;
int channels, encoding;
mpg123_getformat(mp3, &rate, &channels, &encoding);
Global::debug(0) << "rate " << rate << " channels " << channels << " encoding " << encoding << std::endl;
*/
}
void Mp3Player::setVolume(double volume){
mpg123_volume(mp3, volume);
/*
this->volume = volume;
// mpg123_volume(mp3, volume * base_volume / 5000);
mpg123_volume(mp3, 0.0001);
*/
// mpg123_volume(mp3, volume);
}
Mp3Player::~Mp3Player(){
mpg123_close(mp3);
mpg123_exit();
}
#endif /* MP3_MPG123 */
#ifdef HAVE_OGG
int OGG_BUFFER_SIZE = 1024 * 32;
OggPlayer::OggPlayer(string path):
path(path){
file = fopen(path.c_str(), "rb");
if (!file) {
throw MusicException(__FILE__, __LINE__, "Could not open file");
}
if (ov_open_callbacks(file, &ogg, 0, 0, OV_CALLBACKS_DEFAULT) != 0) {
fclose(file);
throw MusicException(__FILE__, __LINE__, "Could not open ogg");
}
vorbis_info * info = ov_info(&ogg, -1);
frequency = info->rate;
channels = info->channels;
bits = 16;
length = ov_pcm_total(&ogg, -1);
setRenderer(new MusicRenderer(info->rate, info->channels));
buffer = new OggPage();
buffer->buffer1.buffer = new char[OGG_BUFFER_SIZE];
// buffer->buffer2.buffer = new char[OGG_BUFFER_SIZE];
fillPage(&buffer->buffer1);
// fillPage(&buffer->buffer2);
// buffer->use = 0;
}
void OggPlayer::fillPage(OggPage::Page * page){
int dont_care;
page->position = 0;
page->max = 0;
while (page->max < OGG_BUFFER_SIZE){
/* ov_read might not read all available samples, I guess it stops
* reading on a page boundary. We just plow on through.
*/
int read = ov_read(&ogg, (char*) page->buffer + page->max, OGG_BUFFER_SIZE - page->max,
bigEndian(), 2, 1, &dont_care);
/* if we hit the end of the file then re-open it and keep reading */
if (read == 0){
ov_clear(&ogg);
file = fopen(path.c_str(), "rb");
if (!file){
throw MusicException(__FILE__, __LINE__, "Could not open file");
}
int ok = ov_open_callbacks(file, &ogg, 0, 0, OV_CALLBACKS_DEFAULT);
if (ok != 0){
fclose(file);
throw MusicException(__FILE__, __LINE__, "Could not open ogg");
}
} else if (read == OV_HOLE){
throw MusicException(__FILE__, __LINE__, "Garbage in ogg file");
} else if (read == OV_EBADLINK){
throw MusicException(__FILE__, __LINE__, "Invalid stream section in ogg");
} else if (read == OV_EINVAL){
throw MusicException(__FILE__, __LINE__, "File headers are corrupt in ogg");
} else {
page->max += read;
}
}
}
void OggPlayer::doRender(char * data, int bytes){
OggPage::Page & page = buffer->buffer1;
if (page.max - page.position >= bytes){
memcpy(data, page.buffer + page.position, bytes);
page.position += bytes;
} else {
/* copy the rest, fill the page, switch to the other buffer */
memcpy(data, page.buffer + page.position, page.max - page.position);
int at = page.max - page.position;
int rest = bytes - (page.max - page.position);
fillPage(&page);
doRender(data + at, rest);
}
}
void OggPlayer::render(void * data, int length){
doRender((char*) data, length * 4);
}
void OggPlayer::setVolume(double volume){
this->volume = volume;
// Mix_VolumeMusic(volume * MIX_MAX_VOLUME);
}
OggPlayer::~OggPlayer(){
/* ov_clear will close the file */
ov_clear(&ogg);
}
#endif /* OGG */
#ifdef HAVE_MP3_MAD
Mp3Player::Mp3Player(string path):
available(NULL),
bytesLeft(0),
position(0),
raw(NULL){
FILE * handle = fopen(path.c_str(), "rb");
if (!handle){
std::ostringstream out;
out << "Could not open mp3 file " << path;
throw MusicException(__FILE__, __LINE__, out.str());
}
fseek(handle, 0, SEEK_END);
rawLength = ftell(handle);
fseek(handle, 0, SEEK_SET);
raw = new unsigned char[rawLength];
int toRead = rawLength;
int where =0;
while (toRead > 0){
int got = fread(raw + where, 1, toRead, handle);
toRead -= got;
}
fclose(handle);
int rate = 44100, channels = 2;
discoverInfo(raw, rawLength, &rate, &channels);
setRenderer(new MusicRenderer(rate, channels));
Global::debug(0) << "Opened mp3 file " << path << " rate " << rate << " channels " << channels << std::endl;
mad_stream_init(&stream);
mad_frame_init(&frame);
mad_synth_init(&synth);
mad_stream_buffer(&stream, raw, rawLength);
fill(4);
}
/* read the first frame and get the rate and channels from the header.
* assume all other frames use the same rate and channels
*/
void Mp3Player::discoverInfo(unsigned char * raw, int length, int * rate, int * channels){
mad_frame frame;
mad_stream stream;
mad_frame_init(&frame);
mad_stream_init(&stream);
mad_stream_buffer(&stream, raw, length);
int ok = mad_header_decode(&frame.header, &stream);
while (ok == -1){
if (MAD_RECOVERABLE(stream.error)){
ok = mad_header_decode(&frame.header, &stream);
} else {
throw MusicException(__FILE__, __LINE__, "Could not decode mp3 frame");
}
}
*rate = frame.header.samplerate;
switch (frame.header.mode){
case MAD_MODE_SINGLE_CHANNEL: *channels = 1; break;
case MAD_MODE_DUAL_CHANNEL: *channels = 2; break;
case MAD_MODE_JOINT_STEREO: *channels = 2; break;
case MAD_MODE_STEREO: *channels = 2; break;
}
mad_frame_finish(&frame);
mad_stream_finish(&stream);
}
mad_flow Mp3Player::error(void * data, mad_stream * stream, mad_frame * frame){
if (MAD_RECOVERABLE(stream->error)){
return MAD_FLOW_CONTINUE;
}
throw MusicException(__FILE__, __LINE__, "Error decoding mp3 stream");
}
static inline signed int mad_scale(mad_fixed_t sample){
/* round */
sample += (1L << (MAD_F_FRACBITS - 16));
/* clip */
if (sample >= MAD_F_ONE)
sample = MAD_F_ONE - 1;
else if (sample < -MAD_F_ONE)
sample = -MAD_F_ONE;
/* quantize */
return sample >> (MAD_F_FRACBITS + 1 - 16);
}
void Mp3Player::output(mad_header const * header, mad_pcm * pcm){
unsigned int channels = pcm->channels;
unsigned int samples = pcm->length;
/*
mad_fixed_t const * left = pcm->samples[0];
mad_fixed_t const * right = pcm->samples[1];
*/
unsigned short * out = new unsigned short[samples * channels];
for (unsigned int index = 0; index < samples; index++){
for (int channel = 0; channel < channels; channel++){
mad_fixed_t const * left = pcm->samples[channel] + index;
out[index * channels + channel] = mad_scale(*left) & 0xffff;
// out[index * 2 + 1] = mad_scale(*right) & 0xffff;
}
// left += 1;
// right += 1;
}
/* N channels * 2 bytes per sample */
pages.push_back(Data((char*) out, samples * channels * 2));
}
mad_flow Mp3Player::input(void * data, mad_stream * stream){
/*
Mp3Player * player = (Mp3Player*) data;
if (!player->readMore){
return MAD_FLOW_STOP;
} else {
player->readMore = false;
}
int read = fread(player->raw, 1, RAW_SIZE, player->handle);
if (feof(player->handle)){
/ * start over * /
fseek(player->handle, 0, SEEK_SET);
}
mad_stream_buffer(stream, player->raw, read);
return MAD_FLOW_CONTINUE;
*/
return MAD_FLOW_CONTINUE;
}
void Mp3Player::fill(int frames){
for (int i = 0; i < frames; i++){
int headerError = mad_header_decode(&frame.header, &stream);
while (headerError == -1){
if (MAD_RECOVERABLE(stream.error)){
} else {
if (stream.error == MAD_ERROR_BUFLEN){
mad_stream_finish(&stream);
mad_frame_finish(&frame);
mad_synth_finish(&synth);
mad_stream_init(&stream);
mad_frame_init(&frame);
mad_synth_init(&synth);
mad_stream_buffer(&stream, raw, rawLength);
}
}
headerError = mad_header_decode(&frame.header, &stream);
}
mad_frame_decode(&frame, &stream);
mad_synth_frame(&synth, &frame);
output(&frame.header, &synth.pcm);
}
/*
readMore = true;
int result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
*/
bytesLeft = 0;
for (std::vector<Data>::iterator it = pages.begin(); it != pages.end(); it++){
bytesLeft += it->length;
}
// Global::debug(0) << "Read " << bytesLeft << std::endl;
delete[] available;
available = new char[bytesLeft];
position = 0;
int here = 0;
for (std::vector<Data>::iterator it = pages.begin(); it != pages.end(); it++){
memcpy(available + here, it->data, it->length);
here += it->length;
delete[] it->data;
}
// Global::debug(0) << "Filled mp3 with " << bytesLeft << std::endl;
pages.clear();
}
void Mp3Player::render(void * data, int length){
length *= 4;
// Global::debug(0) << "Mp3 render " << length << " have " << bytesLeft << std::endl;
while (length > 0){
int left = length;
if (left > bytesLeft){
left = bytesLeft;
}
memcpy(data, available + position, left);
length -= left;
bytesLeft -= left;
position += left;
data = ((char*) data) + left;
if (bytesLeft == 0){
fill(4);
}
}
}
void Mp3Player::setVolume(double volume){
/* TODO */
}
Mp3Player::~Mp3Player(){
delete[] raw;
delete[] available;
mad_stream_finish(&stream);
mad_frame_finish(&frame);
mad_synth_finish(&synth);
// mad_decoder_finish(&decoder);
}
#endif /* MP3_MAD */
}
diff --git a/util/sdl/sound.cpp b/util/sdl/sound.cpp
index c467704d..e090f75f 100644
--- a/util/sdl/sound.cpp
+++ b/util/sdl/sound.cpp
@@ -1,95 +1,98 @@
#include "../sound.h"
#include <SDL.h>
#include "mixer/SDL_mixer.h"
+#include "util/debug.h"
Sound::Sound():
own(NULL){
}
/* create from wav file (riff header + pcm) */
Sound::Sound(const char * data, int length):
own(NULL){
SDL_RWops * ops = SDL_RWFromConstMem(data, length);
this->data.chunk = Mix_LoadWAV_RW(ops, 1);
own = new int;
*own = 1;
}
/* load from path */
Sound::Sound(const std::string & path) throw (LoadException):
own(NULL){
data.chunk = Mix_LoadWAV(path.c_str());
if (!data.chunk){
printf("Can't load sound %s\n", path.c_str());
// throw LoadException("Could not load sound " + path);
} else {
own = new int;
*own = 1;
}
}
void Sound::initialize(){
// int audio_rate = 22050;
/* allegro uses 44100 by default with alsa9 */
int audio_rate = Info.frequency;
// int audio_rate = 22050;
Uint16 audio_format = AUDIO_S16;
// Uint16 audio_format = MIX_DEFAULT_FORMAT;
int audio_channels = 2;
int audio_buffers = 4096;
// int audio_buffers = 44100;
if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers)) {
printf("Unable to open audio: %s!\n", Mix_GetError());
// exit(1);
}
/* use the frequency enforced by the audio system */
Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
Info.frequency = audio_rate;
Info.channels = audio_channels;
Info.format = audio_format;
+
+ Global::debug(0) << "Opened audio at rate " << audio_rate << " channels " << audio_channels << " format " << audio_format << std::endl;
}
void Sound::uninitialize(){
Mix_CloseAudio();
}
void Sound::play(){
if (data.chunk != NULL){
Mix_VolumeChunk(data.chunk, (int) scale(MIX_MAX_VOLUME));
Mix_PlayChannel(-1, data.chunk, 0);
}
}
void Sound::play(double volume, int pan){
if (data.chunk != NULL){
Mix_VolumeChunk(data.chunk, (int) scale(volume * MIX_MAX_VOLUME));
Mix_PlayChannel(-1, data.chunk, 0);
}
}
void Sound::playLoop(){
if (data.chunk != NULL){
Mix_VolumeChunk(data.chunk, (int) scale(MIX_MAX_VOLUME));
Mix_PlayChannel(-1, data.chunk, -1);
}
}
void Sound::destroy(){
if (own){
*own -= 1;
if ( *own == 0 ){
delete own;
if (data.chunk != NULL){
Mix_FreeChunk(data.chunk);
}
own = NULL;
}
}
}
void Sound::stop(){
if (data.channel != -1){
Mix_HaltChannel(data.channel);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jun 11, 10:06 AM (3 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
68280
Default Alt Text
(43 KB)

Event Timeline