Skip to content

Instantly share code, notes, and snippets.

@dz0ny
Last active August 29, 2015 13:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dz0ny/10154196 to your computer and use it in GitHub Desktop.
Save dz0ny/10154196 to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <groove/groove.h>
#include <limits.h>
//compile with gcc -o waveform main.c -O3 -lgroove -std=c99
// Define a vector type
typedef struct {
int size;
float *data;
} Samples;
int getSoundPoints(Samples* soundPoints, char* in_file_path, int fps) {
float min_sample = -1.0f;
int samples_to_take = 0;
int frame_count = 0;
int frames_until_emit;
int emit_every;
groove_init();
struct GrooveFile *file = groove_file_open(in_file_path);
if (! file) {
fprintf(stderr, "Error opening input file: %s\n", in_file_path);
groove_finish();
return 1;
}
float duration = groove_file_duration(file);
struct GroovePlaylist *playlist = groove_playlist_create();
struct GrooveSink *sink = groove_sink_create();
struct GrooveBuffer *buffer;
sink->audio_format.sample_rate = 44100;
sink->audio_format.channel_layout = GROOVE_CH_LAYOUT_MONO;
sink->audio_format.sample_fmt = GROOVE_SAMPLE_FMT_FLT;
if (groove_sink_attach(sink, playlist) < 0) {
fprintf(stderr, "error attaching sink\n");
return 1;
}
struct GroovePlaylistItem *item =
groove_playlist_insert(playlist, file, 1.0, NULL);
// scan the song for the exact correct duration
while (groove_sink_buffer_get(sink, &buffer, 1) == GROOVE_BUFFER_YES) {
frame_count += buffer->frame_count;
groove_buffer_unref(buffer);
}
groove_playlist_seek(playlist, item, 0);
// Calculate best sample resolution for file
samples_to_take = duration * fps; // take sample every 125ms (at 8)
emit_every = frame_count / samples_to_take;
frames_until_emit = emit_every;
// Initialize vector
soundPoints->size = 0;
soundPoints->data = calloc(samples_to_take+1, sizeof(float));
while (groove_sink_buffer_get(sink, &buffer, 1) == GROOVE_BUFFER_YES) {
// process the buffer
for (int i = 0; i < buffer->frame_count && soundPoints->size < samples_to_take;
i += 1, frames_until_emit -= 1)
{
if (frames_until_emit == 0) {
soundPoints->data[soundPoints->size++] = min_sample;
frames_until_emit = emit_every;
min_sample = -1.0f;
}
float *samples = (float *) buffer->data[0];
float sample = samples[i];
if (sample > min_sample) min_sample = sample;
}
groove_buffer_unref(buffer);
}
// emit the last column if necessary. This will have to run multiple times
// if the duration specified in the metadata is incorrect.
while (soundPoints->size < samples_to_take) {
soundPoints->data[soundPoints->size++] = 0.0f;
}
groove_sink_detach(sink);
groove_sink_destroy(sink);
groove_playlist_clear(playlist);
groove_file_close(file);
groove_playlist_destroy(playlist);
groove_finish();
}
int main(int argc, char *argv[])
{
Samples result;
char* file = argv[1];
if (file == NULL){
fprintf(stderr, "%s", "Missing file to process\nExample: ./waveform file.mp3");
return 1;
}
int err = getSoundPoints(&result, file, 8);
if (err){
return 1;
}
fprintf(stdout, "{\"samples\": %d,\"data\": [", result.size);
for (int i = 0; i < result.size-1; ++i)
{
fprintf(stdout, "%lf,", result.data[i]);
}
fprintf(stdout, "%lf]}", result.data[result.size]);
free(result.data);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment