Last active
August 29, 2015 13:58
-
-
Save dz0ny/10154196 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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