--- ../kino-0.7.6-orig/kino.glade	2005-05-16 06:10:22.000000000 +0200
+++ ./kino.glade	2005-10-03 21:52:12.000000000 +0200
@@ -11065,6 +11065,25 @@
 				      <property name="fill">True</property>
 				    </packing>
 				  </child>
+
+				  <child>
+				    <widget class="GtkCheckButton" id="cb_virtualdub">
+				      <property name="visible">True</property>
+				      <property name="can_focus">True</property>
+				      <property name="label" translatable="yes">virtual dub</property>
+				      <property name="use_underline">True</property>
+				      <property name="relief">GTK_RELIEF_NORMAL</property>
+				      <property name="focus_on_click">True</property>
+				      <property name="active">False</property>
+				      <property name="inconsistent">False</property>
+				      <property name="draw_indicator">True</property>
+				    </widget>
+				    <packing>
+				      <property name="padding">0</property>
+				      <property name="expand">False</property>
+				      <property name="fill">False</property>
+				    </packing>
+				  </child>
 				</widget>
 				<packing>
 				  <property name="padding">0</property>
diff -ur ../kino-0.7.6-orig/src/framedisplayer.cc ./src/framedisplayer.cc
--- ../kino-0.7.6-orig/src/framedisplayer.cc	2005-04-02 01:02:59.000000000 +0200
+++ ./src/framedisplayer.cc	2005-10-02 15:12:44.000000000 +0200
@@ -32,6 +32,9 @@
 #include "framedisplayer.h"
 #include "preferences.h"
 #include "frame.h"
+#include "kino_av_pipe.h"
+
+void l_(char* file, long line, const char* func, char* fmt, ...);
 
 FrameDisplayer::FrameDisplayer() : 
 	displayer( NULL ), 
@@ -70,9 +73,18 @@
 	}
 }
 
+bool virtualdub = false;
+char* virtualdub_file = NULL;
+bool virtualdub_reopen = false;
+int virtualdub_framestart = 0;
+int virtualdub_frameoffset = 0;
 
-void FrameDisplayer::PutSound( Frame &frame )
+void FrameDisplayer::PutSound( Frame &frame, int pos )
 {
+        static KinoAudioInput * wav = NULL;
+        static AudioResample<int16_le_t,int16_ne_t> *wresampler;
+	int16_t temp[ 4 * DV_AUDIO_MAX_SAMPLES * 2 ];
+
 	static Preferences & prefs = Preferences::getInstance();
 	if ( prefs.enableAudio )
 	{
@@ -88,15 +100,47 @@
 				if ( resampler == NULL )
 					resampler = AudioResampleFactory<int16_ne_t,int16_ne_t>::createAudioResample(
 									AUDIO_RESAMPLE_SRC_SINC_FASTEST, audio_sampling_rate );
-				resampler->Resample( frame );
-				if ( resampler->size )
-					dv_oss_player( audio_device, resampler->output, resampler->size );
+
+                                if ( virtualdub && virtualdub_file && pos != -1 ) {
+                                  if ( wav == NULL || virtualdub_reopen ) {
+                                    wav = KinoAudioInputFactory::CreateAudioInput( virtualdub_file );
+                                    if ( wav ) {
+                                      l3( "File %s has a frequency of %d Hz and lasts %.02f seconds", virtualdub_file,
+                                          wav->GetFrequency(), ( double ) wav->GetNumberOfSamples() / ( double ) wav->GetFrequency() );
+                                      wresampler = AudioResampleFactory<int16_ne_t,int16_ne_t>::createAudioResample( AUDIO_RESAMPLE_SRC_SINC_FASTEST, audio_sampling_rate );
+                                    } else {
+                                      l0( "Invalid file selected." );
+                                    }
+                                  }
+                                  virtualdub_reopen = false;
+                                } else {
+                                  wav = NULL;
+                                }
+
+                                if ( virtualdub && wav && pos >= virtualdub_framestart) {
+                                  memset( temp, 0, sizeof( temp ) );
+                                  int samples_per_frame = ((double)1)/25*wav->GetFrequency();
+                                  wav->Seek( (pos-virtualdub_framestart+virtualdub_frameoffset) * samples_per_frame*4 );
+                                  if ( !wav->Get( temp, samples_per_frame*4 ) ) {
+                                    printf( "No more?\n" );
+                                  } else {
+                                    wresampler->Resample( temp, wav->GetFrequency(), 2, samples_per_frame );
+                                    if ( wresampler->size ) {
+                                      dv_oss_player( audio_device, wresampler->output, wresampler->size );
+                                    }
+                                  }
+                                } else {
+                                  resampler->Resample( frame );
+                                  if ( resampler->size ) {
+                                    dv_oss_player( audio_device, resampler->output, resampler->size );
+                                  }
+                                }
 			}
 		}
 	}
 }
 
-void FrameDisplayer::Put( Frame &frame, GtkWidget *drawingarea, gboolean no_audio )
+void FrameDisplayer::Put( Frame &frame, GtkWidget *drawingarea, gboolean no_audio, int pos )
 {
 	if ( frame.GetWidth() > 0 && frame.GetHeight() > 0 )
 	{
@@ -118,7 +162,7 @@
 				break;
 			}
 			if ( !no_audio )
-				PutSound( frame );
+				PutSound( frame, pos );
 			if ( prevIsWide != frame.IsWide() )
 			{
 				prevIsWide = frame.IsWide();
diff -ur ../kino-0.7.6-orig/src/framedisplayer.h ./src/framedisplayer.h
--- ../kino-0.7.6-orig/src/framedisplayer.h	2005-04-02 01:02:59.000000000 +0200
+++ ./src/framedisplayer.h	2005-10-01 01:15:32.000000000 +0200
@@ -51,8 +51,8 @@
 	FrameDisplayer();
 	~FrameDisplayer();
 	void CloseSound();
-	void Put( Frame &frame, GtkWidget *drawingarea, gboolean no_audio );
-	void PutSound( Frame &frame );
+	void Put( Frame &frame, GtkWidget *drawingarea, gboolean no_audio, int pos );
+	void PutSound( Frame &frame, int pos );
 	void PutGDKRGB32( Frame &frame, GtkWidget *drawingarea );
 private:
 	bool prevIsWide;
diff -ur ../kino-0.7.6-orig/src/kino_common.cc ./src/kino_common.cc
--- ../kino-0.7.6-orig/src/kino_common.cc	2005-05-16 04:26:47.000000000 +0200
+++ ./src/kino_common.cc	2005-10-01 18:46:36.000000000 +0200
@@ -1471,6 +1471,78 @@
 	}
 }
 
+extern bool virtualdub;
+extern char* virtualdub_file;
+extern bool virtualdub_reopen;
+extern int virtualdub_framestart;
+extern int virtualdub_frameoffset;
+
+void cb_vd_act (GtkEntry * e, GtkDialog * data)
+{
+  gtk_dialog_response(GTK_DIALOG(data), GTK_RESPONSE_NONE);
+}
+
+void cb_virtualdub_toggled (GtkCheckButton *cb, gpointer data)
+{
+  KinoCommon * common = (KinoCommon*) data;
+  if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb)) ) {
+    virtualdub = true;
+    GtkWidget *dialog, *label, *entry, *hb_framestart, *lbl_framestart, *sb_framestart, *hb_frameoffset, *lbl_frameoffset, *sb_frameoffset;
+    dialog = gtk_dialog_new_with_buttons ("Choose virtual dub file",
+                                          NULL,
+                                          GTK_DIALOG_DESTROY_WITH_PARENT,
+                                          GTK_STOCK_OK,
+                                          GTK_RESPONSE_NONE,
+                                          NULL);
+
+   label = gtk_label_new ( "Choose virtual dub file:" );
+   gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+                      label);
+
+   entry = gtk_entry_new();
+   if ( virtualdub_file ) {
+     gtk_entry_set_text( GTK_ENTRY(entry), virtualdub_file );
+   }
+   g_signal_connect (entry,
+                     "activate", 
+                     G_CALLBACK (cb_vd_act),
+                     (gpointer)dialog);
+   gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+                      entry);
+
+   hb_framestart = gtk_hbox_new(FALSE, 5);
+   lbl_framestart = gtk_label_new( "Start only at frame: " );
+   gtk_box_pack_start(GTK_BOX(hb_framestart), lbl_framestart, FALSE, FALSE, 0);
+   sb_framestart = gtk_spin_button_new_with_range(0, common->getPlayList()->GetNumFrames(), 1);
+   gtk_spin_button_set_value(GTK_SPIN_BUTTON(sb_framestart), (int)virtualdub_framestart);
+   gtk_box_pack_end(GTK_BOX(hb_framestart), sb_framestart, FALSE, FALSE, 0);
+   gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+                      hb_framestart);
+
+   hb_frameoffset = gtk_hbox_new(FALSE, 5);
+   lbl_frameoffset = gtk_label_new( "Frame offset in dub: " );
+   gtk_box_pack_start(GTK_BOX(hb_frameoffset), lbl_frameoffset, FALSE, FALSE, 0);
+   sb_frameoffset = gtk_spin_button_new_with_range(0, 100000, 1);
+   gtk_spin_button_set_value(GTK_SPIN_BUTTON(sb_frameoffset), (int)virtualdub_frameoffset);
+   gtk_box_pack_end(GTK_BOX(hb_frameoffset), sb_frameoffset, FALSE, FALSE, 0);
+   gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+                      hb_frameoffset);
+
+   gtk_widget_show_all(dialog);
+
+   gtk_dialog_run(GTK_DIALOG(dialog));
+   virtualdub_file = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
+   virtualdub_framestart = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sb_framestart));
+   virtualdub_frameoffset = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sb_frameoffset));
+   virtualdub_reopen = true;
+
+   gtk_widget_destroy(dialog);
+
+  } else {
+    virtualdub = false;
+  }
+}
+
 /** Trigger the start action of the current page. This method is also responsible
 	for determining the state of the main page buttons and widgets.
 */
@@ -1479,6 +1554,8 @@
 {
 	gtk_label_set_text( GTK_LABEL( lookup_widget( getWidget(), "position_label_current" ) ), "" );
 	gtk_label_set_text( GTK_LABEL( lookup_widget( getWidget(), "position_label_total" ) ), "" );
+	g_signal_connect( lookup_widget( getWidget(), "cb_virtualdub" ), "toggled",
+				G_CALLBACK(cb_virtualdub_toggled), this );
 	
 	getCurrentPage() ->start();
 	activateWidgets();
diff -ur ../kino-0.7.6-orig/src/oss.c ./src/oss.c
--- ../kino-0.7.6-orig/src/oss.c	2003-11-25 21:51:15.000000000 +0100
+++ ./src/oss.c	2005-10-01 02:58:37.000000000 +0200
@@ -245,3 +245,23 @@
 		free( oss );
 	}
 } /* dv_oss_close */
+
+
+char * vasprintf_(const char *msg, va_list args)
+{
+        char s[8192];
+        vsnprintf(s, sizeof(s), msg, args);
+        return strdup(s);
+}
+
+void l_(char* file, long line, const char* func, char* fmt, ...)
+{
+    char *msg;
+    va_list args;
+    va_start(args, fmt);
+    msg = vasprintf_(fmt, args); // segfault later if no more memory :)
+    va_end(args);
+    fprintf(stderr, "%s:%ld(%s): %s\n",
+                    file, line, func, msg);
+    free(msg);
+}
diff -ur ../kino-0.7.6-orig/src/page_capture.cc ./src/page_capture.cc
--- ../kino-0.7.6-orig/src/page_capture.cc	2005-05-19 00:18:23.000000000 +0200
+++ ./src/page_capture.cc	2005-10-01 01:17:59.000000000 +0200
@@ -1351,7 +1351,7 @@
 	if ( frameBuffer[ framePosition ] == NULL )
 		common->clearPreview( frameArea );
 	else
-		displayer->Put( *frameBuffer[ framePosition ], GTK_WIDGET( frameArea ), TRUE );
+		displayer->Put( *frameBuffer[ framePosition ], GTK_WIDGET( frameArea ), TRUE, -1 );
 }
 
 
@@ -1516,7 +1516,7 @@
 				// Play audio
 				if ( audioOn && !critical_mass )
 					if ( writer == NULL || ( writer != NULL && Preferences::getInstance().preview_capture ) )
-						displayer->PutSound( *frame );
+						displayer->PutSound( *frame, -1 );
 
 				// All access to the writer is protected
 				pthread_mutex_lock( &writerlock );
@@ -1577,7 +1577,7 @@
 				{
 					if ( writer == NULL || ( writer != NULL && Preferences::getInstance().preview_capture ) )
 					{
-						displayer->Put( *frameBuffer[ position ], drawingarea, TRUE );
+						displayer->Put( *frameBuffer[ position ], drawingarea, TRUE, -1 );
 					}
 				}
 
diff -ur ../kino-0.7.6-orig/src/page_editor.cc ./src/page_editor.cc
--- ../kino-0.7.6-orig/src/page_editor.cc	2004-11-06 01:25:15.000000000 +0100
+++ ./src/page_editor.cc	2005-10-01 02:58:19.000000000 +0200
@@ -299,7 +299,7 @@
 		common->showFrameInfo( i );
 		if ( dv1394 != NULL )
 			dv1394->SendFrame( *frame ) ;
-		getFrameDisplayer() ->Put( *frame, GTK_WIDGET( frameArea ), no_audio );
+		getFrameDisplayer() ->Put( *frame, GTK_WIDGET( frameArea ), no_audio, i );
 		lastFrameShown = i;
 		GetFramePool( ) ->DoneWithFrame( frame );
 	}
@@ -1267,7 +1267,7 @@
 	else
 	{
 		DrawBar( position );
-		getFrameDisplayer() ->Put( frame, GTK_WIDGET( frameArea ), TRUE );
+		getFrameDisplayer() ->Put( frame, GTK_WIDGET( frameArea ), TRUE, position );
 		showFrameInfo( common->g_currentFrame, frame );
 	}
 }
@@ -1571,8 +1571,8 @@
 					common->g_currentFrame = frameNumber[ tail ];
 					pending --;
 
 					if ( prefs.enableAudio && ( frameNumber[ tail ] != lastFrame || ctl->rate > 1 ) )
-						common->getPageEditor() ->getFrameDisplayer() ->PutSound( *frameContent[ tail ] );
+						common->getPageEditor() ->getFrameDisplayer() ->PutSound( *frameContent[ tail ], newFrame );
 					gdk_threads_enter();
 					common->getPageEditor() ->showFrame( frameNumber[ tail ], *frameContent[ tail ] );
 					gdk_flush();
@@ -1652,7 +1654,7 @@
 					// Play the audio
 					if ( prefs.enableAudio && ( frameNumber[ tail ] != lastFrame || ctl->rate > 1 ) )
 					{
-						common->getPageEditor() ->getFrameDisplayer() ->PutSound( *frameContent[ tail ] );
+						common->getPageEditor() ->getFrameDisplayer() ->PutSound( *frameContent[ tail ], common->g_currentFrame );
 					}
 					else if ( ctl->step == 0 && !prefs.audioScrub )
 					{
diff -ur ../kino-0.7.6-orig/src/page_trim.cc ./src/page_trim.cc
--- ../kino-0.7.6-orig/src/page_trim.cc	2004-11-06 01:25:16.000000000 +0100
+++ ./src/page_trim.cc	2005-10-01 18:15:06.000000000 +0200
@@ -992,7 +992,7 @@
 		common->showFrameInfo( i );
 		if ( dv1394 != NULL )
 			dv1394->SendFrame( frame );
-		getFrameDisplayer() ->Put( frame, GTK_WIDGET( frameArea ), no_audio );
+		getFrameDisplayer() ->Put( frame, GTK_WIDGET( frameArea ), no_audio, i );
 		lastPos = i;
 		skipPosUpdate = TRUE;
 		gtk_adjustment_set_value( trim_adj[ TRIM_ADJ_POS ], ( gfloat ) i );
@@ -1010,7 +1010,7 @@
 	}
 	else
 	{
-		getFrameDisplayer() ->Put( frame, GTK_WIDGET( frameArea ), TRUE );
+		getFrameDisplayer() ->Put( frame, GTK_WIDGET( frameArea ), TRUE, position );
 		common->showFrameInfo( position );
 		skipPosUpdate = TRUE;
 		gtk_adjustment_set_value( trim_adj[ TRIM_ADJ_POS ], ( gfloat ) position );
@@ -2089,7 +2089,7 @@
 
 					// If audio is enabled and some other conditions
 					if ( prefs.enableAudio && ( frameNumber[ tail ] != lastFrame || ctl->rate > 1 ) )
-						common->getPageTrim() ->getFrameDisplayer() ->PutSound( *frameContent[ tail ] );
+						common->getPageTrim() ->getFrameDisplayer() ->PutSound( *frameContent[ tail ], newFrame );
 
 					// Show the corresponding image
 					gdk_threads_enter();
@@ -2165,7 +2165,7 @@
 					// Play the audio
 					if ( prefs.enableAudio && ( frameNumber[ tail ] != lastFrame || ctl->rate > 1 ) )
 					{
-						common->getPageTrim() ->getFrameDisplayer() ->PutSound( *frameContent[ tail ] );
+						common->getPageTrim() ->getFrameDisplayer() ->PutSound( *frameContent[ tail ], common->g_currentFrame );
 						if ( pending < 10 )
 						{
 							// Give the reader more time

