diff options
author | Ludovic Pouzenc <ludovic@pouzenc.fr> | 2012-06-04 21:44:28 +0000 |
---|---|---|
committer | Ludovic Pouzenc <ludovic@pouzenc.fr> | 2012-06-04 21:44:28 +0000 |
commit | 5b385abef33b20c122bddf9cc9e947594e0ebd32 (patch) | |
tree | 2cda5c9fc70fa79ac880b4f744ffb7f65769ed33 /tests | |
parent | 9c7c14513dfe3440dad9cf0b1c652dde9e4f1de7 (diff) | |
download | 2012-violon-leds-5b385abef33b20c122bddf9cc9e947594e0ebd32.tar.gz 2012-violon-leds-5b385abef33b20c122bddf9cc9e947594e0ebd32.tar.bz2 2012-violon-leds-5b385abef33b20c122bddf9cc9e947594e0ebd32.zip |
Bon. Partie pulse audio finie je pense.
début de la partie galère sur le "vrai" calcul pour le vu-mètre.
C'est compliqué car si on veut du dbA il faut faire une FFT pour appliquer des poids par fréquence.
Analyse fréquentielle copiée depuis le projet Audacity (adaptée du C++ au C et décimée).
Il y a des tas de petits mallocs pour la FFT et ça pue. D'ailleurs l'exécution de cette version donne un assertion failed sur malloc() que j'avais jamais vu...
git-svn-id: file:///var/svn/2012-violon-leds/trunk@12 6be1fa4d-33ac-4c33-becc-79fcb3794bb6
Diffstat (limited to 'tests')
-rw-r--r-- | tests/test5/capture.c | 40 | ||||
-rw-r--r-- | tests/test5/compute.c | 293 | ||||
-rw-r--r-- | tests/test5/compute.h | 12 | ||||
-rw-r--r-- | tests/test5/test5.c | 14 | ||||
-rw-r--r-- | tests/test5/win_main.c | 12 |
5 files changed, 330 insertions, 41 deletions
diff --git a/tests/test5/capture.c b/tests/test5/capture.c index 441a6ac..00d9c54 100644 --- a/tests/test5/capture.c +++ b/tests/test5/capture.c @@ -5,7 +5,7 @@ #define APP_TITLE "Test 5 lpo" #define BUFSIZE 1024 -extern void my_process(gint sound_level, void *userdata); +extern void my_process(float *data, size_t nsamples, size_t nchan); capture_sound_level_cb_t *capture_sound_level_cb=NULL; @@ -16,7 +16,6 @@ void context_get_source_info_callback(pa_context *c, const pa_source_info *si, i pa_stream * create_stream(pa_context *c, const pa_source_info *si); void stream_state_callback(pa_stream *s, void *userdata); void stream_read_callback(pa_stream *s, size_t nbytes, void *userdata); -gint compute_level(const void *data, size_t nbytes); char *device_name=NULL; @@ -106,7 +105,6 @@ void context_get_server_info_callback(pa_context *c, const pa_server_info*si, vo } void context_get_source_info_callback(pa_context *c, const pa_source_info *si, int is_last, void *userdata) { - static pa_stream* pa_st=NULL; if (is_last < 0) { printf("Failed to get sink information\n"); @@ -117,18 +115,10 @@ void context_get_source_info_callback(pa_context *c, const pa_source_info *si, i return; } - pa_st=create_stream(c,si); - g_assert(pa_st); + create_stream(c,si); } pa_stream *create_stream(pa_context *c, const pa_source_info *si) { - /* The sample type to use - static const pa_sample_spec nss = { - .format = PA_SAMPLE_S16LE, - .rate = 44100, - .channels = 1 - }; -*/ static const pa_buffer_attr ba={ .maxlength=-1, .tlength=1024, @@ -137,6 +127,12 @@ pa_stream *create_stream(pa_context *c, const pa_source_info *si) { .fragsize=512 }; + pa_sample_spec nss = { + .format = PA_SAMPLE_FLOAT32LE, + .rate = si->sample_spec.rate, + .channels = si->sample_spec.channels + }; + pa_stream *stream=NULL; //pa_proplist *pl=NULL; char t[256]; @@ -148,7 +144,7 @@ pa_stream *create_stream(pa_context *c, const pa_source_info *si) { */ printf("Source : %s (%s)\n", si->name, si->description); - printf("Using sample format: %s\n", pa_sample_spec_snprint(t, sizeof(t), &(si->sample_spec))); + printf("Using sample format: %s\n", pa_sample_spec_snprint(t, sizeof(t), &nss)); printf("Using channel map: %s\n", pa_channel_map_snprint(t, sizeof(t), &(si->channel_map))); /* pl=pa_proplist_new(); @@ -156,7 +152,7 @@ pa_stream *create_stream(pa_context *c, const pa_source_info *si) { pa_proplist_set(pl, "tlength", &pl_tlength, sizeof(pl_tlength)); pa_proplist_set(pl, "fragsize", &pl_fragsize, sizeof(pl_fragsize)); */ - stream=pa_stream_new(c, APP_TITLE, &(si->sample_spec), &(si->channel_map)); + stream=pa_stream_new(c, APP_TITLE, &nss, &(si->channel_map)); //stream=pa_stream_new_with_proplist(c, APP_TITLE, &(si->sample_spec), &(si->channel_map), pl); pa_stream_set_state_callback(stream, stream_state_callback, NULL); pa_stream_set_read_callback(stream, stream_read_callback, NULL); @@ -196,11 +192,9 @@ void stream_read_callback(pa_stream *s, size_t nbytes, void *userdata) { printf("pa_stream_peek() failed\n");//: %s", pa_strerror(pa_context_errno(context))); return; } - //printf("level : %i\n", compute_level(p,nbytes)); //printf("debug : before call capture_sound_level_cb==%p\n", capture_sound_level_cb); - - my_process(compute_level(p,nbytes), NULL); + my_process((float *)p,nbytes/sizeof(float), pa_stream_get_sample_spec(s)->channels); // (*capture_sound_level_cb)(compute_level(p,nbytes), NULL); //printf("debug : after call capture_sound_level_cb==%p\n", capture_sound_level_cb); @@ -208,15 +202,3 @@ void stream_read_callback(pa_stream *s, size_t nbytes, void *userdata) { pa_stream_drop(s); } -gint compute_level(const void *data, size_t nbytes) { - size_t i; - gint val, level=0; - for (i=0;i<nbytes/2;i++) { - val=((int16_t *)data)[i]; - //printf("val==%i\n", val); - if (val<0) val=-val; - if (level<val) level=val; - } - return level; -} - diff --git a/tests/test5/compute.c b/tests/test5/compute.c index 11a7f81..f6f08c4 100644 --- a/tests/test5/compute.c +++ b/tests/test5/compute.c @@ -1,5 +1,298 @@ #include "compute.h" +#include <stdlib.h> +#include <math.h> + +#define MaxFastBits 16 + +int **gFFTBitTable = NULL; + +gfloat compute_level(const float *data, size_t nsamples, size_t nchan) { + + size_t i; + float level=0; + float *input = malloc(nsamples*sizeof(float)); + float *output = malloc(nsamples*sizeof(float)); + + double rate=44100; //TODO dynamique +/* Just return the max peak + for (i=0;i<nsamples;i+=nchan) { + val=((float *)data)[i]; + //printf("val==%i\n", val); + if (val<0) val=-val; + if (level<val) level=val; + } +*/ + for (i=0;i<nsamples;i++) { + input[i]=data[i*nchan]; + } + + compute_spectrom(input, nsamples, rate, output); + + for (i=0;i<nsamples;i++) { + level+=output[i]; + } + + return level/nsamples; +} + +// From Audacity +void compute_spectrom(float * data, int width, double rate, float *output) { + + int i; + float processed[256]={ 0.0f }; + float in[256]; + float out[256]; + + int start = 0; + int windows = 0; + while (start + 256 <= width) { + for (i=0; i<256; i++) + in[i] = data[start + i]; + + // Hanning + for (i=0; i<256; i++) + in[i] *= 0.50 - 0.50 * cos(2 * M_PI * i / (256 - 1)); + break; + + PowerSpectrum(in, out); + + // Take real part of result + for (i=0; i<256/2; i++) + processed[i] += out[i]; + + start += 256/2; + windows++; + } + // Convert to decibels + // But do it safely; -Inf is nobody's friend + for (i = 0; i < 256/2; i++){ + float temp=(processed[i] / 256 / windows); + if (temp > 0.0) + processed[i] = 10*log10(temp); + else + processed[i] = 0; + } + for(i=0;i<256/2;i++) + output[i] = processed[i]; +} + +/* + * PowerSpectrum + * + * This function computes the same as RealFFT, above, but + * adds the squares of the real and imaginary part of each + * coefficient, extracting the power and throwing away the + * phase. + * + * For speed, it does not call RealFFT, but duplicates some + * of its code. + */ + +void PowerSpectrum(float *In, float *Out) +{ + int i; + + float theta = M_PI / 128; + + float tmpReal[128]; + float tmpImag[128]; + float RealOut[128]; + float ImagOut[128]; + + for (i = 0; i < 128; i++) { + tmpReal[i] = In[2 * i]; + tmpImag[i] = In[2 * i + 1]; + } + + FFT(128, 0, tmpReal, tmpImag, RealOut, ImagOut); + + float wtemp = sin(0.5 * theta); + + float wpr = -2.0 * wtemp * wtemp; + float wpi = -1.0 * sin(theta); + float wr = 1.0 + wpr; + float wi = wpi; + + int i3; + + float h1r, h1i, h2r, h2i, rt, it; + for (i = 1; i < 128 / 2; i++) { + + i3 = 128 - i; + + h1r = 0.5 * (RealOut[i] + RealOut[i3]); + h1i = 0.5 * (ImagOut[i] - ImagOut[i3]); + h2r = 0.5 * (ImagOut[i] + ImagOut[i3]); + h2i = -0.5 * (RealOut[i] - RealOut[i3]); + + rt = h1r + wr * h2r - wi * h2i; + it = h1i + wr * h2i + wi * h2r; + + Out[i] = rt * rt + it * it; + + rt = h1r - wr * h2r + wi * h2i; + it = -h1i + wr * h2i + wi * h2r; + + Out[i3] = rt * rt + it * it; + + wr = (wtemp = wr) * wpr - wi * wpi + wr; + wi = wi * wpr + wtemp * wpi + wi; + } + + rt = (h1r = RealOut[0]) + ImagOut[0]; + it = h1r - ImagOut[0]; + Out[0] = rt * rt + it * it; + rt = RealOut[128 / 2]; + it = ImagOut[128 / 2]; + Out[128 / 2] = rt * rt + it * it; +} + +void FFT(int NumSamples, + gboolean InverseTransform, + float *RealIn, float *ImagIn, float *RealOut, float *ImagOut) +{ + int NumBits; /* Number of bits needed to store indices */ + int i, j, k, n; + int BlockSize, BlockEnd; + + double angle_numerator = 2.0 * M_PI; + double tr, ti; /* temp real, temp imaginary */ +/* + if (!IsPowerOfTwo(NumSamples)) { + fprintf(stderr, "%d is not a power of two\n", NumSamples); + exit(1); + } +*/ + if (!gFFTBitTable) + InitFFT(); + + if (!InverseTransform) + angle_numerator = -angle_numerator; + + NumBits = NumberOfBitsNeeded(NumSamples); + + /* + ** Do simultaneous data copy and bit-reversal ordering into outputs... + */ + for (i = 0; i < NumSamples; i++) { + j = FastReverseBits(i, NumBits); + RealOut[j] = RealIn[i]; + ImagOut[j] = (ImagIn == NULL) ? 0.0 : ImagIn[i]; + } + + /* + ** Do the FFT itself... + */ + + BlockEnd = 1; + for (BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1) { + + double delta_angle = angle_numerator / (double) BlockSize; + + double sm2 = sin(-2 * delta_angle); + double sm1 = sin(-delta_angle); + double cm2 = cos(-2 * delta_angle); + double cm1 = cos(-delta_angle); + double w = 2 * cm1; + double ar0, ar1, ar2, ai0, ai1, ai2; + + for (i = 0; i < NumSamples; i += BlockSize) { + ar2 = cm2; + ar1 = cm1; + + ai2 = sm2; + ai1 = sm1; + + for (j = i, n = 0; n < BlockEnd; j++, n++) { + ar0 = w * ar1 - ar2; + ar2 = ar1; + ar1 = ar0; + + ai0 = w * ai1 - ai2; + ai2 = ai1; + ai1 = ai0; + + k = j + BlockEnd; + tr = ar0 * RealOut[k] - ai0 * ImagOut[k]; + ti = ar0 * ImagOut[k] + ai0 * RealOut[k]; + + RealOut[k] = RealOut[j] - tr; + ImagOut[k] = ImagOut[j] - ti; + + RealOut[j] += tr; + ImagOut[j] += ti; + } + } + BlockEnd = BlockSize; + } + + /* + ** Need to normalize if inverse transform... + */ + + if (InverseTransform) { + float denom = (float) NumSamples; + + for (i = 0; i < NumSamples; i++) { + RealOut[i] /= denom; + ImagOut[i] /= denom; + } + } +} + +void InitFFT() +{ + gFFTBitTable = malloc(MaxFastBits*sizeof(int)); + + int len = 2; + int b, i; + for (b=1; b<=MaxFastBits; b++) { + gFFTBitTable[b-1]=malloc(len*sizeof(int)); + + for (i=0; i<len; i++) + gFFTBitTable[b-1][i] = ReverseBits(i, b); + + len <<= 1; + } +} + +int NumberOfBitsNeeded(int PowerOfTwo) +{ + int i; + +/* + if (PowerOfTwo < 2) { + fprintf(stderr, "Error: FFT called with size %d\n", PowerOfTwo); + exit(1); + } +*/ + for (i = 0;; i++) + if (PowerOfTwo & (1 << i)) + return i; +} + +inline int FastReverseBits(int i, int NumBits) +{ + if (NumBits <= MaxFastBits) + return gFFTBitTable[NumBits - 1][i]; + else + return ReverseBits(i, NumBits); +} + +int ReverseBits(int index, int NumBits) +{ + int i, rev; + + for (i = rev = 0; i < NumBits; i++) { + rev = (rev << 1) | (index & 1); + index >>= 1; + } + + return rev; +} + + void audio2hsv_1(gint audio_level, gint *light_h, gint *light_s, gint *light_v) { // Dummy code *light_h=-audio_level; diff --git a/tests/test5/compute.h b/tests/test5/compute.h index 2454e56..90230a7 100644 --- a/tests/test5/compute.h +++ b/tests/test5/compute.h @@ -3,6 +3,18 @@ #include <gtk/gtk.h> +gfloat compute_level(const float *data, size_t nsamples, size_t nchan); + +void compute_spectrom(float * data, int width, double rate, float *output); +void PowerSpectrum(float *In, float *Out); +void FFT(int NumSamples, gboolean InverseTransform, + float *RealIn, float *ImagIn, float *RealOut, float *ImagOut); +void InitFFT(); +int NumberOfBitsNeeded(int PowerOfTwo); +inline int FastReverseBits(int i, int NumBits); +int ReverseBits(int index, int NumBits); + + void audio2hsv_1(gint audio_level, gint *light_h, gint *light_s, gint *light_v); void hsv2rgb(gint h, gint s, gint v, gint *r, gint *g, gint *b); diff --git a/tests/test5/test5.c b/tests/test5/test5.c index a12c093..809aaa6 100644 --- a/tests/test5/test5.c +++ b/tests/test5/test5.c @@ -7,7 +7,7 @@ #include "capture.h" gint *audio_vumeter_val, *light_h, *light_s, *light_v, *light_r, *light_g, *light_b; -void my_process(gint sound_level, void *userdata); +void my_process(float *data, size_t nsamples, size_t nchan); int main (int argc, char **argv) { GtkWidget *mainwin; @@ -42,11 +42,14 @@ int main (int argc, char **argv) { return 0; } -void my_process(gint sound_level, void *userdata) { +void my_process(float *data, size_t nsamples, size_t nchan) { -// printf("my_process(%i, %p)\n", sound_level, userdata); - // Dummy code for audio capture - *audio_vumeter_val=sound_level; + float sound_level; + + sound_level=compute_level(data, nsamples, nchan); + + // Update sound vumeter value (refreshed asynchronously) + *audio_vumeter_val=sound_level*65535; // Transfert Function audio2hsv_1(*audio_vumeter_val,light_h,light_s,light_v); @@ -56,6 +59,5 @@ void my_process(gint sound_level, void *userdata) { // Send to DMX //TODO - } diff --git a/tests/test5/win_main.c b/tests/test5/win_main.c index e5d681d..f44c940 100644 --- a/tests/test5/win_main.c +++ b/tests/test5/win_main.c @@ -26,33 +26,33 @@ GtkWidget *win_main_build() { gtk_box_pack_start(GTK_BOX(hbox1), vumeter_sound, FALSE, FALSE, 0); vumeter_h = gtk_vu_meter_new (TRUE); - gtk_vu_meter_set_min_max (vumeter_h, 0, 65535); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_h), 0, 65535); gtk_vu_meter_set_gradient(GTK_VU_METER(vumeter_h), 7, f_gradient_hue, 7, b_gradient_hue); gtk_box_pack_start(GTK_BOX(hbox1), vumeter_h, FALSE, FALSE, 0); vumeter_s = gtk_vu_meter_new (TRUE); - gtk_vu_meter_set_min_max (vumeter_s, 0, 65535); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_s), 0, 65535); gtk_vu_meter_set_gradient(GTK_VU_METER(vumeter_s), 2, f_gradient_red, 2, b_gradient_red); gtk_box_pack_start(GTK_BOX(hbox1), vumeter_s, FALSE, FALSE, 0); vumeter_v = gtk_vu_meter_new (TRUE); - gtk_vu_meter_set_min_max (vumeter_v, 0, 65535); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_v), 0, 65535); gtk_vu_meter_set_gradient(GTK_VU_METER(vumeter_v), 2, f_gradient_red, 2, b_gradient_red); gtk_box_pack_start(GTK_BOX(hbox1), vumeter_v, FALSE, FALSE, 0); vumeter_r = gtk_vu_meter_new (TRUE); - gtk_vu_meter_set_min_max (vumeter_r, 0, 255); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_r), 0, 255); gtk_vu_meter_set_gradient(GTK_VU_METER(vumeter_r), 2, f_gradient_red, 2, b_gradient_red); gtk_box_pack_start(GTK_BOX(hbox1), vumeter_r, FALSE, FALSE, 0); vumeter_g = gtk_vu_meter_new (TRUE); - gtk_vu_meter_set_min_max (vumeter_g, 0, 255); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_g), 0, 255); gtk_vu_meter_set_gradient(GTK_VU_METER(vumeter_g), 2, f_gradient_green, 2, b_gradient_green); gtk_box_pack_start(GTK_BOX(hbox1), vumeter_g, FALSE, FALSE, 0); vumeter_b = gtk_vu_meter_new (TRUE); - gtk_vu_meter_set_min_max (vumeter_b, 0, 255); + gtk_vu_meter_set_min_max(GTK_VU_METER(vumeter_b), 0, 255); gtk_vu_meter_set_gradient(GTK_VU_METER(vumeter_b), 2, f_gradient_blue, 2, b_gradient_blue); gtk_box_pack_start(GTK_BOX(hbox1), vumeter_b, FALSE, FALSE, 0); |