Reputation: 3060
My goal is to edit together parts of different audio files and apply to each of these parts a single time-stretch effect (tempo property of the Pitch element). Each segment should last 12 seconds in the final mix, so that the output file is 36 seconds long. The following code does this for 3 files (it could be extended by using a loop and storing everything into lists). [yes, it's kind of long]
import gst, gobject
gobject.threads_init()
import numpy as np
import math
comp = gst.element_factory_make("gnlcomposition", "composition")
gsrc1 = gst.element_factory_make("gnlfilesource")
gsrc1.props.location = "file1.mp3"
gsrc1.props.start = 0
gsrc1.props.duration = ?
gsrc1.props.media_start = 0
gsrc1.props.priority = 3
comp.add(gsrc1)
gsrc2 = gst.element_factory_make("gnlfilesource")
gsrc2.props.location = "file2.mp3"
gsrc2.props.start = ?
gsrc2.props.duration = ?
gsrc2.props.media_start = 0
gsrc2.props.priority = 4
comp.add(gsrc2)
gsrc3 = gst.element_factory_make("gnlfilesource")
gsrc3.props.location = "file3.mp3"
gsrc3.props.start = ?
gsrc3.props.duration = ?
gsrc3.props.media_start = 0
gsrc3.props.priority = 5
comp.add(gsrc3)
bin = gst.Bin()
audioconvertbin = gst.element_factory_make("audioconvert")
pitch1 = gst.element_factory_make("pitch")
pitch1.set_property("tempo", 1.05)
bin.add(audioconvertbin, pitch1)
audioconvertbin.link(pitch1)
bin.add_pad(gst.GhostPad("sink", audioconvertbin.get_pad("sink")))
bin.add_pad(gst.GhostPad("src", pitch1.get_pad("src")))
bin2 = gst.Bin()
audioconvertbin2 = gst.element_factory_make("audioconvert")
pitch2 = gst.element_factory_make("pitch")
pitch2.set_property("tempo", 0.95)
bin2.add(audioconvertbin2, pitch2)
audioconvertbin2.link(pitch2)
bin2.add_pad(gst.GhostPad("sink", audioconvertbin2.get_pad("sink")))
bin2.add_pad(gst.GhostPad("src", pitch2.get_pad("src")))
bin3 = gst.Bin()
audioconvertbin3 = gst.element_factory_make("audioconvert")
pitch3 = gst.element_factory_make("pitch")
pitch3.set_property("tempo", 1.1)
bin3.add(audioconvertbin3, pitch3)
audioconvertbin3.link(pitch3)
bin3.add_pad(gst.GhostPad("sink", audioconvertbin3.get_pad("sink")))
bin3.add_pad(gst.GhostPad("src", pitch3.get_pad("src")))
op = gst.element_factory_make("gnloperation")
comp.add(op)
op2 = gst.element_factory_make("gnloperation", "op2")
comp.add(op2)
op3 = gst.element_factory_make("gnloperation", "op3")
comp.add(op3)
op.add(bin)
op.props.start = 0 * gst.SECOND
op.props.duration = ?
op.props.priority = 1
op2.add(bin2)
op2.props.start = ?
op2.props.duration = ?
op2.props.priority = 1
op3.add(bin3)
op3.props.start = ?
op3.props.duration = ?
op3.props.priority = 1
pipeline = gst.Pipeline()
audioconvert = gst.element_factory_make("audioconvert")
encoder = gst.element_factory_make("vorbisenc")
mux = gst.element_factory_make("oggmux")
filesink = gst.element_factory_make("filesink")
filesink.set_property("location", "output.ogg")
pipeline.add(comp, audioconvert, encoder, mux, filesink)
gst.element_link_many(audioconvert, encoder, mux, filesink)
def on_pad(comp, pad, elements):
convpad = elements.get_compatible_pad(pad, pad.get_caps())
pad.link(convpad)
comp.connect("pad-added", on_pad, audioconvert)
loop = gobject.MainLoop(is_running=True)
bus = pipeline.get_bus()
bus.add_signal_watch()
def on_message(bus, message, loop):
if message.type == gst.MESSAGE_EOS:
loop.quit()
elif message.type == gst.MESSAGE_ERROR:
print message
loop.quit()
bus.connect("message", on_message, loop)
pipeline.set_state(gst.STATE_PLAYING)
loop.run()
pipeline.set_state(gst.STATE_NULL)
Upvotes: 0
Views: 323
Reputation: 3060
I have found that the "best" values for the "?" are:
gsrc1 = gst.element_factory_make("gnlfilesource")
gsrc1.props.location = "file1"
gsrc1.props.start = 0
gsrc1.props.duration = 12 * 1.05 * gst.SECOND
gsrc1.props.media_start = 0
gsrc1.props.priority = 3
comp.add(gsrc1)
gsrc2 = gst.element_factory_make("gnlfilesource")
gsrc2.props.location = "file2.mp3"
gsrc2.props.start = int(12 * 1.05 * gst.SECOND)
gsrc2.props.duration = int(12 * gst.SECOND)
gsrc2.props.media_start = 36 * gst.SECOND
gsrc2.props.priority = 4
comp.add(gsrc2)
gsrc3 = gst.element_factory_make("gnlfilesource")
gsrc3.props.location = "file3.mp3"
gsrc3.props.start = int(12 * 1.05 * gst.SECOND + 12 * gst.SECOND)
gsrc3.props.duration = int(12 * 1.1 * gst.SECOND)
gsrc3.props.media_start = 60 * gst.SECOND
gsrc3.props.priority = 4
comp.add(gsrc3)
op.add(bin)
op.props.start = 0 * gst.SECOND
op.props.duration = int(12 * 1.05 * gst.SECOND)
op.props.priority = 1
op2.add(bin2)
op2.props.start = int(12 * 1.05 * gst.SECOND)
op2.props.duration = int(12 * gst.SECOND)
op2.props.priority = 1
op3.add(bin3)
op3.props.start = int(12 * 1.05 * gst.SECOND + 12 * gst.SECOND)
op3.props.duration = int(12 * 1.1 * gst.SECOND)
op3.props.priority = 1
In a nutshell you should not consider tempo changes < 1 has having any consequence, but you should take into account tempo changes > 1 (this is kind of a strange behavior, from a user point of view).
I compared this against Audacity, it gives roughly the same results, except there is a thin gap between two excerpts of rougly 0.026s. I don't think this is due to rounding error, maybe my duration / start times are not set exactly right (see the gap on the image below).
Hope it will help some people.
Upvotes: 0