monome / aleph

open source sound computer
Other
80 stars 39 forks source link

load bfin audio buffers via SPI #62

Open catfact opened 11 years ago

catfact commented 11 years ago

extend SPI protocol for reading audio wavefiles to bfin SDRAM.

electropourlaction commented 9 years ago

currently reading raw audio files as parameter changes to SDRAM, it works but it's kinda slow, about 4k/second I think. I'm wondering if it is possible to temporarily (during startup) increase the speed of the parameter changes? Or does this need another solution altogether?

catfact commented 9 years ago

not sure what you';re doing exactly (can you link to code?) but i wouldn't use the param change framework per se. i would extend the SPI protocol to allow arbitrarily sized block transfers and define a separate command in the protocol header: https://github.com/electropourlaction/aleph/blob/master/common/protocol.h#L115-L126

it is theoretically possible to change the SPI transfer block size on the fly, though we are not doing that right now. at one point in early development we did switch bfin SPI modes, from master to slave, during codec intialization, and it worked fine. i would do all large transfers at a specific stage in module initialization, before processing audio.

avr32 spi setup: https://github.com/tehn/aleph/blob/master/avr32_lib/src/init.c#L256-L257 )

bfin spi setup: https://github.com/electropourlaction/aleph/blob/master/bfin_lib/src/init.c#L99

catfact commented 9 years ago

to clarify, i would make the process something like:

then the bfin RX state machine can be made pretty efficient.

electropourlaction commented 9 years ago

I have tried using a separate command, it failed however on writing the data to the SDRAM buffer. This is the current solution using parameter changes, see custom callback:

https://github.com/electropourlaction/aleph/blob/dev/apps/prgm/src/app_timers.c#L99-116

The speed is set to 1:

https://github.com/electropourlaction/aleph/blob/dev/apps/prgm/src/app_timers.c#L121

catfact commented 8 years ago

i'm still wondering why this didn't work for you. you say it failed on writing data to the SDRAM buffer - how so? SDRAM writes are a little slow, but shouldn't be so slow that it can't keep up with the avr32.

i just think it should be more efficient to send the audio bytes directly rather than wrapping each one in a param change. (or rahter, two param changes.)

electropourlaction commented 8 years ago

I got the data thru the spi process with a separate command, did a test by sending it to out[0] directly in the spi process (it came out distorted so probably not in sync with the audio processing, but something came thru at least), where I failed was at storing the data at a specific address in SDRAM, I´m just learning these things so I haven't looked at if there could be some kind of interrupt or pin state requirement in this type of command, or maybe I did not get the addressing part right...

electropourlaction commented 7 years ago

managed to move sample transfer outside of the timer/parameter loop now, just don't know what I was doing a year ago :)

also started to look at the inevitable next step, stream/load with tiny buffers from file to SDRAM.

not aiming at audio rate streaming but to load up to 64MB samples, and to switch patterns faster by loading them "in the background" instead of stopping up to handle memory allocation etc. (atm first step on a new pattern will get a short, sometimes jon bonham-ish groovy, delay when the external clock is over ~120 bpm).

starting up by looking at the newly merged monome/avr32lib.

electropourlaction commented 7 years ago

first attempt without a temporary buffer, it's working but not as fast as I'm hoping for, will keep digging for ways to improve the speed.

void files_load_sample(u8 n) { // pointer to file, size void *fp; u32 size = 0; ParamValueSwap s;

delay_ms(10);

fp = fl_fopen(sample_filepath(n-7), "r");
print_dbg("\r\n sample[n]->num ");
print_dbg_ulong(sample[n]->num);
print_dbg("\r\n sample_filepath(n) ");
print_dbg(sample_filepath(n-7));

size = ((FL_FILE*)(fp))->filelength;
sample[n]->loop = size / sizeof(s32);
bfinMemCheck += sample[n]->loop;

//  check SDRAM size limit
if (bfinMemCheck < SAMPLES_SIZE)
{
    //  check sample size limit
    if (size < SAMPLE_SIZE)
    {
        sample[n+1]->offset = sample[n]->offset + sample[n]->loop;
        //  send sample loop point to bfin
        ctl_param_change(n+1, eParamSampleLoopPoint, sample[n+1]->offset);

        if (fp != NULL)
        {
            //  transfer sample
            render_boot(sample_name[smpl-N_BUFFERS]);

            idx8 = 0;

            app_pause();

            bfin_sample_start(sample[n]->offset);

            while (idx8 < size)
            {
                s.asByte[0] = fl_fgetc(fp);
                s.asByte[1] = fl_fgetc(fp);
                s.asByte[2] = fl_fgetc(fp);
                s.asByte[3] = fl_fgetc(fp);

                bfin_sample(s.asInt);

                idx8 += 4;
            }

            bfin_sample_end();

            fl_fclose(fp);

            app_resume();
        }
        else
        {
            render_boot("sample file error");
            fl_fclose(fp);
        }
    }
    else
    {
        render_boot("sample size limit error");
        fl_fclose(fp);
    }
}
else
{
    render_boot("SDRAM size limit error");
    smpl = N_OFFSETS + 1;
    fl_fclose(fp);
}

}

electropourlaction commented 7 years ago

test w buffer. 28kb/s

void files_load_sample(u8 n) { void *fp; uint8_t sector[512]; uint32_t sread, i; uint32_t ssample; ParamValueSwap s;

delay_ms(10);

fp = fl_fopen(sample_filepath(n-7), "r");
ssample = ((FL_FILE*)(fp))->filelength;
sample[n]->loop = ssample / sizeof(s32);
bfinMemCheck += sample[n]->loop;

//  check SDRAM size limit
if (bfinMemCheck < SAMPLES_SIZE)
{
    sample[n+1]->offset = sample[n]->offset + sample[n]->loop;

    //  send sample loop point to bfin
    ctl_param_change(n+1, eParamSampleLoopPoint, sample[n+1]->offset);

    if (fp != NULL)
    {
        //  transfer sample
        render_boot(sample_name[smpl-N_BUFFERS]);

        app_pause();

        bfin_sample_start(sample[n]->offset);

        do
        {
            if (ssample < 512)
            {
                sread = ssample;
            }
            else
            {
                sread = 512;
            }
            ssample -= sread;

            fl_fread(&sector, sread, 1, fp);

            for (i = 0; i < sread; i += 4)
            {
                s.asByte[0] = sector[i];
                s.asByte[1] = sector[i + 1];
                s.asByte[2] = sector[i + 2];
                s.asByte[3] = sector[i + 3];

                bfin_sample(s.asInt);
            }
        }
        while (ssample > 0);

        bfin_sample_end();

        fl_fclose(fp);

        app_resume();
    }
    else
    {
        render_boot("sample file error");
        fl_fclose(fp);
    }
}
else
{
    render_boot("SDRAM size limit error");
    smpl = N_OFFSETS + 1;
    fl_fclose(fp);
}

}

electropourlaction commented 7 years ago

test w/o buffer, tweaked. 70kb/s. this without the spi transfer takes about 3 s, w spi transfer 22 seconds (for my test samples), thus leaving the card/pdca side of it for now to look at the spi some more.. !

void files_load_sample(u8 n) { void *fp; u32 fsize, foffset; uint32_t chunk;

delay_ms(10);

fp = fl_fopen(sample_filepath(n-7), "r");
fsize = ((FL_FILE*)(fp))->filelength;
foffset = ((FL_FILE*)(fp))->bytenum % FAT_SECTOR_SIZE;

sample[n]->loop = fsize / sizeof(s32);
bfinMemCheck += sample[n]->loop;

//  check SDRAM size limit
if (bfinMemCheck < SAMPLES_SIZE)
{
    sample[n+1]->offset = sample[n]->offset + sample[n]->loop;

    //  send sample loop point to bfin
    ctl_param_change(n+1, eParamSampleLoopPoint, sample[n+1]->offset);

    //  transfer sample
    render_boot(sample_name[smpl-N_BUFFERS]);

    app_pause();

    bfin_sample_start(sample[n]->offset);

    do
    {
        if (fsize < FAT_SECTOR_SIZE)
        {
            chunk = fsize;
        }
        else
        {
            chunk = FAT_SECTOR_SIZE;
        }
        fsize -= chunk;

        bfin_sample_transfer(fl_return_sector(fp, foffset), chunk);

        foffset += 1;
        ((FL_FILE*)(fp))->bytenum += chunk;
    }
    while (fsize > 0);

    bfin_sample_end();

    fl_fclose(fp);

    app_resume();
}
else
{
    render_boot("SDRAM size limit error");
    smpl = N_OFFSETS + 1;
}

}

int bfin_sample_transfer(unsigned long sector, unsigned long bytes) { unsigned long i; uint32_t s;

if (sd_mmc_spi_read_open_PDCA(sector))
{
    pdca_load_channel(AVR32_PDCA_CHANNEL_SPI_RX, &pdcaRxBuf, bytes);
    pdca_load_channel(AVR32_PDCA_CHANNEL_SPI_TX, (void *)&pdcaTxBuf, bytes); //send dummy to activate the clock

    spi_write(SD_MMC_SPI, 0xFF);

    pdca_enable_interrupt_transfer_complete(AVR32_PDCA_CHANNEL_SPI_RX);

    pdca_enable(AVR32_PDCA_CHANNEL_SPI_RX);
    pdca_enable(AVR32_PDCA_CHANNEL_SPI_TX);

    //  wait for signal from ISR (irq_pdca)
    while (!(pdcaRxChan->isr & AVR32_PDCA_ISR_TRC_MASK));

    //  spi transfer
    for (i=0; i<bytes; i+=4)
    {
        s = 0;
        s |= pdcaRxBuf[i] << 24;
        s |= pdcaRxBuf[i + 1] << 16;
        s |= pdcaRxBuf[i + 2] << 8;
        s |= pdcaRxBuf[i + 3];

        bfin_sample(s);
    }
}
return 1;

}

electropourlaction commented 7 years ago

have tried setting the spi .trans_delay down to 1 instead of 20, seems to be working, what was the purpose of setting it so high? too limit it to one transfer per sample?

edit. will dig some more when time permits and try to get bi-directional transfer going, the goal is realtime bounce to card (not sure if I'm gonna succeed though!).

some thoughts so far. try bfin as spi master during run, driving the clock at 'audio rate' sending samples on miso, and at the same time getting parameter changes from the avr32 on mosi, the avr32 will also need a function to handle/buffer the audio samples and save them to a file.