diff --git a/panda/src/audiotraits/audio_load_mp3.cxx b/panda/src/audiotraits/audio_load_mp3.cxx index e69de29bb2..52a93b508f 100644 --- a/panda/src/audiotraits/audio_load_mp3.cxx +++ b/panda/src/audiotraits/audio_load_mp3.cxx @@ -0,0 +1,481 @@ +// Filename: audio_load_mp3.cxx +// Created by: cary (11Oct00) +// +//////////////////////////////////////////////////////////////////// + +#include +#include "audio_pool.h" +#include "config_audio.h" +#include "audio_trait.h" + +Configure(audio_load_mp3); + +extern "C" { +#include +} + +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; ifatal() << "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); +}