In order to make the fountain video I needed to take an mp3 file and get its spectrum data to an ascii file. This data would then be used as the input to the simulation.

The code below will take an "INPUT.mp3" and output to "OUTPUT.txt" the rate at which the data is collected (fps) can be controlled along with the resolution of the data (BANDS).

The code was written using the writewav.c and spectrum.c examples in the BASS sdk. The library is free for non-commercial use and is availible for Windows, Mac OS, Linux, Win64, WinCE, iOS, Android, and ARM

//Written by Hammad Mazhar
//2012 hamelot.co.uk
//

#include <stdlib.h>
#include <stdio.h>
#include "bass.h"

#ifdef _WIN32 // Windows
#include <conio.h>
#else // OSX
#include <sys/types.h>
#include <sys/time.h>
#include <termios.h>
#include <string.h>
#endif

FILE *output_data;
char *output_name = "OUTPUT.txt";
char *input_name = "INPUT.mp3";
double fps = 60;    //how many samples should be taken per second
int BANDS = 120;    //how many "bins" to devide the spectum in (also the number of columns on each row of output)

void main(int argc, char **argv)
{
    DWORD chan, p;
    QWORD pos;
    output_data = fopen(output_name, "w");
    printf("BASS Spectrum writer example : MOD/MPx/OGG -> FILE.TXT\n-------------------------------------------------\n");
    BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 0);    // no audio output, therefore no update period needed
    BASS_Init(0, 44100, 0, 0, NULL);                // null device, 44100hz, stereo, 16 bits
    chan = BASS_StreamCreateFile(FALSE, input_name, 0, 0, BASS_STREAM_DECODE);  //streaming the file
    pos = BASS_ChannelGetLength(chan, BASS_POS_BYTE);
    printf("streaming file [%llu bytes]", pos);
    p = (DWORD) BASS_ChannelBytes2Seconds(chan, pos);  //length of file in seconds
    printf(" %u:%02u\n", p / 60, p % 60);
    float time = 0;

    while (BASS_ChannelIsActive(chan)) {
        long byte_pos = BASS_ChannelSeconds2Bytes(chan, time);
        BASS_ChannelSetPosition(chan, byte_pos, BASS_POS_BYTE);
        float fft[1024];
        int b0 = 0;
        BASS_ChannelGetData(chan, fft, BASS_DATA_FFT2048);  //get the fft data, in this case there are 2048 samples

        //binning the fft, modified from bass spectum.c example in sdk.
        for (int i = 0; i < BANDS; i++) {
            float peak = 0;
            int b1 = pow(2, i * 10.0 / (BANDS - 1)); //determine size of the bin

            if (b1 > 1023) {b1 = 1023;} //upper bound on bin size

            if (b1 <= b0) {b1 = b0 + 1;} //make sure atleast one bin is used

            //loop over every bin
            for (; b0 < b1; b0++) {
                if (peak < fft[1 + b0]) {peak = fft[1 + b0];}
            }
            //write each column to file
            fprintf(output_data, "%f,", sqrt(peak));
        }
        //endline after every row
        fprintf(output_data, "\n");
        pos = BASS_ChannelGetPosition(chan, BASS_POS_BYTE);
        p = (DWORD) BASS_ChannelBytes2Seconds(chan, pos);
        printf(" %u:%02u\n", p / 60, p % 60);  //print current time
        time += 1 / fps;   //increment time
    }

    printf("DONE!");
    fclose(output_data);
    BASS_Free();
}


Published

2012-11-25

Categories


Tags