mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 00:32:57 -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