A library which allows the playing of Wav files using the TLV320

Dependents:   RSALB_hbridge_helloworld RSALB_lobster WavPlayer_test AudioCODEC_HelloWorld

WavPlayer.cpp

Committer:
p07gbar
Date:
2012-09-21
Revision:
3:a7380cfc1987
Parent:
2:d54e6966fa2b

File content as of revision 3:a7380cfc1987:

// TODO: Add licence and stop start remembering and fill buffer functions



#include "WavPlayer.h"

#define DEBUG 0
#define debug_printf(args ...) if (DEBUG) printf(args)

#define WAVPLAYER_PLAYER_BUF 1000

#define WAVPLAYER_OUTPUT_RATE 48000

#define WAVPLAYER_READ_MARGIN 0.9

#define WAVLPAYER_SLOW_MARGIN 5

#define WAVPLAYER_GEN_TEST 0

#define WAVPLAYER_GEN_FREQ 440

#if WAVPLAYER_GEN_TEST == 1
#include "sinelookup.h"
#else
#define SINE16LENGTH 2
#define SINE32LENGTH 2
const int16_t sine16lookup[] = {0,10000};
#endif

WavPlayer* WavPlayer::instance;
//void WavPlayer::i2sisr();

WavPlayer::WavPlayer() :
        flag_play(true), i2s(I2S_TRANSMIT, p5, p6, p7), codec(p9, p10)//, ext_flag(p22), //run_flag(p23)
{
    instance = this;
    codec.power(true);                  //power up TLV apart from analogue input
    codec.stop();
    codec.frequency(WAVPLAYER_OUTPUT_RATE);            //set sample frequency
    codec.wordsize(16);  //set transfer protocol
    codec.master(false);
    codec.headphone_volume(0.6);
    //interrupt come from the I2STXFIFO only

    i2s.masterslave(I2S_MASTER);
    i2s.attach(&WavPlayer::i2sisr);

    i2s.wordsize(16);
    i2s.stereomono(I2S_MONO);

    i2s.frequency(WAVPLAYER_OUTPUT_RATE);

    //ext_flag = false;

    flag_vol = false;
    volume = 1;
}

WavPlayer::WavPlayer(FILE **fpp):flag_play(true), i2s(I2S_TRANSMIT, p5, p6, p7), codec(p9, p10)//, ext_flag(p22), //run_flag(p23)
{
    instance = this;
    codec.power(true);                  //power up TLV apart from analogue input
    codec.stop();
    codec.frequency(WAVPLAYER_OUTPUT_RATE);            //set sample frequency
    codec.wordsize(16);  //set transfer protocol
    codec.master(false);
    codec.headphone_volume(0.6);
    //interrupt come from the I2STXFIFO only

    i2s.masterslave(I2S_MASTER);
    i2s.attach(&WavPlayer::i2sisr);

    i2s.wordsize(16);
    i2s.stereomono(I2S_MONO);

    i2s.frequency(WAVPLAYER_OUTPUT_RATE);

    //ext_flag = false;

    flag_vol = false;
    volume = 1;
    open(fpp);
}

void WavPlayer::i2sisr()
{
    instance->i2sisr_();
}

void WavPlayer::i2sisr_()
{
    int to_write = i2s.max_fifo_points() - i2s.fifo_points();
    int temp[32];
    /*for(int i = 0; i < 32; i++)
     {
     temp[i] = 0;
     }*/
    int destore[2];
    if (rbuf.numberStored() > to_write + 8 && flag_play)
    {
        for (int i = 0; i < to_write; i += 2)
        {
            rbuf.readFirst(destore);
            rbuf.usedFirst();
            temp[i]   = destore[0];
            temp[i+1] = destore[1];
        }

        i2s.write(temp, to_write);
    }
    else
    {
        for (int i = 0; i < to_write; i++)
        {
            temp[i] = 0;
        }
        i2s.write(temp, to_write);
        isr_underrun++;
        
    }

}

void WavPlayer::open(FILE **fpp)
{
    filepp = fpp;
    if (*filepp == NULL)
        printf(
                "Please open a file before passing the file pointer pointer...\n\r");
    char temp[4];
    fgets_m(temp, 4, *filepp);
    debug_printf("%s\n\r", temp);
    if (strcmp(temp, "RIFF") != 0)
        printf("This is not a RIFF file\n\r");
    fseekread(*filepp, temp, 8, 4);
    if (strcmp(temp, "WAVE") != 0)
        printf("This is not a WAVE file\n\r");
    
    current_time = 0;
    
    getConfig();
}

int WavPlayer::getConfig()
{
    char temp[4];
    clear(temp, 4);
    fseekread(*filepp, temp, 4, 4);
    config.file_size = getu32(temp, 4);
    debug_printf("FileSize = %i\n\r", config.file_size);

    int chunkstart = findChunk(*filepp, "fmt ", 4, config.file_size, 12);

    if (chunkstart != -1)
    {

        config.format_tag = fsru16(*filepp, chunkstart + 8);
        debug_printf("Format_tag:            %i\n\r", config.format_tag);

        config.channels = fsru16(*filepp, chunkstart + 10);
        debug_printf("nChannels:            %i\n\r", config.channels);

        config.samples_per_sec = fsru32(*filepp, chunkstart + 12);
        debug_printf("nSamplesPerSec:        %i\n\r", config.samples_per_sec);

        config.avg_bytes_per_sec = fsru32(*filepp, chunkstart + 16);
        debug_printf("avg_bytes_per_sec:    %i\n\r", config.avg_bytes_per_sec);

        config.block_align = fsru16(*filepp, chunkstart + 20);
        debug_printf("block_align:            %i\n\r", config.block_align);

        config.bits_per_sample = fsru16(*filepp, chunkstart + 22);
        debug_printf(
                "bits per sample:            %i\n\r", config.bits_per_sample);

    }

    chunkstart = findChunk(*filepp, "data", 4, config.file_size, 12);

    if (chunkstart != -1)
    {
        config.data_length = fsru32(*filepp, chunkstart + 4);
        debug_printf("Data Length:            %i\n\r", config.data_length);
    }
    return 0;

}

float WavPlayer::play()
{
    return play(float(config.data_length) / float(config.samples_per_sec));

}

float WavPlayer::play(float time)
{
    return play(current_time, time);
}

float WavPlayer::play(float start, float timefor)
{

    #if WAVPLAYER_GEN_TEST == 1
    config.channels = 1;    
    config.samples_per_sec = 48000;
    #endif
    
    float timeElapsed = 0;
    int endPoint = int((start + timefor) * float(config.samples_per_sec));
    if (endPoint * config.block_align >= config.data_length)
        endPoint = config.data_length / config.block_align;
    //printf("Will end after: %i samples \n\r", endPoint);
    int point = int(start * float(config.samples_per_sec));
    bool valid = true;
    flag_play = false;
    int dataOffset = findChunk(*filepp, "data", 4, config.file_size, 12) + 8 + (point * config.block_align);

    double timeComp = 0;

    double out_rate_t_p = 1 / double(WAVPLAYER_OUTPUT_RATE);
    //printf("out_rate_t_p = %f\n\r", out_rate_t_p);
    double out_rate_t_n = -1 / double(WAVPLAYER_OUTPUT_RATE);
    double in_rate = 1 / double(config.samples_per_sec); //0.0000125
    //printf("in_rate = %f\n\r", in_rate);
    
    float phase = 0;

    if (dataOffset != -1)
    {
        fseek(*filepp, dataOffset, SEEK_SET);

        int16_t buffer[WAVPLAYER_PLAYER_BUF];

        codec.start();
        //debug_printf("Expecting isr\n\r");

        i2s.frequency(WAVPLAYER_OUTPUT_RATE);


        if (config.channels == 1)
        {
            stereo = false;
            i2s.stereomono(I2S_MONO);
        }
        else
        {
            stereo =true;
            i2s.stereomono(I2S_STEREO);
        }

        i2s.attach(&WavPlayer::i2sisr);
        i2s.start();
        flag_play = true;

        while (valid && point < endPoint)
        {
            if (rbuf.numberStored() < RBLENGTH - 10)
            {

                //timer.reset();
                //timer.start();
                int numToread = WAVPLAYER_PLAYER_BUF;
                if (numToread > (RBLENGTH - 10) - rbuf.numberStored())
                {
                    numToread = (RBLENGTH - 10) - rbuf.numberStored();
                }
                if (numToread + (point * (config.block_align/config.channels)) > config.data_length)
                {
                    numToread = config.data_length - point * config.block_align;
                    valid = false;
                    debug_printf("EOF detected\n\r");
                }
                if(numToread % 2 == 1) numToread--;
                //printf("Generating\n\r");
                //run_flag = true;
                //phase = sine_gen(buffer,numToread,float(config.samples_per_sec)/120,phase);
                timer.reset();
                timer.start();
                //fread(buffer, config.block_align/config.channels, numToread, *filepp);
                 #if WAVPLAYER_GEN_TEST == 1
                    phase = sine_gen(buffer,numToread,float(config.samples_per_sec)/WAVPLAYER_GEN_FREQ,phase);  
                #else
                    fread(buffer, config.block_align/config.channels, numToread, *filepp);
                #endif
                timer.stop();
                //printf("numToread:%f\n\r",float(numToread));
                if(numToread>=10)
                {
                    read_time += (timer.read()/float(numToread));
                    read_time_div += 1;
                }
                
                
                if(read_time/float(read_time_div) >= WAVPLAYER_READ_MARGIN / float(config.samples_per_sec*config.channels))
                {
                    slow_count++;
                    if(slow_count > WAVLPAYER_SLOW_MARGIN)
                    {
                        printf("Data rates not high enough to sustain read...%f seconds per read\n\r", read_time/float(read_time_div));
                        valid = false;
                        break;
                    }

                }
                
                //run_flag = false;
                int storer[2];
                for (int i = 0; i < numToread; i += 2)
                {

                    //run_flag = true;

                    if (timeComp < out_rate_t_p && timeComp > out_rate_t_n)
                    {
                        storer[0] = (int) buffer[i];
                        storer[1] = (int) buffer[i + 1];

                        //storer[0] = blip;
                        //storer[1] = blip;
                        timeComp += in_rate;
                        while (rbuf.numberStored() > RBLENGTH - 10)
                        {
                            //run_flag = true;
                            //run_flag = false;
                        }
                        rbuf.addToBuffer(storer);
                        timeComp -= out_rate_t_p;
                        //printf(",",timeComp);
                        point++;
                        //printf("straight %f\n\r",timeComp);
                    }
                    else if (timeComp >= out_rate_t_p)
                    {

                        storer[0] = (int) buffer[i];
                        storer[1] = (int) buffer[i + 1];

                        //storer[0] = blip;
                        //storer[1] = blip;

                        timeComp += in_rate;
                        /*const float ct = 5;
                         float c = 0;
                         float bufi = float(buffer[i]);
                         float bufip = float((i+1 >= numToread) ? buffer[i+1] : bufi);*/

                        while (timeComp >= out_rate_t_p)
                        {
                            //storer[0] = int((((ct-c)/ct)*bufi)+((c/ct)*bufip));
                            while (rbuf.numberStored() > RBLENGTH - 10)
                            {
                                //run_flag = true;
                                //run_flag = false;
                            }
                            rbuf.addToBuffer(storer);
                            timeComp -= out_rate_t_p;
                            //printf(";");
                            //if(c < ct) c++;
                        }
                        point++;

                    }
                    else if (timeComp <= out_rate_t_n)
                    {
                        timeComp += in_rate;
                        //printf("-");
                    }

                    //storer[1] = 0;
                    //if(i%1 == 0) printf("%i\n",int(storer[0]),int(storer[1]));
                    //run_flag = false;

                }
                flag_play = true;
                //timer.stop();
                //printf("point: %i\n\r",isr_underrun);
                //read_time += timer;
                //read_time_div =numToread;

            }
        }
    }
    flag_play = false;
    current_time = float(point)/float(config.samples_per_sec);
    return current_time;
}

int WavPlayer::fseekread(FILE *fp, char* str, int offset, int len)
{
    fseek(fp, offset, SEEK_SET);
    fgets_m(str, len, fp);
    return len;
}

uint32_t WavPlayer::getu32(char str[], int len)
{
    uint32_t temp = 0;
    for (int i = 0; i < len; i++)
    {
        temp += str[i] << (i * 8);

    }
    return temp;
}

uint16_t WavPlayer::getu16(char str[], int len)
{
    uint16_t temp = 0;
    for (int i = 0; i < len; i++)
    {
        temp += str[i] << (i * 8);
    }
    return temp;
}

int32_t WavPlayer::get32(char str[], int len)
{
    uint32_t temp = 0;
    for (int i = 0; i < len; i++)
    {
        temp += str[i] << (i * 8);

    }
    return temp;
}

int16_t WavPlayer::get16(char str[], int len)
{
    uint16_t temp = 0;
    for (int i = 0; i < len; i++)
    {
        temp += str[i] << (i * 8);
    }
    return temp;
}

uint32_t WavPlayer::fsru32(FILE *fp, int offset, int len)
{
    char temp[4];
    fseekread(fp, temp, offset, len);
    return getu32(temp, len);
}

uint16_t WavPlayer::fsru16(FILE *fp, int offset, int len)
{
    char temp[2];
    fseekread(fp, temp, offset, len);
    return getu16(temp, len);
}

void WavPlayer::clear(char* str, int len)
{
    for (int i = 0; i < len; i++)
    {
        str[i] = 0;
    }
}

int WavPlayer::findChunk(FILE *fp, char* match, int len, int fileSize,
        int startOffset)
{
    char temp[5];
    int count = startOffset;
    while ((count + 8) < fileSize)
    {
        clear(temp, 5);
        fseekread(fp, temp, count, 4);
        int chunksize = fsru32(fp, count + 4) + 8;
        //debug_printf("@ %i Chunk Name: %s Size: %i\n\r", count, temp, chunksize);

        if (strcmp(temp, match) == 0)
        {
            return count;
        }
        count += chunksize;
    }
    return -1;
}

int WavPlayer::findChunk(FILE *fp, char* match, int len, int startOffset)
{
    return findChunk(fp, match, len, config.file_size, startOffset);
}

void WavPlayer::fgets_m(char* str, int num, FILE* fp)
{
    for (int i = 0; i < num; i++)
    {
        str[i] = fgetc(fp);
        //printf("%c",str[i]);
    }
}

float WavPlayer::sine_gen(int16_t* buf, int len, float div, float phase)
{
    #if WAVPLAYER_GEN_TEST == 1
    float t = SINE16LENGTH / div;
    for (int i = 0; i < len; i++)
    {
        buf[i] = sine16lookup[int(phase)];
        //printf("%i\n\r",buf[i]);
        phase += t;
        while (phase >= SINE16LENGTH)
            phase -= SINE16LENGTH;
    }
    #endif
    return phase;
}