Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

audio_filters.cc

Go to the documentation of this file.
00001 /*
00002 * audio_filters.h -- audio filters
00003 * Copyright (C) 2002 Charles Yates <charles.yates@pandora.be>
00004 * Copyright (C) 2002-2007 Dan Dennedy <dan@dennedy.org>
00005 *
00006 * This program is free software; you can redistribute it and/or modify
00007 * it under the terms of the GNU General Public License as published by
00008 * the Free Software Foundation; either version 2 of the License, or
00009 * (at your option) any later version.
00010 *
00011 * This program is distributed in the hope that it will be useful,
00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 * GNU General Public License for more details.
00015 *
00016 * You should have received a copy of the GNU General Public License
00017 * along with this program; if not, write to the Free Software Foundation,
00018 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024 
00025 #include <iostream>
00026 
00027 // Project Includes
00028 #include <gtk/gtk.h>
00029 #include <glade/glade.h>
00030 #include "audio_filters.h"
00031 #include "page_magick.h"
00032 #include "kino_av_pipe.h"
00033 #include "frame.h"
00034 #include "kino_extra.h"
00035 #include "time_map.h"
00036 
00037 // C Includes
00038 
00039 #include <string.h>
00040 #include <stdio.h>
00041 
00042 #define MAX_VECTOR 5000
00043 
00044 extern "C"
00045 {
00046 #include "support.h"
00047     extern GladeXML* magick_glade;
00048 }
00049 
00053 class AudioFilterKeep : public AudioFilter
00054 {
00055 public:
00056     char *GetDescription( ) const
00057     {
00058         return _( "No Change" );
00059     }
00060 
00061     void GetFrame( int16_t **buffer, int frequency, int channels, int& samples, double position, double frame_delta )
00062     {}
00063 }
00064 ;
00065 
00069 class AudioFilterSilence : public AudioFilter
00070 {
00071 public:
00072     char *GetDescription( ) const
00073     {
00074         return _( "Silence" );
00075     }
00076 
00077     void GetFrame( int16_t **buffer, int frequency, int channels, int& samples, double position, double frame_delta )
00078     {
00079         for ( int i = 0; i < channels; i ++ )
00080             for ( int j = 0; j < samples; j ++ )
00081                 buffer[ i ][ j ] = 0;
00082     }
00083 };
00084 
00088 class AudioFilterFadeOut : public AudioFilter
00089 {
00090 public:
00091     char *GetDescription( ) const
00092     {
00093         return _( "Fade Out" );
00094     }
00095 
00096     void GetFrame( int16_t **buffer, int frequency, int channels, int& samples, double position, double frame_delta )
00097     {
00098         for ( int i = 0; i < channels; i ++ )
00099             for ( int j = 0; j < samples; j ++ )
00100                 buffer[ i ][ j ] = ( int ) ( buffer[ i ][ j ] * ( 1 - position ) );
00101     }
00102 };
00103 
00107 class AudioFilterFadeIn : public AudioFilter
00108 {
00109 public:
00110     char *GetDescription( ) const
00111     {
00112         return _( "Fade In" );
00113     }
00114 
00115     void GetFrame( int16_t **buffer, int frequency, int channels, int& samples, double position, double frame_delta )
00116     {
00117         for ( int i = 0; i < channels; i ++ )
00118             for ( int j = 0; j < samples; j ++ )
00119                 buffer[ i ][ j ] = ( int ) ( buffer[ i ][ j ] * position );
00120     }
00121 };
00122 
00126 static void
00127 gtk_curve_reset_vector( GtkCurve *curve, gfloat y = 1.0 )
00128 {
00129     if ( curve->ctlpoint )
00130         g_free ( curve->ctlpoint );
00131 
00132     curve->num_ctlpoints = 2;
00133     curve->ctlpoint = ( gfloat( * ) [ 2 ] ) g_malloc ( 2 * sizeof ( curve->ctlpoint[ 0 ] ) );
00134     curve->ctlpoint[ 0 ][ 0 ] = curve->min_x;
00135     curve->ctlpoint[ 0 ][ 1 ] = y;
00136     curve->ctlpoint[ 1 ][ 0 ] = curve->max_x;
00137     curve->ctlpoint[ 1 ][ 1 ] = y;
00138 
00139 }
00140 
00141 class AudioGain : public GDKAudioFilter
00142 {
00143 private:
00144     GtkWidget* m_window;
00145     GtkCurve* m_curve;
00146     gfloat m_vector[ MAX_VECTOR ];
00147 
00148 public:
00149     AudioGain( )
00150     {
00151         m_window = glade_xml_get_widget( magick_glade, "window_gain" );
00152         m_curve = GTK_CURVE( glade_xml_get_widget( magick_glade, "curve_gain" ) );
00153     }
00154     char *GetDescription( ) const
00155     {
00156         return _( "Gain" );
00157     }
00158     void GetFrame( int16_t **buffer, int frequency, int channels, int& samples, double position, double frame_delta )
00159     {
00160         gtk_curve_get_vector( m_curve, MAX_VECTOR, m_vector );
00161         int pos = int( position * MAX_VECTOR + 0.5 );
00162         double gain = m_vector[ pos ];
00163         for ( int s = 0; s < samples; s ++ )
00164             for ( int c = 0; c < channels; c ++ )
00165                 buffer[ c ][ s ] = int16_t( double( buffer[ c ][ s ] * gain + 0.5 ) );
00166     }
00167     void AttachWidgets( GtkBin *bin )
00168     {
00169         gtk_widget_reparent( ( GTK_BIN( m_window ) ) ->child, GTK_WIDGET( bin ) );
00170         while ( gtk_events_pending() )
00171             gtk_main_iteration();
00172         gtk_curve_reset_vector( m_curve );
00173         gtk_curve_set_curve_type( m_curve, GTK_CURVE_TYPE_LINEAR );
00174     }
00175     void DetachWidgets( GtkBin *bin )
00176     {
00177         gtk_widget_reparent( ( GTK_BIN( bin ) ) ->child, GTK_WIDGET( m_window ) );
00178     }
00179 };
00180 
00185 static void on_entry_file_changed( GtkEditable *editable, gpointer user_data );
00186 
00187 class WavSelect
00188 {
00189 private:
00190     char wav_file[ PATH_MAX + NAME_MAX ];
00191     KinoAudioInput *wav;
00192     GtkLabel *label_info;
00193     GtkEntry *file_entry;
00194     AudioResample<int16_le_t,int16_ne_t> *resampler;
00195     int fps;
00196     int16_t temp[ 4 * DV_AUDIO_MAX_SAMPLES * 2 ];
00197     bool wav_selected;
00198 
00199 public:
00200     WavSelect() : wav( NULL ), label_info( NULL ), file_entry( NULL ), resampler(0), wav_selected( false )
00201     {
00202         strcpy( wav_file, "" );
00203     }
00204 
00205     ~WavSelect()
00206     {
00207         delete wav;
00208         delete resampler;
00209     }
00210 
00211     void SetWavInfoLabel( GtkLabel *label )
00212     {
00213         label_info = label;
00214     }
00215 
00216     void SetWavFileEntry( GtkEntry *entry )
00217     {
00218         file_entry = entry;
00219         g_signal_connect( G_OBJECT( entry ), "changed", G_CALLBACK( on_entry_file_changed ), this );
00220     }
00221 
00222     void WavFileSelected( )
00223     {
00224         char text[ 10240 ] = "";
00225 
00226         if ( strcmp( wav_file, gtk_entry_get_text( file_entry ) ) )
00227         {
00228             delete wav;
00229 
00230             strcpy( wav_file, gtk_entry_get_text( file_entry ) );
00231             this->wav = KinoAudioInputFactory::CreateAudioInput( wav_file );
00232 
00233             if ( wav != NULL )
00234             {
00235                 wav_selected = true;
00236                 if ( wav->GetNumberOfSamples() > 0 )
00237                 {
00238                     sprintf( text, _( "Duration: %.02f seconds" ),
00239                             ( double ) wav->GetNumberOfSamples() / ( double ) wav->GetFrequency() );
00240                 }
00241                 else
00242                 {
00243                     sprintf( text, _( "Duration: N/A" ) );
00244                 }
00245             }
00246             else
00247             {
00248                 wav_selected = false;
00249                 strcpy( text, _( "Invalid file selected" ) );
00250             }
00251         }
00252         else if ( !strcmp( wav_file, "" ) )
00253         {
00254             wav_selected = false;
00255             strcpy( text, _( "No file selected" ) );
00256         }
00257 
00258         if ( strcmp( text, "" ) )
00259             gtk_label_set_text( label_info, text );
00260     }
00261 
00262     void WavStart( int frequency, int samples, off_t offset )
00263     {
00264         if ( wav_selected )
00265         {
00266             if ( wav->GetFrequency() != frequency )
00267             {
00268                 delete resampler;
00269                 resampler = AudioResampleFactory<int16_le_t,int16_ne_t>::createAudioResample(
00270                     AUDIO_RESAMPLE_SRC_SINC_BEST_QUALITY );
00271                 resampler->SetOutputFrequency( frequency );
00272             }
00273             else
00274             {
00275                 delete resampler;
00276                 resampler = 0;
00277             }
00278             fps = frequency / samples;
00279             if ( ! wav->Seek( offset ) )
00280             {
00281                 int16_t data[2];
00282                 wav->Open( wav_file );
00283                 for ( off_t i = 0; i < offset; i += sizeof(data) )
00284                 {
00285                     if ( ! wav->Get( data, sizeof(data) ) )
00286                         break;
00287                 }
00288             }
00289         }
00290     }
00291 
00292     int WavRead( int16_t **a, int f, int c, int bytes, double gain = 1.0 )
00293     {
00294         if ( wav_selected )
00295         {
00296             if ( resampler )
00297             {
00298                 memset( temp, 0, sizeof( temp ) );
00299                 int samples = bytes / c / 2 * wav->GetFrequency() / f;
00300                 bytes = samples * c * 2;
00301                 if ( !wav->Get( temp, bytes ) )
00302                     std::cerr << ">>> Underrun? unable to read " << bytes << " bytes" << std::endl;
00303     
00304                 resampler->Resample( reinterpret_cast<int16_le_t *>(temp), wav->GetFrequency(), c, samples );
00305     
00306                 int16_t *p = resampler->output;
00307                 for ( int s = 0; s < resampler->size / c / 2; s ++ )
00308                     for ( int i = 0; i < c; i ++ )
00309                         a[ i ][ s ] = int16_t( double( *p++ ) * gain + 0.5 );
00310                 return (resampler->size / c / 2);
00311             }
00312             else
00313             {
00314                 memset( temp, 0, sizeof( temp ) );
00315                 if ( !wav->Get( temp, bytes ) )
00316                     std::cerr << ">>> Underrun? unable to read " << bytes << " bytes" << std::endl;
00317                 int16_t *p = temp;
00318                 for ( int s = 0; s < bytes / c / 2; s ++ )
00319                     for ( int i = 0; i < c; i ++ )
00320                         a[ i ][ s ] = int16_t( double( *p++ ) * gain + 0.5 );
00321                 return (bytes / c / 2);
00322             }
00323         }
00324         return 0;
00325     }
00326 
00327     bool IsSelected()
00328     {
00329         return wav_selected;
00330     }
00331 
00332     int GetAdjustedSamples( int frequency, int samples ) const
00333     {
00334         return samples * wav->GetFrequency() / frequency;
00335     }
00336     
00337     int WavGetFrequency()
00338     {
00339         return wav->GetFrequency();
00340     }
00341 };
00342 
00346 static void on_entry_file_changed( GtkEditable *editable, gpointer user_data )
00347 {
00348     ( ( WavSelect * ) user_data ) ->WavFileSelected();
00349 }
00350 
00359 class AudioDub : public GDKAudioFilter, WavSelect
00360 {
00361 private:
00362     GtkWidget *window;
00363     GtkCurve* curve;
00364     double offsetSecs;
00365     bool initiated;
00366     gfloat m_vector[ MAX_VECTOR ];
00367 
00368 public:
00369     AudioDub( ) : initiated( false )
00370     {
00371         window = glade_xml_get_widget( magick_glade, "window_dub" );
00372         curve = GTK_CURVE( glade_xml_get_widget( magick_glade, "curve_dub" ) );
00373         g_signal_connect( G_OBJECT( lookup_widget( window, "button_dub_file" ) ),
00374             "clicked", G_CALLBACK( on_button_sub_file_clicked ), this );
00375         SetWavInfoLabel( GTK_LABEL( lookup_widget( window, "label_dub_info" ) ) );
00376         SetWavFileEntry( GTK_ENTRY( lookup_widget( window, "entry_dub_file" ) ) );
00377         WavFileSelected();
00378     }
00379 
00380     virtual ~AudioDub( )
00381     {}
00382 
00383     char *GetDescription( ) const
00384     {
00385         return _( "Dub" );
00386     }
00387 
00388     bool IsAFrameConsumer() const
00389     {
00390         return false;
00391     }
00392 
00393     void GetFrame( int16_t **aframe, int frequency, int channels, int& samples, double position, double frame_delta )
00394     {
00395         if ( IsSelected() )
00396         {
00397             gtk_curve_get_vector( curve, MAX_VECTOR, m_vector );
00398             if ( !initiated )
00399             {
00400                 initiated = true;
00401                 // nasty approximation (sigh)
00402                 off_t byte_offset = ( int( offsetSecs * WavGetFrequency() )
00403                     + ( GetSelectedFramesForFX().GetIndex( position ) * GetAdjustedSamples( frequency, samples ) ) )
00404                     * channels * 2;
00405                 WavStart( frequency, samples, byte_offset );
00406             }
00407             int pos = int( position * MAX_VECTOR + 0.5 );
00408             samples = WavRead( aframe, frequency, channels, samples * channels * 2, m_vector[ pos ] );
00409         }
00410     }
00411 
00412     static void
00413     on_button_sub_file_clicked( GtkButton *button, gpointer user_data)
00414     {
00415         char *filename = common->getFileToOpen( _("Choose an audio file"), false );
00416         if ( filename && strcmp( filename, "" ) )
00417         {
00418             AudioDub *me = static_cast< AudioDub* >( user_data );
00419             gtk_entry_set_text( me->getEntry(), filename );
00420         }
00421     }
00422 
00423     void AttachWidgets( GtkBin *bin )
00424     {
00425         gtk_curve_reset_vector( curve );
00426         gtk_widget_reparent( ( GTK_BIN( window ) ) ->child, GTK_WIDGET( bin ) );
00427         while ( gtk_events_pending() )
00428             gtk_main_iteration();
00429         gtk_curve_set_curve_type( curve, GTK_CURVE_TYPE_LINEAR );
00430     }
00431 
00432     void DetachWidgets( GtkBin *bin )
00433     {
00434         gtk_widget_reparent( ( GTK_BIN( bin ) ) ->child, GTK_WIDGET( window ) );
00435     }
00436 
00437     void InterpretWidgets( GtkBin *bin )
00438     {
00439         GtkWidget *offsetSpin = lookup_widget( window, "spinbutton_dub_offset" );
00440         offsetSecs = gtk_spin_button_get_value( GTK_SPIN_BUTTON( offsetSpin ) );
00441         initiated = false;
00442     }
00443 
00444     GtkEntry *getEntry() const
00445     {
00446         return GTK_ENTRY( lookup_widget( window, "entry_dub_file" ) );
00447     }
00448 };
00449 
00458 class AudioMix : public GDKAudioFilter, WavSelect
00459 {
00460 private:
00461     GtkWidget *window;
00462     GtkCurve* curve;
00463     bool initiated;
00464     double offsetSecs;
00465     int16_t** bframe;
00466     gfloat m_vector[ MAX_VECTOR ];
00467 
00468 public:
00469     AudioMix( ) : initiated( false )
00470     {
00471         window = glade_xml_get_widget( magick_glade, "window_mix" );
00472         curve = GTK_CURVE( glade_xml_get_widget( magick_glade, "curve_mix" ) );
00473         g_signal_connect( G_OBJECT( lookup_widget( window, "button_mix_file" ) ),
00474             "clicked", G_CALLBACK( on_button_mix_file_clicked ), this );
00475         SetWavInfoLabel( GTK_LABEL( lookup_widget( window, "label_mix_info" ) ) );
00476         SetWavFileEntry( GTK_ENTRY( lookup_widget( window, "entry_mix_file" ) ) );
00477         WavFileSelected();
00478         bframe = new int16_t*[4];
00479         for ( int i = 0; i < 4; i++ )
00480             bframe[i] = new int16_t[ DV_AUDIO_MAX_SAMPLES * 2 ];
00481     }
00482 
00483     virtual ~AudioMix( )
00484     {
00485         for ( int i = 0; i < 4; i++ )
00486             delete[] bframe[i];
00487         delete[] bframe;
00488     }
00489 
00490     char *GetDescription( ) const
00491     {
00492         return _( "Mix" );
00493     }
00494 
00495     void Mix( int16_t **io, int16_t **with, int channels, int samples, double mix )
00496     {
00497         for ( int s = 0; s < samples; s ++ )
00498             for ( int c = 0; c < channels; c ++ )
00499                 io[ c ][ s ] = ( int16_t ) ( ( double ) io[ c ][ s ] * ( 1.0 - mix ) + ( double ) with[ c ][ s ] * mix );
00500     }
00501 
00502     void GetFrame( int16_t **aframe, int frequency, int channels, int& samples, double position, double frame_delta )
00503     {
00504         if ( IsSelected() )
00505         {
00506             gtk_curve_get_vector( curve, MAX_VECTOR, m_vector );
00507             if ( !initiated )
00508             {
00509                 initiated = true;
00510                 // nasty approximation (sigh)
00511                 off_t byte_offset = ( int( offsetSecs * WavGetFrequency() )
00512                     + ( GetSelectedFramesForFX().GetIndex( position ) * GetAdjustedSamples( frequency, samples ) ) )
00513                     * channels * 2;
00514                 WavStart( frequency, samples, byte_offset );
00515             }
00516             samples = WavRead( bframe, frequency, channels, samples * channels * 2 );
00517             int pos = int( position * MAX_VECTOR + 0.5 );
00518             Mix( aframe, bframe, channels, samples, m_vector[ pos ] );
00519         }
00520     }
00521 
00522     static void
00523     on_button_mix_file_clicked( GtkButton *button, gpointer user_data)
00524     {
00525         char *filename = common->getFileToOpen( _("Choose an audio file"), false );
00526         if ( filename && strcmp( filename, "" ) )
00527         {
00528             AudioMix *me = static_cast< AudioMix* >( user_data );
00529             gtk_entry_set_text( me->getEntry(), filename );
00530         }
00531     }
00532     
00533     void AttachWidgets( GtkBin *bin )
00534     {
00535         gtk_widget_reparent( ( GTK_BIN( window ) ) ->child, GTK_WIDGET( bin ) );
00536         while ( gtk_events_pending() )
00537             gtk_main_iteration();
00538         gtk_curve_reset_vector( curve, 0.5 );
00539         gtk_curve_set_curve_type( curve, GTK_CURVE_TYPE_LINEAR );
00540     }
00541 
00542     void DetachWidgets( GtkBin *bin )
00543     {
00544         gtk_widget_reparent( ( GTK_BIN( bin ) ) ->child, GTK_WIDGET( window ) );
00545     }
00546 
00547     void InterpretWidgets( GtkBin *bin )
00548     {
00549         GtkWidget *offsetSpin = lookup_widget( window, "spinbutton_mix_offset" );
00550         offsetSecs = gtk_spin_button_get_value( GTK_SPIN_BUTTON( offsetSpin ) );
00551         initiated = false;
00552     }
00553 
00554     GtkEntry *getEntry() const
00555     {
00556         return GTK_ENTRY( lookup_widget( window, "entry_mix_file" ) );
00557     }
00558 };
00559 
00563 static void
00564 on_optionmenu_selected ( GtkMenuItem *menu_item, gpointer user_data )
00565 {
00566     ( ( GDKAudioFilterRepository * ) user_data ) ->SelectionChange();
00567 }
00568 
00574 GDKAudioFilterRepository::GDKAudioFilterRepository() : selected_filter( NULL ), menu( NULL ), container( NULL )
00575 {
00576     // Register an instance of each object (adapting raw filters to GDK filters where necessary)
00577     Register( new GDKAudioFilterAdapter( new AudioFilterKeep() ) );
00578     Register( new AudioDub() );
00579     Register( new GDKAudioFilterAdapter( new AudioFilterFadeIn() ) );
00580     Register( new GDKAudioFilterAdapter( new AudioFilterFadeOut() ) );
00581     Register( new AudioGain() );
00582     Register( new AudioMix() );
00583     Register( new GDKAudioFilterAdapter( new AudioFilterSilence() ) );
00584 }
00585 
00589 GDKAudioFilterRepository::~GDKAudioFilterRepository()
00590 {
00591     // Remove the filters in the repository
00592     for ( unsigned int index = 0; index < filters.size(); index ++ )
00593         delete filters[ index ];
00594 }
00595 
00599 void GDKAudioFilterRepository::Register( GDKAudioFilter *filter )
00600 {
00601     std::cerr <<  ">>> Audio Filter: " << filter->GetDescription() << std::endl;
00602     filters.push_back( filter );
00603 }
00604 
00608 void GDKAudioFilterRepository::Initialise( GtkOptionMenu *menu, GtkBin *container )
00609 {
00610     // Store these for future reference
00611     this->menu = menu;
00612     this->container = container;
00613 
00614     // Add the filters to the menu
00615     GtkMenu *menu_new = GTK_MENU( gtk_menu_new( ) );
00616     for ( unsigned int index = 0; index < filters.size(); index ++ )
00617     {
00618         GtkWidget *item = gtk_menu_item_new_with_label( filters[ index ] ->GetDescription( ) );
00619         gtk_widget_show( item );
00620         gtk_menu_append( menu_new, item );
00621         g_signal_connect( G_OBJECT( item ), "activate", G_CALLBACK( on_optionmenu_selected ), this );
00622     }
00623     gtk_menu_set_active( menu_new, 0 );
00624     gtk_option_menu_set_menu( menu, GTK_WIDGET( menu_new ) );
00625 
00626     // Register the selected items widgets
00627     SelectionChange();
00628 }
00629 
00633 GDKAudioFilter *GDKAudioFilterRepository::Get( ) const
00634 {
00635     GtkMenu * filterMenu = GTK_MENU( gtk_option_menu_get_menu( menu ) );
00636     GtkWidget *active_item = gtk_menu_get_active( filterMenu );
00637     return filters[ g_list_index( GTK_MENU_SHELL( filterMenu ) ->children, active_item ) ];
00638 }
00639 
00643 void GDKAudioFilterRepository::SelectionChange( )
00644 {
00645     bool isPreviewing = false;
00646     PageMagick* magick = 0;
00647 
00648     if ( common && common->getPageMagick( ) )
00649         magick = common->getPageMagick( );
00650     if ( magick && magick->IsPreviewing() )
00651     {
00652         isPreviewing = true;
00653         magick->StopPreview();
00654     }
00655 
00656     // Detach the selected filters widgets
00657     if ( selected_filter != NULL )
00658         selected_filter->DetachWidgets( container );
00659 
00660     // Get the new selection
00661     selected_filter = Get();
00662 
00663     // Attach the new filters widgets
00664     if ( selected_filter != NULL )
00665         selected_filter->AttachWidgets( container );
00666 
00667     if ( magick )
00668     {
00669         if ( isPreviewing )
00670             magick->StartPreview();
00671         else
00672             magick->PreviewFrame();
00673     }
00674 }

Generated on Sun Mar 11 22:11:44 2007 for Kino by  doxygen 1.4.2