/* * Do all the steps of the FFT, taking as input sound data (as described in * sound.h) and returning the intensities of each frequency as doubles in the * range 0 to ((FFT_BUFFER_SIZE / 2) * 32768) ^ 2 * * FIXME - the above range assumes no frequencies present have an amplitude * larger than that of the sample variation. But this is false: we could have * a wave such that its maximums are always between samples, and it's just * inside the representable range at the places samples get taken. * Question: what _is_ the maximum value possible. Twice that value? Root * two times that value? Hmmm. Think it depends on the frequency, too. * * The input array is assumed to have FFT_BUFFER_SIZE elements, * and the output array is assumed to have (FFT_BUFFER_SIZE / 2 + 1) elements. * state is a (non-NULL) pointer returned by fft_init. */ void fft_perform(const sound_sample *input, double *output, fft_state *state) { /* Convert data from sound format to be ready for FFT */ fft_prepare(input, state->real, state->imag); /* Do the actual FFT */ fft_calculate(state->real, state->imag); /* Convert the FFT output into intensities */ fft_output(state->real, state->imag, output); }
// Run the plugin static void run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { // Return values static GimpParam values[1]; gint sel_x1, sel_y1, sel_x2, sel_y2, w, h, padding; PluginData pd; GimpRunMode run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; *nreturn_vals = 1; *return_vals = values; if (param[0].type!= GIMP_PDB_INT32) status=GIMP_PDB_CALLING_ERROR; if (param[2].type!=GIMP_PDB_DRAWABLE) status=GIMP_PDB_CALLING_ERROR; run_mode = (GimpRunMode) param[0].data.d_int32; pd.drawable = gimp_drawable_get(param[2].data.d_drawable); gimp_drawable_mask_bounds(pd.drawable->drawable_id, &sel_x1, &sel_y1, &sel_x2, &sel_y2); pd.selection_width = sel_x2 - sel_x1; pd.selection_height = sel_y2 - sel_y1; pd.selection_offset_x = sel_x1; pd.selection_offset_y = sel_y1; pd.image_width = gimp_drawable_width(pd.drawable->drawable_id); pd.image_height = gimp_drawable_height(pd.drawable->drawable_id); pd.channel_count = gimp_drawable_bpp(pd.drawable->drawable_id); pd.point_grabbed = -1; if (run_mode == GIMP_RUN_INTERACTIVE) { // Interactive call with dialog dialog(&pd); if (pd.curve_user.count > 0) { gimp_set_data (PLUG_IN_BINARY, pd.curve_user.user_points, sizeof (GdkPoint) * pd.curve_user.count); } } else if (run_mode == GIMP_RUN_WITH_LAST_VALS) { // Read a saved curve and apply it fft_prepare(&pd); gimp_get_data(PLUG_IN_BINARY, pd.curve_user.user_points); pd.curve_user.count = gimp_get_data_size(PLUG_IN_BINARY) / sizeof (GdkPoint); gimp_pixel_rgn_init(&pd.region, pd.drawable, 0, 0, pd.image_width, pd.image_height, TRUE, TRUE); fft_apply(&pd); gimp_pixel_rgn_set_rect(&pd.region, pd.img_pixels, 0, 0, pd.image_width, pd.image_height); gimp_drawable_flush(pd.drawable); gimp_drawable_merge_shadow(pd.drawable->drawable_id, TRUE); gimp_drawable_update(pd.drawable->drawable_id, pd.selection_offset_x, pd.selection_offset_y, pd.selection_width, pd.selection_height); fft_destroy(&pd); gimp_displays_flush(); } else { status = GIMP_PDB_CALLING_ERROR; } values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; gimp_drawable_detach(pd.drawable); }
// Create and handle the plugin's dialog gboolean dialog(PluginData *pd) { gimp_ui_init (PLUG_IN_BINARY, FALSE); GtkWidget *dialog, *main_hbox, *hbox_buttons, *preview, *graph, *vbox, *preview_button, *preview_hd_checkbox; dialog = gimp_dialog_new ("Frequency Curves", PLUG_IN_BINARY, NULL, (GtkDialogFlags)0, gimp_standard_help_func, PLUG_IN_NAME, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); main_hbox = gtk_hbox_new (FALSE, 12); gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12); gtk_container_add (GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), main_hbox); curve_init(&pd->curve_user); curve_copy(&pd->curve_user, &pd->curve_fft); pd->preview = gimp_drawable_preview_new (pd->drawable, 0); gtk_box_pack_start (GTK_BOX (main_hbox), pd->preview, TRUE, TRUE, 0); gtk_widget_show (pd->preview); g_signal_connect_swapped (pd->preview, "invalidated", G_CALLBACK (preview_invalidated), pd); vbox = gtk_vbox_new (FALSE, 12); gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); gtk_container_add (GTK_CONTAINER(main_hbox), vbox); gtk_widget_show(vbox); graph = pd->graph = gtk_drawing_area_new(); pd->graph_pixmap = NULL; gtk_widget_set_size_request (graph, GRAPH_WIDTH, GRAPH_HEIGHT); gtk_widget_set_events (graph, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_ENTER_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK); gtk_container_add (GTK_CONTAINER (vbox), graph); gtk_widget_show (graph); g_signal_connect (graph, "event", G_CALLBACK (graph_events), pd); hbox_buttons = gtk_hbox_new (FALSE, 12); gtk_container_set_border_width (GTK_CONTAINER (hbox_buttons), 12); gtk_container_add (GTK_CONTAINER(vbox), hbox_buttons); gtk_widget_show(hbox_buttons); preview_button = gtk_button_new_with_mnemonic ("HD _Preview"); gtk_box_pack_start (GTK_BOX (hbox_buttons), preview_button, FALSE, FALSE, 0); gtk_widget_show (preview_button); g_signal_connect (preview_button, "clicked", G_CALLBACK (preview_hd), pd); preview_hd_checkbox = gtk_check_button_new_with_label("Always preview HD"); gtk_box_pack_start (GTK_BOX (hbox_buttons), preview_hd_checkbox, FALSE, FALSE, 0); gtk_widget_show (preview_hd_checkbox); pd->do_preview_hd = FALSE; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(preview_hd_checkbox), FALSE); g_signal_connect (preview_hd_checkbox, "toggled", G_CALLBACK (preview_hd_toggled), pd); gtk_widget_show(main_hbox); gtk_widget_show(dialog); fft_prepare(pd); histogram_generate(pd); wavelet_prepare(pd); gboolean run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK); if (run) { // set the region mode to actual writing gimp_pixel_rgn_init(&pd->region, pd->drawable, 0, 0, pd->image_width, pd->image_height, TRUE, TRUE); fft_apply(pd); gimp_pixel_rgn_set_rect(&pd->region, pd->img_pixels, 0, 0, pd->image_width, pd->image_height); // show the result gimp_drawable_flush(pd->drawable); gimp_drawable_merge_shadow(pd->drawable->drawable_id, TRUE); gimp_drawable_update(pd->drawable->drawable_id, pd->selection_offset_x, pd->selection_offset_y, pd->selection_width, pd->selection_height); gimp_displays_flush(); } fft_destroy(pd); wavelet_destroy(pd); gtk_widget_destroy (dialog); return run; }