CVS Root: /cvs/gstreamer Module: gst-plugins-bad Changes by: slomo Date: Sun Aug 31 2008 12:20:47 UTC Log message: Patch by: Rov Juvano <rovjuvano at users dot sourceforge dot net> * configure.ac: * docs/plugins/Makefile.am: * docs/plugins/gst-plugins-bad-plugins-docs.sgml: * docs/plugins/gst-plugins-bad-plugins-sections.txt: * docs/plugins/inspect/plugin-scaletempo.xml: * examples/scaletempo/Makefile.am: * examples/scaletempo/demo-gui.c: (pop_status_bar), (status_bar_printf), (demo_gui_seek_bar_format), (update_position), (demo_gui_seek_bar_change), (demo_gui_do_change_rate), (demo_gui_do_set_rate), (demo_gui_do_rate_entered), (demo_gui_do_toggle_advanced), (demo_gui_do_toggle_disabled), (demo_gui_do_seek), (demo_gui_do_play), (demo_gui_do_pause), (demo_gui_do_play_pause), (demo_gui_do_open_file), (demo_gui_do_playlist_prev), (demo_gui_do_playlist_next), (demo_gui_do_about_dialog), (demo_gui_do_quit), (demo_gui_request_set_stride), (demo_gui_request_set_overlap), (demo_gui_request_set_search), (demo_gui_rate_changed), (demo_gui_playing_started), (demo_gui_playing_paused), (demo_gui_playing_ended), (demo_gui_player_errored), (demo_gui_stride_changed), (demo_gui_overlap_changed), (demo_gui_search_changed), (demo_gui_set_player_func), (demo_gui_set_playlist_func), (build_gvalue_array), (create_action), (demo_gui_show_func), (demo_gui_set_player), (demo_gui_set_playlist), (demo_gui_show), (demo_gui_get_property), (demo_gui_set_property), (demo_gui_init), (demo_gui_class_init), (demo_gui_get_type): * examples/scaletempo/demo-gui.h: * examples/scaletempo/demo-main.c: (handle_error_message), (handle_quit), (main): * examples/scaletempo/demo-player.c: (no_pipeline), (demo_player_event_listener), (demo_player_state_changed_cb), (demo_player_eos_cb), (demo_player_build_pipeline), (_set_rate), (demo_player_scale_rate_func), (demo_player_set_rate_func), (_set_state_and_wait), (demo_player_load_uri_func), (demo_player_play_func), (demo_player_pause_func), (_seek_to), (demo_player_seek_by_func), (demo_player_seek_to_func), (demo_player_get_position_func), (demo_player_get_duration_func), (demo_player_scale_rate), (demo_player_set_rate), (demo_player_load_uri), (demo_player_play), (demo_player_pause), (demo_player_seek_by), (demo_player_seek_to), (demo_player_get_position), (demo_player_get_duration), (demo_player_get_property), (demo_player_set_property), (demo_player_init), (demo_player_class_init), (demo_player_get_type): * examples/scaletempo/demo-player.h: * gst/scaletempo/Makefile.am: * gst/scaletempo/gstscaletempo.c: (best_overlap_offset_float), (best_overlap_offset_s16), (output_overlap_float), (output_overlap_s16), (fill_queue), (reinit_buffers), (gst_scaletempo_transform), (gst_scaletempo_transform_size), (gst_scaletempo_sink_event), (gst_scaletempo_set_caps), (gst_scaletempo_get_property), (gst_scaletempo_set_property), (gst_scaletempo_base_init), (gst_scaletempo_class_init), (gst_scaletempo_init): * gst/scaletempo/gstscaletempo.h: * gst/scaletempo/gstscaletempoplugin.c: (plugin_init): Add scaletempo plugin, which allows to scale the speed of audio without changing the pitch by handling seeks with a rate!=1.0. Integrate it into the docs and add the example application for it. Fixes bug #537700. Modified files: . : ChangeLog configure.ac docs/plugins : Makefile.am gst-plugins-bad-plugins-docs.sgml gst-plugins-bad-plugins-sections.txt Added files: docs/plugins/inspect: plugin-scaletempo.xml examples/scaletempo: Makefile.am demo-gui.c demo-gui.h demo-main.c demo-player.c demo-player.h gst/scaletempo : Makefile.am gstscaletempo.c gstscaletempo.h gstscaletempoplugin.c Links: http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/ChangeLog.diff?r1=1.3526&r2=1.3527 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/configure.ac.diff?r1=1.888&r2=1.889 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/docs/plugins/Makefile.am.diff?r1=1.59&r2=1.60 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/docs/plugins/gst-plugins-bad-plugins-docs.sgml.diff?r1=1.65&r2=1.66 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/docs/plugins/gst-plugins-bad-plugins-sections.txt.diff?r1=1.65&r2=1.66 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/docs/plugins/inspect/plugin-scaletempo.xml?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/examples/scaletempo/Makefile.am?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/examples/scaletempo/demo-gui.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/examples/scaletempo/demo-gui.h?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/examples/scaletempo/demo-main.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/examples/scaletempo/demo-player.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/examples/scaletempo/demo-player.h?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/gst/scaletempo/Makefile.am?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/gst/scaletempo/gstscaletempo.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/gst/scaletempo/gstscaletempo.h?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-bad/gst/scaletempo/gstscaletempoplugin.c?rev=1.1&content-type=text/vnd.viewcvs-markup ====Begin Diffs==== Index: ChangeLog =================================================================== RCS file: /cvs/gstreamer/gst-plugins-bad/ChangeLog,v retrieving revision 1.3526 retrieving revision 1.3527 diff -u -d -r1.3526 -r1.3527 --- ChangeLog 30 Aug 2008 20:21:59 -0000 1.3526 +++ ChangeLog 31 Aug 2008 12:20:30 -0000 1.3527 @@ -1,3 +1,68 @@ +2008-08-31 Sebastian Dröge <seb...@co...> + + Patch by: Rov Juvano <rovjuvano at users dot sourceforge dot net> + * configure.ac: + * docs/plugins/Makefile.am: + * docs/plugins/gst-plugins-bad-plugins-docs.sgml: + * docs/plugins/gst-plugins-bad-plugins-sections.txt: + * docs/plugins/inspect/plugin-scaletempo.xml: + * examples/scaletempo/Makefile.am: + * examples/scaletempo/demo-gui.c: (pop_status_bar), + (status_bar_printf), (demo_gui_seek_bar_format), (update_position), + (demo_gui_seek_bar_change), (demo_gui_do_change_rate), + (demo_gui_do_set_rate), (demo_gui_do_rate_entered), + (demo_gui_do_toggle_advanced), (demo_gui_do_toggle_disabled), + (demo_gui_do_seek), (demo_gui_do_play), (demo_gui_do_pause), + (demo_gui_do_play_pause), (demo_gui_do_open_file), + (demo_gui_do_playlist_prev), (demo_gui_do_playlist_next), + (demo_gui_do_about_dialog), (demo_gui_do_quit), + (demo_gui_request_set_stride), (demo_gui_request_set_overlap), + (demo_gui_request_set_search), (demo_gui_rate_changed), + (demo_gui_playing_started), (demo_gui_playing_paused), + (demo_gui_playing_ended), (demo_gui_player_errored), + (demo_gui_stride_changed), (demo_gui_overlap_changed), + (demo_gui_search_changed), (demo_gui_set_player_func), + (demo_gui_set_playlist_func), (build_gvalue_array), + (create_action), (demo_gui_show_func), (demo_gui_set_player), + (demo_gui_set_playlist), (demo_gui_show), (demo_gui_get_property), + (demo_gui_set_property), (demo_gui_init), (demo_gui_class_init), + (demo_gui_get_type): + * examples/scaletempo/demo-gui.h: + * examples/scaletempo/demo-main.c: (handle_error_message), + (handle_quit), (main): + * examples/scaletempo/demo-player.c: (no_pipeline), + (demo_player_event_listener), (demo_player_state_changed_cb), + (demo_player_eos_cb), (demo_player_build_pipeline), (_set_rate), + (demo_player_scale_rate_func), (demo_player_set_rate_func), + (_set_state_and_wait), (demo_player_load_uri_func), + (demo_player_play_func), (demo_player_pause_func), (_seek_to), + (demo_player_seek_by_func), (demo_player_seek_to_func), + (demo_player_get_position_func), (demo_player_get_duration_func), + (demo_player_scale_rate), (demo_player_set_rate), + (demo_player_load_uri), (demo_player_play), (demo_player_pause), + (demo_player_seek_by), (demo_player_seek_to), + (demo_player_get_position), (demo_player_get_duration), + (demo_player_get_property), (demo_player_set_property), + (demo_player_init), (demo_player_class_init), + (demo_player_get_type): + * examples/scaletempo/demo-player.h: + * gst/scaletempo/Makefile.am: + * gst/scaletempo/gstscaletempo.c: (best_overlap_offset_float), + (best_overlap_offset_s16), (output_overlap_float), + (output_overlap_s16), (fill_queue), (reinit_buffers), + (gst_scaletempo_transform), (gst_scaletempo_transform_size), + (gst_scaletempo_sink_event), (gst_scaletempo_set_caps), + (gst_scaletempo_get_property), (gst_scaletempo_set_property), + (gst_scaletempo_base_init), (gst_scaletempo_class_init), + (gst_scaletempo_init): + * gst/scaletempo/gstscaletempo.h: + * gst/scaletempo/gstscaletempoplugin.c: (plugin_init): + Add scaletempo plugin, which allows to scale the speed of audio without + changing the pitch by handling seeks with a rate!=1.0. + Integrate it into the docs and add the example application for it. + Fixes bug #537700. 2008-08-30 David Schleef <ds...@sc...> * ext/dirac/gstdiracenc.cc: Fix some memleaks. Index: configure.ac RCS file: /cvs/gstreamer/gst-plugins-bad/configure.ac,v retrieving revision 1.888 retrieving revision 1.889 diff -u -d -r1.888 -r1.889 --- configure.ac 29 Aug 2008 13:57:39 -0000 1.888 +++ configure.ac 31 Aug 2008 12:20:31 -0000 1.889 @@ -266,6 +266,7 @@ AG_GST_CHECK_PLUGIN(rawparse) AG_GST_CHECK_PLUGIN(real) AG_GST_CHECK_PLUGIN(rtpmanager) +AG_GST_CHECK_PLUGIN(scaletempo) AG_GST_CHECK_PLUGIN(sdp) AG_GST_CHECK_PLUGIN(selector) AG_GST_CHECK_PLUGIN(speed) @@ -1406,6 +1407,7 @@ gst/pcapparse/Makefile gst/rawparse/Makefile gst/rtpmanager/Makefile +gst/scaletempo/Makefile gst/sdp/Makefile gst/selector/Makefile gst/speed/Makefile @@ -1437,6 +1439,7 @@ examples/Makefile examples/app/Makefile examples/directfb/Makefile +examples/scaletempo/Makefile examples/switch/Makefile ext/amrwb/Makefile ext/alsaspdif/Makefile Index: Makefile.am RCS file: /cvs/gstreamer/gst-plugins-bad/docs/plugins/Makefile.am,v retrieving revision 1.59 retrieving revision 1.60 diff -u -d -r1.59 -r1.60 --- Makefile.am 21 Aug 2008 13:22:29 -0000 1.59 +++ Makefile.am 31 Aug 2008 12:20:31 -0000 1.60 @@ -135,6 +135,7 @@ $(top_srcdir)/gst/rtpmanager/gstrtpptdemux.h \ $(top_srcdir)/gst/rtpmanager/gstrtpsession.h \ $(top_srcdir)/gst/rtpmanager/gstrtpssrcdemux.h \ + $(top_srcdir)/gst/scaletempo/gstscaletempo.h \ $(top_srcdir)/gst/sdp/gstsdpdemux.h \ $(top_srcdir)/gst/selector/gstinputselector.h \ $(top_srcdir)/gst/selector/gstoutputselector.h \ Index: gst-plugins-bad-plugins-docs.sgml RCS file: /cvs/gstreamer/gst-plugins-bad/docs/plugins/gst-plugins-bad-plugins-docs.sgml,v retrieving revision 1.65 retrieving revision 1.66 diff -u -d -r1.65 -r1.66 --- gst-plugins-bad-plugins-docs.sgml 22 Aug 2008 06:27:57 -0000 1.65 +++ gst-plugins-bad-plugins-docs.sgml 31 Aug 2008 12:20:31 -0000 1.66 @@ -46,6 +46,7 @@ <xi:include href="xml/element-mythtvsrc.xml" /> <xi:include href="xml/element-nuvdemux.xml" /> <xi:include href="xml/element-output-selector.xml" /> + <xi:include href="xml/element-scaletempo.xml" /> <xi:include href="xml/element-sdlaudiosink.xml" /> <xi:include href="xml/element-sdlvideosink.xml" /> <xi:include href="xml/element-sdpdemux.xml" /> @@ -112,6 +113,7 @@ <xi:include href="xml/plugin-rawparse.xml" /> <xi:include href="xml/plugin-real.xml" /> <xi:include href="xml/plugin-rfbsrc.xml" /> + <xi:include href="xml/plugin-scaletempo.xml" /> <xi:include href="xml/plugin-sdl.xml" /> <xi:include href="xml/plugin-sdp.xml" /> <xi:include href="xml/plugin-selector.xml" /> Index: gst-plugins-bad-plugins-sections.txt RCS file: /cvs/gstreamer/gst-plugins-bad/docs/plugins/gst-plugins-bad-plugins-sections.txt,v --- gst-plugins-bad-plugins-sections.txt 21 Aug 2008 13:22:29 -0000 1.65 +++ gst-plugins-bad-plugins-sections.txt 31 Aug 2008 12:20:31 -0000 1.66 @@ -558,6 +558,20 @@ </SECTION> <SECTION> +<FILE>element-scaletempo</FILE> +<TITLE>scaletempo</TITLE> +GstScaletempo +<SUBSECTION Standard> +GstScaletempoClass +GST_IS_SCALETEMPO +GST_IS_SCALETEMPO_CLASS +GST_SCALETEMPO +GST_SCALETEMPO_CLASS +GST_TYPE_SCALETEMPO +gst_scaletempo_get_type +</SECTION> +<SECTION> <FILE>element-sdlaudiosink</FILE> <TITLE>sdlaudiosink</TITLE> GstSDLAudioSink --- NEW FILE: plugin-scaletempo.xml --- <plugin> <name>scaletempo</name> <description>Scale audio tempo in sync with playback rate</description> <filename>../../gst/scaletempo/.libs/libgstscaletempoplugin.so</filename> <basename>libgstscaletempoplugin.so</basename> <version>0.10.8.1</version> <license>LGPL</license> <source>gst-plugins-bad</source> <package>GStreamer</package> <origin>http://gstreamer.freedesktop.org/</origin> <elements> <element> <name>scaletempo</name> <longname>Scaletempo</longname> <class>Filter/Effect/Rate</class> <description>Sync audio tempo with playback rate</description> <author>Rov Juvano <rov...@us...></author> <pads> <caps> <name>sink</name> <direction>sink</direction> <presence>always</presence> <details>audio/x-raw-float, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 2147483647 ], endianness=(int)1234, width=(int)32; audio/x-raw-int, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 2147483647 ], endianness=(int)1234, width=(int)16, depth=(int)16, signed=(boolean)true</details> </caps> <name>src</name> <direction>source</direction> </pads> </element> </elements> </plugin>--- NEW FILE: Makefile.am --- noinst_PROGRAMS = scaletempo-demo scaletempo_demo_SOURCES = demo-main.c demo-player.c demo-gui.c scaletempo_demo_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GTK_CFLAGS) scaletempo_demo_LDFLAGS = $(GST_LIBS) $(GST_PLUGINS_BASE_LIBS) $(GTK_LIBS) -lgstinterfaces-@GST_MAJORMINOR@ noinst_HEADERS = demo-player.h demo-gui.h --- NEW FILE: demo-gui.c --- /* demo-gui.c * Copyright (C) 2008 Rov Juvano <rov...@us...> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifdef HAVE_CONFIG_H #include "config.h" [...1219 lines suppressed...] demo_gui_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static const GTypeInfo info = { sizeof /* Class */ (DemoGuiClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) demo_gui_class_init, (GClassFinalizeFunc) NULL, (gconstpointer) NULL, /* class_data */ sizeof /* Instance */ (DemoGui), /* n_preallocs */ 0, (GInstanceInitFunc) demo_gui_init, (const GTypeValueTable *) NULL }; type = g_type_register_static (G_TYPE_OBJECT, "DemoGui", &info, 0); } return type; } --- NEW FILE: demo-gui.h --- /* demo-gui.h #ifndef __DEMO_GUI_H_INCLUDED_ #define __DEMO_GUI_H_INCLUDED_ #include <glib-object.h> #include "demo-player.h" G_BEGIN_DECLS #define DEMO_TYPE_GUI (demo_gui_get_type()) #define DEMO_GUI(o) (G_TYPE_CHECK_INSTANCE_CAST((o), DEMO_TYPE_GUI, DemoGui)) #define DEMO_IS_GUI(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), DEMO_TYPE_GUI)) #define DEMO_GUI_TYPE(o) (G_TYPE_FROM_INSTANCE (o)) #define DEMO_GUI_TYPE_NAME(o) (g_type_name (DEMO_GUI_GUI (o))) #define DEMO_GUI_CLASS(c) (G_TYPE_CHECK_CLASS_CAST((c), DEMO_TYPE_GUI, DemoGuiClass)) #define DEMO_IS_GUI_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE((c), DEMO_TYPE_GUI)) #define DEMO_GUI_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DEMO_TYPE_GUI, DemoGuiClass)) typedef struct _DemoGui DemoGui; typedef struct _DemoGuiClass DemoGuiClass; struct _DemoGui GObject parent; }; struct _DemoGuiClass GObjectClass parent; void (*set_player) (DemoGui *gui, DemoPlayer *player); void (*set_playlist) (DemoGui *gui, GList *uris); void (*show) (DemoGui *gui); GType demo_gui_get_type (void); void demo_gui_set_player (DemoGui *gui, DemoPlayer *player); void demo_gui_set_playlist (DemoGui *gui, GList *uris); void demo_gui_show (DemoGui *gui); G_END_DECLS #endif /* __DEMO_GUI_H_INCLUDED_ */ --- NEW FILE: demo-main.c --- /* main.c #endif #include "demo-gui.h" extern GOptionGroup *gtk_get_option_group (gboolean); extern GOptionGroup *gst_init_get_option_group (void); static void handle_error_message (DemoPlayer * player, const gchar * msg, gpointer data) const gchar *format = (const gchar *) data; g_print (format, msg); handle_quit (gpointer source, gpointer data) g_main_loop_quit ((GMainLoop *) data); int main (int argc, char *argv[]) DemoGui *gui; DemoPlayer *player; gchar **uris = NULL; GOptionContext *ctx; GError *err = NULL; GMainLoop *loop; const GOptionEntry entries[] = { {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uris, "Special option that collects any remaining arguments for us"}, {NULL,} }; if (!g_thread_supported ()) g_thread_init (NULL); ctx = g_option_context_new ("uri ..."); g_option_context_add_group (ctx, gst_init_get_option_group ()); g_option_context_add_group (ctx, gtk_get_option_group (FALSE)); g_option_context_add_main_entries (ctx, entries, NULL); if (!g_option_context_parse (ctx, &argc, &argv, &err)) { g_print ("Error initializing: %s\n", err->message); g_error_free (err); return -1; g_option_context_free (ctx); gui = g_object_new (DEMO_TYPE_GUI, NULL); player = g_object_new (DEMO_TYPE_PLAYER, NULL); g_signal_connect (player, "error", G_CALLBACK (handle_error_message), "PLAYER ERROR: %s\n"); g_signal_connect (gui, "error", G_CALLBACK (handle_error_message), "GUI ERROR: %s\n"); demo_gui_set_player (gui, player); loop = g_main_loop_new (NULL, FALSE); g_signal_connect (gui, "quiting", G_CALLBACK (handle_quit), loop); if (uris != NULL) { int i, num = g_strv_length (uris); GList *uri_list = NULL; for (i = 0; i < num; i++) { uri_list = g_list_append (uri_list, uris[i]); } demo_gui_set_playlist (gui, uri_list); demo_gui_show (gui); g_main_loop_run (loop); return 0; --- NEW FILE: demo-player.c --- /* demo-player.c #include "gst/gst.h" #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "demo-player" enum SIGNAL_ERROR, SIGNAL_RATE_CHANGE, SIGNAL_PLAYING_STARTED, SIGNAL_PLAYING_PAUSED, SIGNAL_PLAYING_ENDED, LAST_SIGNAL static guint demo_player_signals[LAST_SIGNAL] = { 0 }; PROP_0, PROP_RATE, PROP_STRIDE, PROP_OVERLAP, PROP_SEARCH, PROP_DISABLED typedef struct _DemoPlayerPrivate gdouble rate; GstElement *scaletempo; GstElement *pipeline; gboolean is_disabled; GstElement *scaletempo_line; GstElement *scalerate_line; gboolean ignore_state_change; } DemoPlayerPrivate; #define DEMO_PLAYER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEMO_TYPE_PLAYER, DemoPlayerPrivate)) static gboolean no_pipeline (DemoPlayer * player) DemoPlayerPrivate *priv = DEMO_PLAYER_GET_PRIVATE (player); if (!priv->pipeline) { g_signal_emit (player, demo_player_signals[SIGNAL_ERROR], 0, "No media loaded"); return TRUE; return FALSE; demo_player_event_listener (GstElement * host, GstEvent * event, gpointer data) DemoPlayer *player = DEMO_PLAYER (data); if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) { gdouble rate, applied_rate; gdouble new_rate; gst_event_parse_new_segment_full (event, NULL, &rate, &applied_rate, NULL, NULL, NULL, NULL); new_rate = rate * applied_rate; if (priv->rate != new_rate) { priv->rate = new_rate; g_signal_emit (player, demo_player_signals[SIGNAL_RATE_CHANGE], 0, new_rate); return TRUE; demo_player_state_changed_cb (GstBus * bus, GstMessage * message, gpointer data) GstState old, new, pending; if (GST_ELEMENT (GST_MESSAGE_SRC (message)) != priv->pipeline) return; gst_message_parse_state_changed (message, &old, &new, &pending); if (pending == GST_STATE_VOID_PENDING) { if (priv->ignore_state_change) { priv->ignore_state_change = FALSE; } else if (new == GST_STATE_PAUSED) { g_signal_emit (player, demo_player_signals[SIGNAL_PLAYING_PAUSED], 0); } else if (new == GST_STATE_PLAYING) { g_signal_emit (player, demo_player_signals[SIGNAL_PLAYING_STARTED], 0); demo_player_eos_cb (GstBus * bus, GstMessage * message, gpointer data) g_signal_emit (player, demo_player_signals[SIGNAL_PLAYING_ENDED], 0); #define MAKE_ELEMENT(line, var, type, name) \ if ( !(var = gst_element_factory_make (type, name) ) ) { \ g_print ("element could not be created: %s/%s\n", type, name); \ return; \ } \ if (line) gst_bin_add (GST_BIN (line), var); #define LINK_ELEMENTS(src, sink) \ if (!gst_element_link (src, sink)) { \ g_warning ("Failed to link elements: %s -> %s", \ GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (sink) ); \ return; \ demo_player_build_pipeline (DemoPlayer * player) GstElement *filter, *playbin, *vsink, *audioline, *format, *resample, *asink; GstPlugin *gconf; GstBus *bus; gboolean has_gconf; const gchar *audiosink_name; GstPad *ghostpad; priv->pipeline = NULL; if (!priv->scaletempo) { filter = priv->scaletempo; MAKE_ELEMENT (NULL, playbin, "playbin", "playbin"); gconf = gst_default_registry_find_plugin ("gconfelements"); has_gconf = (gconf != NULL); gst_object_unref (gconf); if (has_gconf) { MAKE_ELEMENT (NULL, vsink, "gconfvideosink", "vsink"); g_object_set (G_OBJECT (playbin), "video_sink", vsink, NULL); audiosink_name = has_gconf ? "gconfaudiosink" : "autoaudiosink"; audioline = gst_bin_new ("audioline"); gst_bin_add (GST_BIN (audioline), filter); MAKE_ELEMENT (audioline, format, "audioconvert", "format"); MAKE_ELEMENT (audioline, resample, "audioresample", "resample"); MAKE_ELEMENT (audioline, asink, audiosink_name, "audio_sink"); LINK_ELEMENTS (filter, format); LINK_ELEMENTS (format, resample); LINK_ELEMENTS (resample, asink); gst_pad_add_event_probe (gst_element_get_static_pad (asink, "sink"), G_CALLBACK (demo_player_event_listener), player); ghostpad = gst_element_get_static_pad (filter, "sink"); gst_element_add_pad (audioline, gst_ghost_pad_new ("sink", ghostpad)); gst_object_unref (ghostpad); g_object_set (G_OBJECT (playbin), "audio-sink", audioline, NULL); bus = gst_pipeline_get_bus (GST_PIPELINE (playbin)); gst_bus_add_signal_watch (bus); g_signal_connect (bus, "message::state-changed", G_CALLBACK (demo_player_state_changed_cb), player); g_signal_connect (bus, "message::eos", G_CALLBACK (demo_player_eos_cb), player); gst_object_unref (bus); priv->scaletempo = filter; priv->pipeline = playbin; priv->scaletempo_line = audioline; MAKE_ELEMENT (NULL, priv->scalerate_line, "gconfaudiosink", "scaling_audio_sink"); gst_pad_add_event_probe (gst_element_get_static_pad (priv->scalerate_line, "sink"), G_CALLBACK (demo_player_event_listener), player); g_object_ref (priv->scaletempo_line); g_object_ref (priv->scalerate_line); /* method implementations */ _set_rate (DemoPlayer * player, gdouble new_rate, gint second) DemoPlayerPrivate *priv; gint64 pos; GstSeekType seek_type; if (new_rate == 0) { "Cannot set playback to zero. Pausing instead."); demo_player_pause (player); priv = DEMO_PLAYER_GET_PRIVATE (player); if (second < 0) { GstFormat fmt = GST_FORMAT_TIME; seek_type = GST_SEEK_TYPE_SET; if (!gst_element_query_position (priv->pipeline, &fmt, &pos)) { // This should be the default but too many upstream elements seek anyway pos = GST_CLOCK_TIME_NONE; seek_type = GST_SEEK_TYPE_NONE; } else { pos = second * GST_SECOND; if (!gst_element_seek (priv->pipeline, new_rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, seek_type, pos, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) { "Unable to change playback rate"); priv->ignore_state_change = TRUE; demo_player_scale_rate_func (DemoPlayer * player, gdouble scale) if (no_pipeline (player)) if (scale != 1.0) { g_message ("Scaling Rate by: %3.2f", scale); _set_rate (player, priv->rate * scale, -1); demo_player_set_rate_func (DemoPlayer * player, gdouble new_rate) if (priv->rate != new_rate) { g_message ("Setting Rate to: %3.2f", new_rate); _set_rate (player, new_rate, -1); _set_state_and_wait (DemoPlayer * player, GstState new_state, GstClockTime timeout, const gchar * error_msg) GstStateChangeReturn ret = gst_element_set_state (priv->pipeline, new_state); if (ret == GST_STATE_CHANGE_ASYNC) { ret = gst_element_get_state (priv->pipeline, NULL, NULL, timeout); if (ret != GST_STATE_CHANGE_SUCCESS) { g_signal_emit (player, demo_player_signals[SIGNAL_ERROR], 0, error_msg); return FALSE; demo_player_load_uri_func (DemoPlayer * player, gchar * uri) GstState end_state; demo_player_build_pipeline (player); if (!priv->pipeline) { g_signal_emit (player, demo_player_signals[SIGNAL_ERROR], 0, "Could not build player"); return; if (!g_str_has_prefix (uri, "file:///")) { GError *err = NULL; if (g_path_is_absolute (uri)) { uri = g_filename_to_uri (uri, NULL, &err); } else { gchar *curdir = g_get_current_dir (); gchar *absolute_path = g_strconcat (curdir, G_DIR_SEPARATOR_S, uri, NULL); uri = g_filename_to_uri (absolute_path, NULL, &err); g_free (absolute_path); g_free (curdir); if (err) { gchar *msg = g_strconcat ("Could not load uri: ", err->message, NULL); g_signal_emit (player, demo_player_signals[SIGNAL_ERROR], 0, msg); g_message ("Loading URI: %s", uri); end_state = (GST_STATE (priv->pipeline) == GST_STATE_PLAYING) ? GST_STATE_PLAYING : GST_STATE_PAUSED; if (!_set_state_and_wait (player, GST_STATE_NULL, 10 * GST_SECOND, "Unable to load uri")) g_object_set (G_OBJECT (priv->pipeline), "uri", uri, NULL); rate = priv->rate; if (rate && rate != 1.0) { _set_state_and_wait (player, GST_STATE_PAUSED, 10 * GST_SECOND, "Unable to keep playback rate"); _set_rate (player, rate, -1); gst_element_set_state (priv->pipeline, end_state); demo_player_play_func (DemoPlayer * player) GstStateChangeReturn ret; if (GST_STATE (priv->pipeline) == GST_STATE_PLAYING) { "Already playing"); g_debug ("Starting to Play"); ret = gst_element_set_state (priv->pipeline, GST_STATE_PLAYING); if (ret == GST_STATE_CHANGE_FAILURE) { "Unable to start playback"); demo_player_pause_func (DemoPlayer * player) if (GST_STATE (priv->pipeline) == GST_STATE_PAUSED) { "Already paused"); g_debug ("Starting to Pause"); ret = gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); "Unable to pause playback"); _seek_to (DemoPlayer * player, gint new_second) if (!gst_element_seek (priv->pipeline, priv->rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, new_second * GST_SECOND, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) { g_signal_emit (player, demo_player_signals[SIGNAL_ERROR], 0, "Seek failed"); priv->ignore_state_change = TRUE; demo_player_seek_by_func (DemoPlayer * player, gint seconds) gint pos; g_debug ("Seeking by: %i", seconds); pos = demo_player_get_position (player); if (pos < 0) { "Seek-by failed: could not determine position"); _seek_to (player, MAX (0, pos + seconds)); demo_player_seek_to_func (DemoPlayer * player, gint second) gint new_second; g_debug ("Seeking to: %i", second); gint dur = demo_player_get_duration (player); if (dur < 0) { "Seek-to failed: could not determine duration"); new_second = MAX (0, dur + second); new_second = second; _seek_to (player, new_second); static gint demo_player_get_position_func (DemoPlayer * player) GstFormat fmt = GST_FORMAT_TIME; if (!priv->pipeline) if (!gst_element_query_position (priv->pipeline, &fmt, &pos) || pos < 0) { return (gint) (pos / GST_SECOND); demo_player_get_duration_func (DemoPlayer * player) gint64 dur; if (!gst_element_query_duration (priv->pipeline, &fmt, &dur) || dur < 0) { return (gint) (dur / GST_SECOND); /* Method wrappers */ void demo_player_scale_rate (DemoPlayer * player, gdouble scale) g_return_if_fail (DEMO_IS_PLAYER (player)); DEMO_PLAYER_GET_CLASS (player)->scale_rate (player, scale); demo_player_set_rate (DemoPlayer * player, gdouble new_rate) DEMO_PLAYER_GET_CLASS (player)->set_rate (player, new_rate); demo_player_load_uri (DemoPlayer * player, gchar * uri) DEMO_PLAYER_GET_CLASS (player)->load_uri (player, uri); demo_player_play (DemoPlayer * player) DEMO_PLAYER_GET_CLASS (player)->play (player); demo_player_pause (DemoPlayer * player) DEMO_PLAYER_GET_CLASS (player)->pause (player); demo_player_seek_by (DemoPlayer * player, gint seconds) DEMO_PLAYER_GET_CLASS (player)->seek_by (player, seconds); demo_player_seek_to (DemoPlayer * player, gint second) DEMO_PLAYER_GET_CLASS (player)->seek_to (player, second); gint demo_player_get_position (DemoPlayer * player) g_return_val_if_fail (DEMO_IS_PLAYER (player), -1); return DEMO_PLAYER_GET_CLASS (player)->get_position (player); demo_player_get_duration (DemoPlayer * player) return DEMO_PLAYER_GET_CLASS (player)->get_duration (player); /* GObject overrides */ demo_player_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) DemoPlayer *player = DEMO_PLAYER (object); switch (property_id) { case PROP_RATE: g_value_set_double (value, priv->rate); break; case PROP_STRIDE: g_object_get_property (G_OBJECT (priv->scaletempo), "stride", value); case PROP_OVERLAP: g_object_get_property (G_OBJECT (priv->scaletempo), "overlap", value); case PROP_SEARCH: g_object_get_property (G_OBJECT (priv->scaletempo), "search", value); case PROP_DISABLED: g_value_set_boolean (value, priv->is_disabled); default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); demo_player_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) g_object_set_property (G_OBJECT (priv->scaletempo), "stride", value); g_object_set_property (G_OBJECT (priv->scaletempo), "overlap", value); g_object_set_property (G_OBJECT (priv->scaletempo), "search", value); case PROP_DISABLED:{ gdouble rate = priv->rate; gint pos = demo_player_get_position (player); GstState end_state; GstElement *new_sink; priv->is_disabled = g_value_get_boolean (value); g_debug ("Scaletempo: %s", priv->is_disabled ? "disabled" : "enabled"); end_state = (GST_STATE (priv->pipeline) == GST_STATE_PLAYING) ? GST_STATE_PLAYING : GST_STATE_PAUSED; if (!_set_state_and_wait (player, GST_STATE_NULL, 10 * GST_SECOND, "Unable to disable")) break; new_sink = (priv->is_disabled) ? priv->scalerate_line : priv->scaletempo_line; g_object_set (G_OBJECT (priv->pipeline), "audio-sink", new_sink, NULL); if (pos > 0 || (rate && rate != 1.0)) { _set_state_and_wait (player, GST_STATE_PAUSED, 10 * GST_SECOND, "Unable to keep playback position and rate"); _set_rate (player, rate, pos); } gst_element_set_state (priv->pipeline, end_state); /* GTypeInfo functions */ demo_player_init (GTypeInstance * instance, gpointer klass) DemoPlayer *player = (DemoPlayer *) instance; priv->scaletempo = gst_element_factory_make ("scaletempo", "scaletempo"); g_error ("Unable to make scaletempo element."); priv->rate = 1.0; priv->ignore_state_change = FALSE; priv->is_disabled = FALSE; demo_player_class_init (gpointer klass, gpointer class_data) DemoPlayerClass *player_class = (DemoPlayerClass *) klass; GObjectClass *as_object_class = G_OBJECT_CLASS (klass); GType type; g_type_class_add_private (klass, sizeof (DemoPlayerPrivate)); /* DemoPlayer */ player_class->scale_rate = demo_player_scale_rate_func; player_class->set_rate = demo_player_set_rate_func; player_class->load_uri = demo_player_load_uri_func; player_class->play = demo_player_play_func; player_class->pause = demo_player_pause_func; player_class->seek_by = demo_player_seek_by_func; player_class->seek_to = demo_player_seek_to_func; player_class->get_position = demo_player_get_position_func; player_class->get_duration = demo_player_get_duration_func; /* GObject */ as_object_class->get_property = demo_player_get_property; as_object_class->set_property = demo_player_set_property; /* Properties */ g_object_class_install_property (as_object_class, PROP_RATE, g_param_spec_double ("rate", "Rate", "Current playback rate", -128, 128, 1.0, G_PARAM_READABLE)); g_object_class_install_property (as_object_class, PROP_STRIDE, g_param_spec_uint ("stride", "Stride Length", "Length in milliseconds to output each stride", 1, 10000, 60, G_PARAM_READWRITE)); g_object_class_install_property (as_object_class, PROP_OVERLAP, g_param_spec_double ("overlap", "Overlap Length", "Percentage of stride to overlap", 0, 1, .2, G_PARAM_READWRITE)); g_object_class_install_property (as_object_class, PROP_SEARCH, g_param_spec_uint ("search", "Search Length", "Length in milliseconds to search for best overlap position", 0, 10000, 14, G_PARAM_READWRITE)); g_object_class_install_property (as_object_class, PROP_DISABLED, g_param_spec_boolean ("disabled", "disable scaletempo", "Disable scaletempo and scale bothe tempo and pitch", FALSE, /* Signals */ type = G_TYPE_FROM_CLASS (klass); demo_player_signals[SIGNAL_ERROR] = g_signal_new ("error", type, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); demo_player_signals[SIGNAL_RATE_CHANGE] = g_signal_new ("rate-changed", type, g_cclosure_marshal_VOID__DOUBLE, G_TYPE_NONE, 1, G_TYPE_DOUBLE); demo_player_signals[SIGNAL_PLAYING_STARTED] = g_signal_new ("playing-started", type, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); demo_player_signals[SIGNAL_PLAYING_PAUSED] = g_signal_new ("playing-paused", type, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, demo_player_signals[SIGNAL_PLAYING_ENDED] = g_signal_new ("playing-ended", type, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, GType demo_player_get_type (void) sizeof /* Class */ (DemoPlayerClass), (GClassInitFunc) demo_player_class_init, sizeof /* Instance */ (DemoPlayer), (GInstanceInitFunc) demo_player_init, type = g_type_register_static (G_TYPE_OBJECT, "DemoPlayer", &info, 0); --- NEW FILE: demo-player.h --- /* gui.h #ifndef __DEMO_PLAYER_H_INCLUDED_ #define __DEMO_PLAYER_H_INCLUDED_ #define DEMO_TYPE_PLAYER (demo_player_get_type()) #define DEMO_PLAYER(o) (G_TYPE_CHECK_INSTANCE_CAST((o), DEMO_TYPE_PLAYER, DemoPlayer)) #define DEMO_IS_PLAYER(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), DEMO_TYPE_PLAYER)) #define DEMO_PLAYER_TYPE(o) (G_TYPE_FROM_INSTANCE (o)) #define DEMO_PLAYER_TYPE_NAME(o) (g_type_name (DEMO_PLAYER_TYPE (o))) #define DEMO_PLAYER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST((c), DEMO_TYPE_PLAYER, DemoPlayerClass)) #define DEMO_IS_PLAYER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE((c), DEMO_TYPE_PLAYER)) #define DEMO_PLAYER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DEMO_TYPE_PLAYER, DemoPlayerClass)) typedef struct _DemoPlayer DemoPlayer; typedef struct _DemoPlayerClass DemoPlayerClass; struct _DemoPlayer struct _DemoPlayerClass void (*scale_rate) (DemoPlayer *player, gdouble scale); void (*set_rate) (DemoPlayer *player, gdouble new_rate); void (*load_uri) (DemoPlayer *player, gchar *uri); void (*play) (DemoPlayer *player); void (*pause) (DemoPlayer *player); void (*seek_by) (DemoPlayer *player, gint seconds); void (*seek_to) (DemoPlayer *player, gint seconds); gint (*get_position) (DemoPlayer *player); gint (*get_duration) (DemoPlayer *player); GType demo_player_get_type (void); void demo_player_scale_rate (DemoPlayer *player, gdouble scale); void demo_player_set_rate (DemoPlayer *player, gdouble new_rate); void demo_player_load_uri (DemoPlayer *player, gchar *uri); void demo_player_play (DemoPlayer *player); void demo_player_pause (DemoPlayer *player); void demo_player_seek_by (DemoPlayer *player, gint seconds); void demo_player_seek_to (DemoPlayer *player, gint second); gint demo_player_get_position (DemoPlayer *player); gint demo_player_get_duration (DemoPlayer *player); #endif /* __DEMO_PLAYER_H_INCLUDED_ */ --- NEW FILE: Makefile.am --- plugin_LTLIBRARIES = libgstscaletempoplugin.la # sources used to compile this plug-in libgstscaletempoplugin_la_SOURCES = gstscaletempoplugin.c gstscaletempo.c # flags used to compile this plugin # add other _CFLAGS and _LIBS as needed libgstscaletempoplugin_la_CFLAGS = $(GST_CFLAGS) libgstscaletempoplugin_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) libgstscaletempoplugin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) # headers we need but don't want installed noinst_HEADERS = gstscaletempo.h --- NEW FILE: gstscaletempo.c --- /* * GStreamer * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. /** * SECTION:element-scaletempo * <refsect2> * <para> * Scale tempo while maintaining pitch * (WSOLA-like technique with cross correlation) * Inspired by SoundTouch library by Olli Parviainen * </para> * Use Sceletempo to apply playback rates without the chipmunk effect. * <title>Example pipelines</title> * <programlisting> * filesrc location=media.ext ! decodebin name=d \ * d. ! queue ! audioconvert ! audioresample ! scaletempo ! audioconvert ! audioresample ! autoaudiosink \ * d. ! queue ! ffmpegcolorspace ! autovideosink * </programlisting> * OR * playbin uri=... audio_sink="scaletempo ! audioconvert ! audioresample ! autoaudiosink" * When an application sends a seek event with rate != 1.0, Scaletempo applies * the rate change by scaling the tempo without scaling the pitch. * Scaletempo works by producing audio in constant sized chunks (a "stride") but * consuming chunks proportional to the playback rate. * Scaletempo then smooths the output by blending the end of one stride with * the next ("overlap"). * Scaletempo smooths the overlap further by searching within the input buffer * for the best overlap position. Scaletempo uses a statistical cross correlation * (roughly a dot-product). Scaletempo consumes most of its CPU cycles here. * </refsect2> * Note: frame = audio key unit (i.e. one sample for each channel) #include <gst/gst.h> #include <gst/base/gstbasetransform.h> #include <string.h> /* for memset */ #include "gstscaletempo.h" GST_DEBUG_CATEGORY_STATIC (gst_scaletempo_debug); #define GST_CAT_DEFAULT gst_scaletempo_debug /* Filter signals and args */ #define SUPPORTED_CAPS \ GST_STATIC_CAPS ( \ "audio/x-raw-float, " \ "rate = (int) [ 1, MAX ], " \ "channels = (int) [ 1, MAX ], " \ "endianness = (int) BYTE_ORDER, " \ "width = (int) 32;" \ "audio/x-raw-int, " \ "rate = (int) [ 1, MAX ], " \ "width = (int) 16, " \ "depth = (int) 16, " \ "signed = (boolean) true;" \ ) static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS); static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, #define DEBUG_INIT(bla) GST_DEBUG_CATEGORY_INIT (gst_scaletempo_debug, "scaletempo", 0, "scaletempo element"); GST_BOILERPLATE_FULL (GstScaletempo, gst_scaletempo, GstBaseTransform, GST_TYPE_BASE_TRANSFORM, DEBUG_INIT); typedef struct _GstScaletempoPrivate gdouble scale; /* parameters */ guint ms_stride; gdouble percent_overlap; guint ms_search; /* caps */ gboolean use_int; guint samples_per_frame; /* AKA number of channels */ guint bytes_per_sample; guint bytes_per_frame; guint sample_rate; /* stride */ gdouble frames_stride_scaled; gdouble frames_stride_error; guint bytes_stride; gdouble bytes_stride_scaled; guint bytes_queue_max; guint bytes_queued; guint bytes_to_slide; gint8 *buf_queue; /* overlap */ guint samples_overlap; guint samples_standing; guint bytes_overlap; guint bytes_standing; gpointer buf_overlap; gpointer table_blend; void (*output_overlap) (GstScaletempo * scaletempo, gpointer out_buf, guint bytes_off); /* best overlap */ guint frames_search; gpointer buf_pre_corr; gpointer table_window; guint (*best_overlap_offset) (GstScaletempo * scaletempo); /* gstreamer */ gint64 segment_start; /* threads */ gboolean reinit_buffers; } GstScaletempoPrivate; #define GST_SCALETEMPO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_SCALETEMPO, GstScaletempoPrivate)) static guint best_overlap_offset_float (GstScaletempo * scaletempo) GstScaletempoPrivate *p = GST_SCALETEMPO_GET_PRIVATE (scaletempo); gfloat *pw, *po, *ppc, *search_start; gfloat best_corr = G_MININT; guint best_off = 0; gint i, off; pw = p->table_window; po = p->buf_overlap; po += p->samples_per_frame; ppc = p->buf_pre_corr; for (i = p->samples_per_frame; i < p->samples_overlap; i++) { *ppc++ = *pw++ * *po++; search_start = (gfloat *) p->buf_queue + p->samples_per_frame; for (off = 0; off < p->frames_search; off++) { gfloat corr = 0; gfloat *ps = search_start; ppc = p->buf_pre_corr; for (i = p->samples_per_frame; i < p->samples_overlap; i++) { corr += *ppc++ * *ps++; if (corr > best_corr) { best_corr = corr; best_off = off; search_start += p->samples_per_frame; return best_off * p->bytes_per_frame; /* buffer padding for loop optimization: sizeof(gint32) * (loop_size - 1) */ #define UNROLL_PADDING (4*3) best_overlap_offset_s16 (GstScaletempo * scaletempo) gint32 *pw, *ppc; gint16 *po, *search_start; gint64 best_corr = G_MININT64; guint off; glong i; *ppc++ = (*pw++ * *po++) >> 15; search_start = (gint16 *) p->buf_queue + p->samples_per_frame; gint64 corr = 0; gint16 *ps = search_start; ppc += p->samples_overlap - p->samples_per_frame; ps += p->samples_overlap - p->samples_per_frame; i = -(p->samples_overlap - p->samples_per_frame); do { corr += ppc[i + 0] * ps[i + 0]; corr += ppc[i + 1] * ps[i + 1]; corr += ppc[i + 2] * ps[i + 2]; corr += ppc[i + 3] * ps[i + 3]; i += 4; } while (i < 0); output_overlap_float (GstScaletempo * scaletempo, gpointer buf_out, guint bytes_off) gfloat *pout = buf_out; gfloat *pb = p->table_blend; gfloat *po = p->buf_overlap; gfloat *pin = (gfloat *) (p->buf_queue + bytes_off); gint i; for (i = 0; i < p->samples_overlap; i++) { *pout++ = *po - *pb++ * (*po - *pin++); po++; output_overlap_s16 (GstScaletempo * scaletempo, gint16 *pout = buf_out; gint32 *pb = p->table_blend; gint16 *po = p->buf_overlap; gint16 *pin = (gint16 *) (p->buf_queue + bytes_off); *pout++ = *po - ((*pb++ * (*po - *pin++)) >> 16); fill_queue (GstScaletempo * scaletempo, GstBuffer * buf_in, guint offset) guint bytes_in = GST_BUFFER_SIZE (buf_in) - offset; guint offset_unchanged = offset; if (p->bytes_to_slide > 0) { if (p->bytes_to_slide < p->bytes_queued) { guint bytes_in_move = p->bytes_queued - p->bytes_to_slide; memmove (p->buf_queue, p->buf_queue + p->bytes_to_slide, bytes_in_move); p->bytes_to_slide = 0; p->bytes_queued = bytes_in_move; guint bytes_in_skip; p->bytes_to_slide -= p->bytes_queued; bytes_in_skip = MIN (p->bytes_to_slide, bytes_in); p->bytes_queued = 0; p->bytes_to_slide -= bytes_in_skip; offset += bytes_in_skip; bytes_in -= bytes_in_skip; if (bytes_in > 0) { guint bytes_in_copy = MIN (p->bytes_queue_max - p->bytes_queued, bytes_in); memcpy (p->buf_queue + p->bytes_queued, GST_BUFFER_DATA (buf_in) + offset, bytes_in_copy); p->bytes_queued += bytes_in_copy; offset += bytes_in_copy; return offset - offset_unchanged; reinit_buffers (GstScaletempo * scaletempo) gint i, j; guint frames_overlap; guint new_size; guint frames_stride = p->ms_stride * p->sample_rate / 1000.0; p->bytes_stride = frames_stride * p->bytes_per_frame; frames_overlap = frames_stride * p->percent_overlap; if (frames_overlap < 1) { /* if no overlap */ p->bytes_overlap = 0; p->bytes_standing = p->bytes_stride; p->samples_standing = p->bytes_standing / p->bytes_per_sample; p->output_overlap = NULL; guint prev_overlap = p->bytes_overlap; p->bytes_overlap = frames_overlap * p->bytes_per_frame; p->samples_overlap = frames_overlap * p->samples_per_frame; p->bytes_standing = p->bytes_stride - p->bytes_overlap; p->buf_overlap = g_realloc (p->buf_overlap, p->bytes_overlap); p->table_blend = g_realloc (p->table_blend, p->samples_overlap * 4); /* sizeof (gint32|gfloat) */ if (p->bytes_overlap > prev_overlap) { memset (p->buf_overlap + prev_overlap, 0, p->bytes_overlap - prev_overlap); if (p->use_int) { gint32 *pb = p->table_blend; gint64 blend = 0; for (i = 0; i < frames_overlap; i++) { gint32 v = blend / frames_overlap; for (j = 0; j < p->samples_per_frame; j++) { *pb++ = v; } blend += 65535; /* 2^16 */ p->output_overlap = output_overlap_s16; gfloat *pb = p->table_blend; gfloat t = (gfloat) frames_overlap; gfloat v = i / t; p->output_overlap = output_overlap_float; p->frames_search = (frames_overlap <= 1) ? 0 : p->ms_search * p->sample_rate / 1000.0; if (p->frames_search < 1) { /* if no search */ p->best_overlap_offset = NULL; guint bytes_pre_corr = (p->samples_overlap - p->samples_per_frame) * 4; /* sizeof (gint32|gfloat) */ p->buf_pre_corr = g_realloc (p->buf_pre_corr, bytes_pre_corr + UNROLL_PADDING); p->table_window = g_realloc (p->table_window, bytes_pre_corr); gint64 t = frames_overlap; gint32 n = 8589934588LL / (t * t); /* 4 * (2^31 - 1) / t^2 */ gint32 *pw; memset (p->buf_pre_corr + bytes_pre_corr, 0, UNROLL_PADDING); pw = p->table_window; for (i = 1; i < frames_overlap; i++) { gint32 v = (i * (t - i) * n) >> 15; *pw++ = v; p->best_overlap_offset = best_overlap_offset_s16; gfloat *pw = p->table_window; gfloat v = i * (frames_overlap - i); p->best_overlap_offset = best_overlap_offset_float; new_size = (p->frames_search + frames_stride + frames_overlap) * p->bytes_per_frame; if (p->bytes_queued > new_size) { if (p->bytes_to_slide > p->bytes_queued) { guint new_queued = MIN (p->bytes_queued - p->bytes_to_slide, new_size); memmove (p->buf_queue, p->buf_queue + p->bytes_queued - new_queued, new_queued); p->bytes_queued = new_queued; p->bytes_queue_max = new_size; p->buf_queue = g_realloc (p->buf_queue, p->bytes_queue_max); p->bytes_stride_scaled = p->bytes_stride * p->scale; p->frames_stride_scaled = p->bytes_stride_scaled / p->bytes_per_frame; GST_DEBUG ("%.3f scale, %.3f stride_in, %i stride_out, %i standing, %i overlap, %i search, %i queue, %s mode", p->scale, p->frames_stride_scaled, (gint) (p->bytes_stride / p->bytes_per_frame), (gint) (p->bytes_standing / p->bytes_per_frame), (gint) (p->bytes_overlap / p->bytes_per_frame), p->frames_search, (gint) (p->bytes_queue_max / p->bytes_per_frame), (p->use_int ? "s16" : "float")); p->reinit_buffers = FALSE; /* GstBaseTransform vmethod implementations */ static GstFlowReturn gst_scaletempo_transform (GstBaseTransform * trans, GstBuffer * inbuf, GstBuffer * outbuf) GstScaletempo *scaletempo = GST_SCALETEMPO (trans); gint8 *pout = (gint8 *) GST_BUFFER_DATA (outbuf); guint offset_in = fill_queue (scaletempo, inbuf, 0); guint bytes_out = 0; while (p->bytes_queued >= p->bytes_queue_max) { guint bytes_off = 0; gdouble frames_to_slide; guint frames_to_stride_whole; // output stride if (p->output_overlap) { if (p->best_overlap_offset) { bytes_off = p->best_overlap_offset (scaletempo); p->output_overlap (scaletempo, pout, bytes_off); memcpy (pout + p->bytes_overlap, p->buf_queue + bytes_off + p->bytes_overlap, p->bytes_standing); pout += p->bytes_stride; bytes_out += p->bytes_stride; // input stride memcpy (p->buf_overlap, p->buf_queue + bytes_off + p->bytes_stride, p->bytes_overlap); frames_to_slide = p->frames_stride_scaled + p->frames_stride_error; frames_to_stride_whole = (gint) frames_to_slide; p->bytes_to_slide = frames_to_stride_whole * p->bytes_per_frame; p->frames_stride_error = frames_to_slide - frames_to_stride_whole; offset_in += fill_queue (scaletempo, inbuf, offset_in); GST_BUFFER_SIZE (outbuf) = bytes_out; GST_BUFFER_TIMESTAMP (outbuf) = (GST_BUFFER_TIMESTAMP (outbuf) - p->segment_start) / p->scale + p->segment_start; //GST_BUFFER_DURATION (outbuf) = bytes_out * GST_SECOND / (p->bytes_per_frame * p->sample_rate); return GST_FLOW_OK; gst_scaletempo_transform_size (GstBaseTransform * trans, GstPadDirection direction, GstCaps * caps, guint size, GstCaps * othercaps, guint * othersize) if (direction == GST_PAD_SINK) { GstScaletempo *scaletempo = GST_SCALETEMPO (trans); GstScaletempoPrivate *priv = GST_SCALETEMPO_GET_PRIVATE (scaletempo); gint bytes_to_out; if (priv->reinit_buffers) reinit_buffers (scaletempo); bytes_to_out = size + priv->bytes_queued - priv->bytes_to_slide; if (bytes_to_out < (gint) priv->bytes_queue_max) { *othersize = 0; /* while (total_buffered - stride_length * n >= queue_max) n++ */ *othersize = priv->bytes_stride * ((guint) ( (bytes_to_out - priv->bytes_queue_max + /* rounding protection */ priv->bytes_per_frame) / priv->bytes_stride_scaled) + 1); gst_scaletempo_sink_event (GstBaseTransform * trans, GstEvent * event) gboolean update; GstFormat format; gint64 start, stop, position; gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, &format, &start, &stop, &position); if (priv->scale != rate) { if (ABS (rate - 1.0) < 1e-10) { priv->scale = 1.0; gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (scaletempo), TRUE); } else { FALSE); priv->scale = rate; priv->bytes_stride_scaled = priv->bytes_stride * priv->scale; priv->frames_stride_scaled = priv->bytes_stride_scaled / priv->bytes_per_frame; GST_DEBUG ("%.3f scale, %.3f stride_in, %i stride_out", priv->scale, priv->frames_stride_scaled, (gint) (priv->bytes_stride / priv->bytes_per_frame)); priv->bytes_to_slide = 0; if (priv->scale != 1.0) { priv->segment_start = start; applied_rate = priv->scale; rate = 1.0; //gst_event_unref (event); event = gst_event_new_new_segment_full (update, rate, applied_rate, format, start, stop, position); gst_pad_push_event (GST_BASE_TRANSFORM_SRC_PAD (trans), event); return FALSE; parent_class->event (trans, event); gst_scaletempo_set_caps (GstBaseTransform * trans, GstCaps * incaps, GstCaps * outcaps) GstScaletempoPrivate *priv = GST_SCALETEMPO_GET_PRIVATE (scaletempo); GstStructure *s = gst_caps_get_structure (incaps, 0); gint width, bps, nch, rate; const gchar *type = gst_structure_get_name (s); if (g_str_equal (type, "audio/x-raw-int")) { use_int = TRUE; gst_structure_get_int (s, "depth", &width); } else if (g_str_equal (type, "audio/x-raw-float")) { use_int = FALSE; gst_structure_get_int (s, "width", &width); bps = width / 8; gst_structure_get_int (s, "channels", &nch); gst_structure_get_int (s, "rate", &rate); GST_DEBUG ("caps: %s seek, " "%5" G_GUINT32_FORMAT " rate, " "%2" G_GUINT32_FORMAT " nch, " "%2" G_GUINT32_FORMAT " bps", type, rate, nch, bps); if (rate != priv->sample_rate || nch != priv->samples_per_frame || bps != priv->bytes_per_sample || use_int != priv->use_int) { priv->sample_rate = rate; priv->samples_per_frame = nch; priv->bytes_per_sample = bps; priv->bytes_per_frame = nch * bps; priv->use_int = use_int; priv->reinit_buffers = TRUE; /* GObject vmethod implementations */ gst_scaletempo_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) GstScaletempo *scaletempo = GST_SCALETEMPO (object); switch (prop_id) { g_value_set_double (value, priv->scale); g_value_set_uint (value, priv->ms_stride); g_value_set_double (value, priv->percent_overlap); g_value_set_uint (value, priv->ms_search); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); gst_scaletempo_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) case PROP_STRIDE:{ guint new_value = g_value_get_uint (value); if (priv->ms_stride != new_value) { priv->ms_stride = new_value; priv->reinit_buffers = TRUE; case PROP_OVERLAP:{ gdouble new_value = g_value_get_double (value); if (priv->percent_overlap != new_value) { priv->percent_overlap = new_value; case PROP_SEARCH:{ if (priv->ms_search != new_value) { priv->ms_search = new_value; gst_scaletempo_base_init (gpointer klass) static GstElementDetails element_details = { "Scaletempo", "Filter/Effect/Rate", "Sync audio tempo with playback rate", "Rov Juvano <rov...@us...>" GstElementClass *element_class = GST_ELEMENT_CLASS (klass); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&src_template)); gst_static_pad_template_get (&sink_template)); gst_element_class_set_details (element_class, &element_details); gst_scaletempo_class_init (GstScaletempoClass * klass) GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstBaseTransformClass *basetransform_class = GST_BASE_TRANSFORM_CLASS (klass); g_type_class_add_private (klass, sizeof (GstScaletempoPrivate)); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_scaletempo_get_property); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_scaletempo_set_property); g_object_class_install_property (gobject_class, PROP_RATE, g_param_spec_double ("rate", "Playback Rate", "Current playback rate", G_MININT, G_MAXINT, 1.0, G_PARAM_READABLE)); g_object_class_install_property (gobject_class, PROP_STRIDE, "Length in milliseconds to output each stride", 1, 5000, 30, g_object_class_install_property (gobject_class, PROP_OVERLAP, g_object_class_install_property (gobject_class, PROP_SEARCH, "Length in milliseconds to search for best overlap position", 0, 500, 14, G_PARAM_READWRITE)); basetransform_class->event = GST_DEBUG_FUNCPTR (gst_scaletempo_sink_event); basetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_scaletempo_set_caps); basetransform_class->transform_size = GST_DEBUG_FUNCPTR (gst_scaletempo_transform_size); basetransform_class->transform = GST_DEBUG_FUNCPTR (gst_scaletempo_transform); gst_scaletempo_init (GstScaletempo * scaletempo, GstScaletempoClass * klass) /* defaults */ priv->ms_stride = 30; priv->percent_overlap = .2; priv->ms_search = 14; /* uninitialized */ priv->scale = 0; priv->sample_rate = 0; priv->frames_stride_error = 0; priv->bytes_stride = 0; priv->bytes_queued = 0; priv->bytes_to_slide = 0; priv->segment_start = 0; --- NEW FILE: gstscaletempo.h --- #ifndef __GST_SCALETEMPO_H__ #define __GST_SCALETEMPO_H__ #define GST_TYPE_SCALETEMPO (gst_scaletempo_get_type()) #define GST_SCALETEMPO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_SCALETEMPO, GstScaletempo)) #define GST_SCALETEMPO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_SCALETEMPO, GstScaletempoClass)) #define GST_IS_SCALETEMPO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_SCALETEMPO)) #define GST_IS_SCALETEMPO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_SCALETEMPO)) typedef struct _GstScaletempo GstScaletempo; typedef struct _GstScaletempoClass GstScaletempoClass; struct _GstScaletempo GstBaseTransform element; struct _GstScaletempoClass GstBaseTransformClass parent_class; GType gst_scaletempo_get_type (void); #endif /* __GST_SCALETEMPO_H__ */ --- NEW FILE: gstscaletempoplugin.c --- * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * Alternatively, the contents of this file may be used under the * GNU Lesser General Public License Version 2.1 (the "LGPL"), in * which case the following provisions apply instead of the ones * mentioned above: # include <config.h> /* entry point to initialize the plug-in * initialize the plug-in itself * register the element factories and pad templates * register the features * ... [truncated message content] |