1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-11-24 14:20:19 +01:00
Files
audacity/lib-src/sbsms/src/mp3.cpp
2010-01-24 09:19:39 +00:00

248 lines
5.7 KiB
C++

# include <stdio.h>
//# include <unistd.h>
# include <sys/stat.h>
#include "mp3tech.h"
#include "audio.h"
#include "import.h"
#include "audiobuffer.h"
#include <iostream>
#include <fstream>
#include "mp3.h"
namespace _sbsms_ {
#define MP3_INPUT_BUFFER_SIZE 4096
#define MP3_OUTPUT_BUFFER_SIZE 4096
#define MP3_OUTPUT_OVERFLOW_BUFFER_SIZE 4096
#define MP3_AUDIO_BUFFER_SIZE 65536
using namespace std;
static
enum mad_flow input(void *_data,
struct mad_stream *stream)
{
MP3Reader *data = (MP3Reader*) _data;
long input_buffer_size = MP3_INPUT_BUFFER_SIZE;
if(data->file->eof()) {
data->rb->writingComplete();
return MAD_FLOW_STOP;
}
/* "Each time you refill your buffer, you need to preserve the data in
* your existing buffer from stream.next_frame to the end.
*
* This usually amounts to calling memmove() on this unconsumed portion
* of the buffer and appending new data after it, before calling
* mad_stream_buffer()"
* -- Rob Leslie, on the mad-dev mailing list */
unsigned int unconsumedBytes;
if(stream->next_frame) {
unconsumedBytes = data->inputBuffer + input_buffer_size - stream->next_frame;
memmove(data->inputBuffer, stream->next_frame, unconsumedBytes);
}
else
unconsumedBytes = 0;
//use read instead of readsome so eof is reached
long start = data->file->tellg();
data->file->read((char*)data->inputBuffer + unconsumedBytes,
(input_buffer_size - unconsumedBytes));
long end = data->file->tellg();
long read = end - start;
mad_stream_buffer(stream, data->inputBuffer, read + unconsumedBytes);
return MAD_FLOW_CONTINUE;
}
inline float scale(mad_fixed_t sample)
{
return (float) (sample / (float) (1L << MAD_F_FRACBITS));
}
static long copy_mad_audio(float *out, struct mad_pcm *pcm, unsigned int nsamples)
{
unsigned int nchannels = pcm->channels;
mad_fixed_t *ch;
ch = pcm->samples[0];
for(unsigned int k = 0;k<nsamples;k++) {
out[2*k] = scale( *(ch++) );
}
if(nchannels==2) {
ch = pcm->samples[1];
for(unsigned int k = 0;k<nsamples;k++) {
out[2*k+1] = scale( *(ch++) );
}
} else {
ch = pcm->samples[0];
for(unsigned int k = 0;k<nsamples;k++) {
out[2*k+1] = scale( *(ch++) );
}
}
return nsamples;
}
/*
* This is the output callback function. It is called after each frame of
* MPEG audio data has been completely decoded. The purpose of this callback
* is to output (or play) the decoded PCM audio.
*/
static
enum mad_flow output(void *_data,
struct mad_header const *header,
struct mad_pcm *pcm)
{
MP3Reader *data = (MP3Reader*) _data;
data->channels = pcm->channels;
audio_in_cb cb = data->cb;
unsigned int nsamples = pcm->length;
float *out = data->getOutputBuffer(nsamples);
copy_mad_audio(out,pcm,nsamples);
if(cb) {
cb(out,nsamples,pcm->samplerate,data->data);
}
return MAD_FLOW_CONTINUE;
}
/*
* This is the error callback function. It is called whenever a decoding
* error occurs. The error is indicated by stream->error; the list of
* possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)
* header file.
*/
static
enum mad_flow error(void *_data,
struct mad_stream *stream,
struct mad_frame *frame)
{
fprintf(stderr, "decoding error 0x%04x (%s) at stream frame %u\n",
stream->error, mad_stream_errorstr(stream),
(unsigned int)stream->this_frame );
/* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */
return MAD_FLOW_CONTINUE;
}
/*
* It instantiates a decoder object and configures it with the input,
* output, and error callback functions above. A single call to
* mad_decoder_run() continues until a callback function returns
* MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and
* signal an error).
*/
int read_cb(float *buf, long n, int Fs, void *data)
{
MP3Reader *decoder = (MP3Reader*)data;
return decoder->rb->write(buf,n);
}
void *mp3importThreadCB(void *data)
{
/* start decoding */
MP3Reader *reader = (MP3Reader*)data;
mad_decoder_run(&(reader->decoder), MAD_DECODER_MODE_SYNC);
reader->bDone = true;
return NULL;
}
long MP3Reader :: read(float *outputBuffer, long block_size)
{
// on first call, start the decoder
if(bFirst) {
bFirst = false;
this->data = this;
this->cb = read_cb;
pthread_create(&importThread, NULL, mp3importThreadCB, this);
}
return rb->read(outputBuffer, block_size);
}
float* MP3Reader :: getOutputBuffer(long nsamples)
{
return outputBuffer;
}
bool MP3Reader :: done()
{
return bDone;
}
bool MP3Reader :: isError()
{
return bError;
}
MP3Reader :: MP3Reader(const char *filename)
{
bError = false;
file = new ifstream();
file->open(filename, ios::in | ios::binary);
if (!file->is_open()) {
bError = true;
}
mp3info mp3;
mp3.file = fopen(filename,"rb");
mp3.filename = filename;
get_mp3_info(&mp3);
this->samples = mp3.samples;
this->sampleRate = mp3.sample_rate;
fclose(mp3.file);
this->n_done = 0;
this->bFirst = true;
this->bDone = false;
/* configure input, output, and error functions */
mad_decoder_init(&decoder, this,
input, 0 /* header */, 0 /* filter */, output,
error, 0 /* message */);
this->outputBuffer = new float[MP3_OUTPUT_BUFFER_SIZE<<1];
this->inputBuffer = new unsigned char [MP3_INPUT_BUFFER_SIZE];
this->rb = new AudioBuffer(MP3_AUDIO_BUFFER_SIZE,2);
}
long MP3Reader :: getFrames()
{
return samples;
}
int MP3Reader :: getSampleRate()
{
return sampleRate;
}
int MP3Reader :: getChannels()
{
return 2;
}
MP3Reader :: ~MP3Reader()
{
file->close();
delete file;
mad_decoder_finish(&decoder);
delete outputBuffer;
delete inputBuffer;
delete rb;
}
}