00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024
00025 #include <iostream>
00026
00027
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
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
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
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
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
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
00611 this->menu = menu;
00612 this->container = container;
00613
00614
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
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
00657 if ( selected_filter != NULL )
00658 selected_filter->DetachWidgets( container );
00659
00660
00661 selected_filter = Get();
00662
00663
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 }