mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 16:58:40 -04:00
woo! mp3 support
This commit is contained in:
parent
7d7e84061a
commit
dfb5f81965
@ -0,0 +1,481 @@
|
|||||||
|
// Filename: audio_load_mp3.cxx
|
||||||
|
// Created by: cary (11Oct00)
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <dconfig.h>
|
||||||
|
#include "audio_pool.h"
|
||||||
|
#include "config_audio.h"
|
||||||
|
#include "audio_trait.h"
|
||||||
|
|
||||||
|
Configure(audio_load_mp3);
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <mpg123.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool initialized = false;
|
||||||
|
static struct audio_info_struct ai;
|
||||||
|
static struct frame fr;
|
||||||
|
struct parameter param = {
|
||||||
|
FALSE, /* aggressive */
|
||||||
|
FALSE, /* shuffle */
|
||||||
|
FALSE, /* remote */
|
||||||
|
DECODE_AUDIO, /* write samples to audio device */
|
||||||
|
FALSE, /* silent operation */
|
||||||
|
FALSE, /* xterm title on/off */
|
||||||
|
0, /* second level buffer size */
|
||||||
|
TRUE, /* resync after stream error */
|
||||||
|
0, /* verbose level */
|
||||||
|
#ifdef TERM_CONTROL
|
||||||
|
FALSE, /* term control */
|
||||||
|
#endif /* TERM_CONTROL */
|
||||||
|
-1, /* force mono */
|
||||||
|
0, /* force stereo */
|
||||||
|
0, /* force 8-bit */
|
||||||
|
0, /* force rate */
|
||||||
|
0, /* down sample */
|
||||||
|
FALSE, /* check range */
|
||||||
|
0, /* double speed */
|
||||||
|
0, /* half speed */
|
||||||
|
0, /* force re-open. always (re)opens audio device for next song */
|
||||||
|
0, /* 3Dnow: autodetect from CPUFLAGS */
|
||||||
|
FALSE, /* 3Dnow: normal operation */
|
||||||
|
FALSE, /* try to run process in 'realtime mode' */
|
||||||
|
{ 0, }, /* wav, cdr, au filename */
|
||||||
|
NULL, /* esdserver */
|
||||||
|
NULL, /* equalfile */
|
||||||
|
0, /* enable_equalizer */
|
||||||
|
32768, /* outscale */
|
||||||
|
0, /* startFrame */
|
||||||
|
};
|
||||||
|
static long numframes = -1;
|
||||||
|
static int intflag = FALSE;
|
||||||
|
static struct mpstr mp;
|
||||||
|
// stuff I have to have to make the linkage happy
|
||||||
|
int OutputDescriptor;
|
||||||
|
int buffer_fd[2];
|
||||||
|
struct reader *rd;
|
||||||
|
txfermem* buffermem;
|
||||||
|
|
||||||
|
static void set_synth_functions(struct frame* fr) {
|
||||||
|
typedef int (*func)(real*, int, unsigned char*, int*);
|
||||||
|
typedef int (*func_mono)(real*, unsigned char*, int*);
|
||||||
|
typedef void (*func_dct36)(real*, real*, real*, real*, real*);
|
||||||
|
|
||||||
|
int ds = fr->down_sample;
|
||||||
|
int p8=0;
|
||||||
|
|
||||||
|
static func funcs[][4] = {
|
||||||
|
{ synth_1to1,
|
||||||
|
synth_2to1,
|
||||||
|
synth_4to1,
|
||||||
|
synth_ntom } ,
|
||||||
|
{ synth_1to1_8bit,
|
||||||
|
synth_2to1_8bit,
|
||||||
|
synth_4to1_8bit,
|
||||||
|
synth_ntom_8bit }
|
||||||
|
};
|
||||||
|
static func_mono funcs_mono[2][2][4] = {
|
||||||
|
{ { synth_1to1_mono2stereo,
|
||||||
|
synth_2to1_mono2stereo,
|
||||||
|
synth_4to1_mono2stereo,
|
||||||
|
synth_ntom_mono2stereo } ,
|
||||||
|
{ synth_1to1_8bit_mono2stereo,
|
||||||
|
synth_2to1_8bit_mono2stereo,
|
||||||
|
synth_4to1_8bit_mono2stereo,
|
||||||
|
synth_ntom_8bit_mono2stereo } } ,
|
||||||
|
{ { synth_1to1_mono,
|
||||||
|
synth_2to1_mono,
|
||||||
|
synth_4to1_mono,
|
||||||
|
synth_ntom_mono } ,
|
||||||
|
{ synth_1to1_8bit_mono,
|
||||||
|
synth_2to1_8bit_mono,
|
||||||
|
synth_4to1_8bit_mono,
|
||||||
|
synth_ntom_8bit_mono } } ,
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((ai.format & AUDIO_FORMAT_MASK) == AUDIO_FORMAT_8)
|
||||||
|
p8 = 1;
|
||||||
|
fr->synth = funcs[p8][ds];
|
||||||
|
fr->synth_mono = funcs_mono[param.force_stereo?0:1][p8][ds];
|
||||||
|
if (p8)
|
||||||
|
make_conv16to8_table(ai.format);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialize(void) {
|
||||||
|
if (initialized)
|
||||||
|
return;
|
||||||
|
// make sure params say what we want
|
||||||
|
param.quiet = TRUE;
|
||||||
|
param.force_stereo = 1;
|
||||||
|
param.force_rate = audio_mix_freq;
|
||||||
|
|
||||||
|
memset(&mp, 0, sizeof(struct mpstr));
|
||||||
|
audio_info_struct_init(&ai);
|
||||||
|
audio_capabilities(&ai);
|
||||||
|
set_synth_functions(&fr);
|
||||||
|
make_decode_tables(param.outscale);
|
||||||
|
init_layer2(); /* inits also shared tables with layer1 */
|
||||||
|
init_layer3(fr.down_sample);
|
||||||
|
equalizer_cnt = 0;
|
||||||
|
for (int i=0; i<32; ++i) {
|
||||||
|
equalizer[0][i] = equalizer[1][i] = 1.0;
|
||||||
|
equalizer_sum[0][i] = equalizer_sum[1][i] = 0.0;
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ostream* my_outstream;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
int audio_open(struct audio_info_struct* ai) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int audio_reset_parameters(struct audio_info_struct* ai) {
|
||||||
|
audio_set_format(ai);
|
||||||
|
audio_set_channels(ai);
|
||||||
|
audio_set_rate(ai);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int audio_rate_best_match(struct audio_info_struct* ai) {
|
||||||
|
if (!ai || ai->rate < 0)
|
||||||
|
return -1;
|
||||||
|
ai->rate = audio_mix_freq;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int audio_set_rate(struct audio_info_struct* ai) {
|
||||||
|
if (ai->rate != audio_mix_freq)
|
||||||
|
audio_cat->warning()
|
||||||
|
<< "trying to decode mp3 to rate other then mix rate (" << ai->rate
|
||||||
|
<< " != " << audio_mix_freq << ")" << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int audio_set_channels(struct audio_info_struct* ai) {
|
||||||
|
if (ai->channels != 2)
|
||||||
|
audio_cat->warning() << "trying to decode mp3 to non-stereo ("
|
||||||
|
<< ai->channels << " != 2)" << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int audio_set_format(struct audio_info_struct* ai) {
|
||||||
|
if (ai->format != AUDIO_FORMAT_SIGNED_16)
|
||||||
|
audio_cat->warning()
|
||||||
|
<< "trying to decode mp3 to format other then signed 16-bit" << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int audio_get_formats(struct audio_info_struct* ai) {
|
||||||
|
return AUDIO_FORMAT_SIGNED_16;
|
||||||
|
}
|
||||||
|
|
||||||
|
int audio_play_samples(struct audio_info_struct* ai, unsigned char* buf,
|
||||||
|
int len) {
|
||||||
|
for (int i=0; i<len; ++i)
|
||||||
|
(*my_outstream) << buf[i];
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int audio_close(struct audio_info_struct* ai) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we won't use these functions, but they have to exist
|
||||||
|
int cdr_open(struct audio_info_struct *ai, char *ame) { return 0; }
|
||||||
|
int au_open(struct audio_info_struct *ai, char *name) { return 0; }
|
||||||
|
int wav_open(struct audio_info_struct *ai, char *wavfilename) { return 0; }
|
||||||
|
int wav_write(unsigned char *buf,int len) { return 0; }
|
||||||
|
int cdr_close(void) { return 0; }
|
||||||
|
int au_close(void) { return 0; }
|
||||||
|
int wav_close(void) { return 0; }
|
||||||
|
int xfermem_get_usedspace(txfermem*) { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_output(void) {
|
||||||
|
static int init_done = FALSE;
|
||||||
|
if (init_done)
|
||||||
|
return;
|
||||||
|
init_done = TRUE;
|
||||||
|
// + 1024 for NtoM rate converter
|
||||||
|
if (!(pcm_sample = (unsigned char*)malloc(audiobufsize*2 + 2*1024))) {
|
||||||
|
audio_cat->fatal() << "cannot allocate sample buffer" << endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
switch (param.outmode) {
|
||||||
|
case DECODE_AUDIO:
|
||||||
|
if (audio_open(&ai) < 0) {
|
||||||
|
audio_cat->fatal() << "could not open output stream" << endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DECODE_WAV:
|
||||||
|
wav_open(&ai, param.filename);
|
||||||
|
break;
|
||||||
|
case DECODE_AU:
|
||||||
|
au_open(&ai, param.filename);
|
||||||
|
break;
|
||||||
|
case DECODE_CDR:
|
||||||
|
cdr_open(&ai, param.filename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_audio(void) {
|
||||||
|
if (param.outmode == DECODE_AUDIO) {
|
||||||
|
audio_close(&ai);
|
||||||
|
if (audio_open(&ai) < 0) {
|
||||||
|
audio_cat->fatal() << "couldn't reopen" << endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int play_frame(struct mpstr* mp, int init, struct frame* fr) {
|
||||||
|
int clip;
|
||||||
|
long newrate;
|
||||||
|
long old_rate, old_format, old_channels;
|
||||||
|
|
||||||
|
if (fr->header_change || init) {
|
||||||
|
if (fr->header_change > 1 || init) {
|
||||||
|
old_rate = ai.rate;
|
||||||
|
old_format = ai.format;
|
||||||
|
old_channels = ai.channels;
|
||||||
|
newrate = freqs[fr->sampling_frequency]>>(param.down_sample);
|
||||||
|
fr->down_sample = param.down_sample;
|
||||||
|
audio_fit_capabilities(&ai, fr->stereo, newrate);
|
||||||
|
// check whether the fitter set our proposed rate
|
||||||
|
if (ai.rate != newrate) {
|
||||||
|
if (ai.rate == (newrate >> 1))
|
||||||
|
fr->down_sample++;
|
||||||
|
else if (ai.rate == (newrate >> 2))
|
||||||
|
fr->down_sample += 2;
|
||||||
|
else {
|
||||||
|
fr->down_sample = 3;
|
||||||
|
audio_cat->warning() << "flexable rate not heavily tested!" << endl;
|
||||||
|
}
|
||||||
|
if (fr->down_sample > 3)
|
||||||
|
fr->down_sample = 3;
|
||||||
|
}
|
||||||
|
switch (fr->down_sample) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
fr->down_sample_sblimit = SBLIMIT >> (fr->down_sample);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
long n = freqs[fr->sampling_frequency];
|
||||||
|
long m = ai.rate;
|
||||||
|
synth_ntom_set_step(n, m);
|
||||||
|
if (n>m) {
|
||||||
|
fr->down_sample_sblimit = SBLIMIT * m;
|
||||||
|
fr->down_sample_sblimit /= n;
|
||||||
|
} else
|
||||||
|
fr->down_sample_sblimit = SBLIMIT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
set_synth_functions(fr);
|
||||||
|
init_output();
|
||||||
|
if (ai.rate != old_rate || ai.channels != old_channels ||
|
||||||
|
ai.format != old_format || param.force_reopen) {
|
||||||
|
if (param.force_mono < 0) {
|
||||||
|
if (ai.channels == 1)
|
||||||
|
fr->single = 3;
|
||||||
|
else
|
||||||
|
fr->single = -1;
|
||||||
|
} else
|
||||||
|
fr->single = param.force_mono;
|
||||||
|
param.force_stereo &= ~0x2;
|
||||||
|
if (fr->single >= 0 && ai.channels == 2)
|
||||||
|
param.force_stereo |= 0x2;
|
||||||
|
set_synth_functions(fr);
|
||||||
|
init_layer3(fr->down_sample_sblimit);
|
||||||
|
reset_audio();
|
||||||
|
}
|
||||||
|
if (intflag)
|
||||||
|
return !0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fr->error_protection)
|
||||||
|
bsi.wordpointer += 2;
|
||||||
|
// do the decoding
|
||||||
|
switch (fr->lay) {
|
||||||
|
case 1:
|
||||||
|
if ((clip=do_layer1(mp, fr, param.outmode, &ai)) < 0)
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if ((clip=do_layer2(mp, fr, param.outmode, &ai)) < 0)
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if ((clip=do_layer3(mp, fr, param.outmode, &ai)) < 0)
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
clip = 0;
|
||||||
|
}
|
||||||
|
if (clip > 0 && param.checkrange)
|
||||||
|
audio_cat->warning() << clip << " samples clipped" << endl;
|
||||||
|
return !0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_file(Filename filename, unsigned char** buf,
|
||||||
|
unsigned long& slen) {
|
||||||
|
int init;
|
||||||
|
unsigned long frameNum = 0;
|
||||||
|
ostringstream out;
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
my_outstream = &out;
|
||||||
|
if (open_stream((char*)(filename.c_str()), -1)) {
|
||||||
|
long leftFrames, newFrame;
|
||||||
|
|
||||||
|
read_frame_init();
|
||||||
|
init = 1;
|
||||||
|
newFrame = param.startFrame;
|
||||||
|
leftFrames = numframes;
|
||||||
|
for (frameNum=0; read_frame(&fr) && leftFrames && !intflag; ++frameNum) {
|
||||||
|
if (frameNum < param.startFrame || (param.doublespeed &&
|
||||||
|
(frameNum % param.doublespeed))) {
|
||||||
|
if (fr.lay == 3)
|
||||||
|
set_pointer(512);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (leftFrames > 0)
|
||||||
|
--leftFrames;
|
||||||
|
if (!play_frame(&mp, init, &fr)) {
|
||||||
|
audio_cat->error() << "Error in frame #" << frameNum << endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
init = 0;
|
||||||
|
}
|
||||||
|
rd->close(rd);
|
||||||
|
if (intflag) {
|
||||||
|
intflag = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
audio_flush(param.outmode, &ai);
|
||||||
|
free(pcm_sample);
|
||||||
|
switch (param.outmode) {
|
||||||
|
case DECODE_AUDIO:
|
||||||
|
audio_close(&ai);
|
||||||
|
break;
|
||||||
|
case DECODE_WAV:
|
||||||
|
wav_close();
|
||||||
|
break;
|
||||||
|
case DECODE_AU:
|
||||||
|
au_close();
|
||||||
|
break;
|
||||||
|
case DECODE_CDR:
|
||||||
|
cdr_close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// generate output
|
||||||
|
string s = out.str();
|
||||||
|
slen = s.length();
|
||||||
|
*buf = new byte[slen];
|
||||||
|
memcpy(*buf, s.data(), slen);
|
||||||
|
my_outstream = (ostream*)0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AUDIO_USE_MIKMOD
|
||||||
|
|
||||||
|
#include "audio_mikmod_traits.h"
|
||||||
|
|
||||||
|
void AudioDestroyMp3(AudioTraits::SampleClass* sample) {
|
||||||
|
delete sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioLoadMp3(AudioTraits::SampleClass** sample,
|
||||||
|
AudioTraits::PlayingClass** state,
|
||||||
|
AudioTraits::PlayerClass** player,
|
||||||
|
AudioTraits::DeleteSampleFunc** destroy, Filename) {
|
||||||
|
audio_cat->warning() << "Mikmod doesn't support reading mp3 data yet"
|
||||||
|
<< endl;
|
||||||
|
*sample = (AudioTraits::SampleClass*)0L;
|
||||||
|
*state = (AudioTraits::PlayingClass*)0L;
|
||||||
|
*player = (AudioTraits::PlayerClass*)0L;
|
||||||
|
*destroy = AudioDestroyMp3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(AUDIO_USE_WIN32)
|
||||||
|
|
||||||
|
#include "audio_win_traits.h"
|
||||||
|
|
||||||
|
void AudioDestroyMp3(AudioTraits::SampleClass* sample) {
|
||||||
|
delete sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioLoadMp3(AudioTraits::SampleClass** sample,
|
||||||
|
AudioTraits::PlayingClass** state,
|
||||||
|
AudioTraits::PlayerClass** player,
|
||||||
|
AudioTraits::DeleteSampleFunc** destroy, Filename) {
|
||||||
|
audio_cat->warning() << "win32 doesn't support reading mp3 data yet"
|
||||||
|
<< endl;
|
||||||
|
*sample = (AudioTraits::SampleClass*)0L;
|
||||||
|
*state = (AudioTraits::PlayingClass*)0L;
|
||||||
|
*player = (AudioTraits::PlayerClass*)0L;
|
||||||
|
*destroy = AudioDestroyMp3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(AUDIO_USE_LINUX)
|
||||||
|
|
||||||
|
#include "audio_linux_traits.h"
|
||||||
|
|
||||||
|
void AudioDestroyMp3(AudioTraits::SampleClass* sample) {
|
||||||
|
delete sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioLoadMp3(AudioTraits::SampleClass** sample,
|
||||||
|
AudioTraits::PlayingClass** state,
|
||||||
|
AudioTraits::PlayerClass** player,
|
||||||
|
AudioTraits::DeleteSampleFunc** destroy, Filename filename) {
|
||||||
|
unsigned char* buf;
|
||||||
|
unsigned long len;
|
||||||
|
read_file(filename, &buf, len);
|
||||||
|
if (buf != (unsigned char*)0L) {
|
||||||
|
*sample = LinuxSample::load_raw(buf, len);
|
||||||
|
*state = ((LinuxSample*)(*sample))->get_state();
|
||||||
|
*player = LinuxPlayer::get_instance();
|
||||||
|
*destroy = AudioDestroyMp3;
|
||||||
|
} else {
|
||||||
|
*sample = (AudioTraits::SampleClass*)0L;
|
||||||
|
*state = (AudioTraits::PlayingClass*)0L;
|
||||||
|
*player = (AudioTraits::PlayerClass*)0L;
|
||||||
|
*destroy = AudioDestroyMp3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(AUDIO_USE_NULL)
|
||||||
|
|
||||||
|
#include "audio_null_traits.h"
|
||||||
|
|
||||||
|
void AudioDestroyMp3(AudioTraits::SampleClass* sample) {
|
||||||
|
delete sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioLoadMp3(AudioTraits::SampleClass** sample,
|
||||||
|
AudioTraits::PlayingClass** state,
|
||||||
|
AudioTraits::PlayerClass** player,
|
||||||
|
AudioTraits::DeleteSampleFunc** destroy, Filename) {
|
||||||
|
*sample = (AudioTraits::SampleClass*)0L;
|
||||||
|
*state = (AudioTraits::PlayingClass*)0L;
|
||||||
|
*player = (AudioTraits::PlayerClass*)0L;
|
||||||
|
*destroy = AudioDestroyMp3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error "unknown audio driver type"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ConfigureFn(audio_load_mp3) {
|
||||||
|
AudioPool::register_sample_loader("mp3", AudioLoadMp3);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user