225 lines
4.2 KiB
C
225 lines
4.2 KiB
C
#include <mpg123.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#include "mp3.h"
|
||
#include "playback.h"
|
||
|
||
static size_t* buffSize;
|
||
static mpg123_handle *mh = NULL;
|
||
static uint32_t rate;
|
||
static uint8_t channels;
|
||
|
||
static int initMp3(const char* file);
|
||
static uint32_t rateMp3(void);
|
||
static uint8_t channelMp3(void);
|
||
static uint64_t decodeMp3(void* buffer);
|
||
static void exitMp3(void);
|
||
static size_t getFileSamplesMp3(void);
|
||
|
||
/**
|
||
* Set decoder parameters for MP3.
|
||
*
|
||
* \param decoder Structure to store parameters.
|
||
*/
|
||
void setMp3(struct decoder_fn* decoder)
|
||
{
|
||
decoder->init = &initMp3;
|
||
decoder->rate = &rateMp3;
|
||
decoder->channels = &channelMp3;
|
||
/*
|
||
* buffSize changes depending on input file. So we set buffSize later when
|
||
* decoder is initialised.
|
||
*/
|
||
buffSize = &(decoder->buffSize);
|
||
decoder->decode = &decodeMp3;
|
||
decoder->exit = &exitMp3;
|
||
decoder->getFileSamples = &getFileSamplesMp3;
|
||
}
|
||
|
||
static size_t getFileSamplesMp3(void)
|
||
{
|
||
off_t len = mpg123_length(mh);
|
||
if(len == MPG123_ERR)
|
||
return 0;
|
||
|
||
return len * (size_t)channels;
|
||
}
|
||
|
||
/**
|
||
* Initialise MP3 decoder.
|
||
*
|
||
* \param file Location of MP3 file to play.
|
||
* \return 0 on success, else failure.
|
||
*/
|
||
int initMp3(const char* file)
|
||
{
|
||
int err = 0;
|
||
int encoding = 0;
|
||
|
||
if((err = mpg123_init()) != MPG123_OK)
|
||
return err;
|
||
|
||
if((mh = mpg123_new(NULL, &err)) == NULL)
|
||
{
|
||
printf("Error: %s\n", mpg123_plain_strerror(err));
|
||
return err;
|
||
}
|
||
|
||
if(mpg123_open(mh, file) != MPG123_OK ||
|
||
mpg123_getformat(mh, (long *) &rate, (int *) &channels, &encoding) != MPG123_OK)
|
||
{
|
||
printf("Trouble with mpg123: %s\n", mpg123_strerror(mh));
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* Ensure that this output format will not change (it might, when we allow
|
||
* it).
|
||
*/
|
||
mpg123_format_none(mh);
|
||
mpg123_format(mh, rate, channels, encoding);
|
||
|
||
/*
|
||
* Buffer could be almost any size here, mpg123_outblock() is just some
|
||
* recommendation. The size should be a multiple of the PCM frame size.
|
||
*/
|
||
*buffSize = mpg123_outblock(mh) * 16;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* Get sampling rate of MP3 file.
|
||
*
|
||
* \return Sampling rate.
|
||
*/
|
||
uint32_t rateMp3(void)
|
||
{
|
||
return rate;
|
||
}
|
||
|
||
/**
|
||
* Get number of channels of MP3 file.
|
||
*
|
||
* \return Number of channels for opened file.
|
||
*/
|
||
uint8_t channelMp3(void)
|
||
{
|
||
return channels;
|
||
}
|
||
|
||
/**
|
||
* Decode part of open MP3 file.
|
||
*
|
||
* \param buffer Decoded output.
|
||
* \return Samples read for each channel.
|
||
*/
|
||
uint64_t decodeMp3(void* buffer)
|
||
{
|
||
size_t done = 0;
|
||
mpg123_read(mh, buffer, *buffSize, &done);
|
||
return done / (sizeof(int16_t));
|
||
}
|
||
|
||
/**
|
||
* Free MP3 decoder.
|
||
*/
|
||
void exitMp3(void)
|
||
{
|
||
mpg123_close(mh);
|
||
mpg123_delete(mh);
|
||
mpg123_exit();
|
||
}
|
||
|
||
/**
|
||
* Check if a file is a valid MP3 file.
|
||
*
|
||
* \param path Path to the MP3 file.
|
||
* \return 0 if valid MP3, 1 if not valid.
|
||
*/
|
||
int isMp3(const char *path)
|
||
{
|
||
#if 0
|
||
int err;
|
||
int result = 1;
|
||
mpg123_handle *mh = NULL;
|
||
long rate;
|
||
int channels, encoding;
|
||
|
||
// Initialise the library
|
||
if (mpg123_init() != MPG123_OK)
|
||
goto out;
|
||
|
||
// Create a decoder handle
|
||
mh = mpg123_new(NULL, &err);
|
||
if (!mh)
|
||
goto exit_init;
|
||
|
||
// skip ID3v2 tags rather than parsing them (so tag-only files don’t count as valid mp3)
|
||
mpg123_param(mh, MPG123_SKIP_ID3V2, 1, 0);
|
||
|
||
// limit how many bytes to scan for a frame sync (e.g. 2048 bytes)
|
||
mpg123_param(mh, MPG123_RESYNC_LIMIT, 2048, 0);
|
||
|
||
// Try opening the file
|
||
err = mpg123_open(mh, path);
|
||
if (err != MPG123_OK)
|
||
goto exit_handle;
|
||
|
||
// Query the decoded format
|
||
if (mpg123_getformat(mh, &rate, &channels, &encoding) != MPG123_OK)
|
||
goto close_handle;
|
||
|
||
// Parse first frame in file
|
||
err = mpg123_framebyframe_next(mh);
|
||
if (err != MPG123_OK)
|
||
{
|
||
// If we can't read the first frame, it's not a valid MP3
|
||
goto close_handle;
|
||
}
|
||
|
||
// All checks passed: valid MP3
|
||
result = 0;
|
||
|
||
close_handle:
|
||
mpg123_close(mh);
|
||
|
||
exit_handle:
|
||
mpg123_delete(mh);
|
||
|
||
exit_init:
|
||
mpg123_exit();
|
||
|
||
out:
|
||
return result;
|
||
#else
|
||
unsigned char buf[4];
|
||
FILE *f = fopen(path, "rb");
|
||
int ret = 1;
|
||
|
||
if(!f) return 1;
|
||
|
||
if(fread(buf, 1, 4, f) < 4)
|
||
goto out;
|
||
|
||
// ID3v2 tag?
|
||
if(buf[0]=='I' && buf[1]=='D' && buf[2]=='3') {
|
||
ret = 0;
|
||
goto out;
|
||
}
|
||
|
||
// MPEG frame sync: 11 one-bits in a row
|
||
if(buf[0]==0xFF && (buf[1]&0xE0)==0xE0) {
|
||
ret = 0;
|
||
goto out;
|
||
}
|
||
|
||
out:
|
||
fclose(f);
|
||
return ret;
|
||
#endif
|
||
}
|
||
|