Log in

View Full Version : How to trim audio


MysteryX
13th August 2022, 19:19
How can I trim the audio in VapourSynth?

clip = core.lsmas.LibavSMASHSource(source)
audio = vs.core.bas.Source(source, track=-1)
clip = clip.std.Trim(first = startpos * clip.fps, length = length * clip.fps)
#audio = audio.std.TrimAudio(first = startpos * clip.sample_rate, length = length * clip.sample_rate)

"AudioTrim performs exactly the same operation on audio clips but the unit is obviously samples instead of frames."

It's really not clear what that means or how I should write it. I think the audio documentation really could be improved.

_Al_
13th August 2022, 20:00
most simple and transparent seems to be something:

import math
def to_samples(frame, fps=clip.fps, samplerate=48000):
return math.floor((samplerate/fps)*frame)

clip = clip[startpos:endpos]
audio = audio[to_samples(startpos):to_samples(endpos)]


where startpos,endpos are frames, but you have them as time

MysteryX
13th August 2022, 20:15
If pos is in seconds, basically you're doing:


clip = core.lsmas.LibavSMASHSource(source)
audio = vs.core.bas.Source(source, track=-1)

def to_samples(second):
return math.floor(audio.sample_rate * second)

clip = clip.std.Trim(first = startpos * clip.fps, length = length * clip.fps)
audio = audio.std.AudioTrim(first = to_samples(startpos), length = to_samples(length))


The returned audio is WAY too short.

For a 5-second encode, audio encode says: Output 240000 samples in 1.81 seconds

no wait -- audio is fine 5 seconds, problem seems to be with the audio/video muxing

_Al_
13th August 2022, 20:20
Slicing operates different than trim maybe, if using slicing, that last integer in trim is not included, you perhaps cannot mix slicing and trim
try:
start_time = 5 #5seconds
length = 10 #seconds

start = int(clip.fps * start_time)
end = int(start + length*clip.fps)

clip = clip[start:end]
audio = audio[to_samples(start):to_samples(end)]
Your funct. is different, rounding errors? How trim function rounds up numbers, meaning float frame to int frame, I'd make sure both clip and audio gets same integer frames for either slicing or trim.

MysteryX
13th August 2022, 21:11
The issue was with the output with ffmpeg

mov file with prores doesn't support PCM audio. I changed it to mkv and it works. Hopefully DaVinci Premiere opens the MKV file fine.


vspipe -o 1 -c wav "denoise.vpy" - | ffmpeg -i pipe: -c:a pcm_s16le Output.wav
vspipe -o 0 -c y4m "denoise.vpy" - | ffmpeg -i pipe: -i Output.wav -c:v prores -c:a copy "Output.mkv"
rm Output.wav

_Al_
13th August 2022, 22:08
videoeditors have function to group a video and audio to one if needed, if it comes to it, maybe you do not need to have video and audio in one container,

just remembered, DJATOM posted a function that does trimming for both , video and audio
https://forum.doom9.net/showthread.php?p=1933080#post1933080

looking at that DJATOM class, it could be avoided to call data() method at the end if def __call__ () is used instead of def data(), and using instead :
vclip, aclip = Trim((vclip, aclip), 18544, 18582)() + Trim((vclip, aclip), 18687, 18724)()
not tested yet, someone might try, but adding might not work then, not sure ...

MysteryX
14th August 2022, 06:04
Actually DaVinci doesn't like prores in a MKV container... need to muxe them back into separate files

_Al_
15th August 2022, 05:08
Or how about this, is this too much fuss for just trimming or multiplying pair of video and audio?

#python code
import vapoursynth as vs
from vapoursynth import core
from dataclasses import dataclass, field
@dataclass
class Pair:
vnode: vs.VideoNode = field(default_factory=lambda: core.std.BlankClip())
anode: vs.AudioNode = field(default_factory=lambda: core.std.BlankAudio())

def trim(self, start, end):
return Pair( self.vnode[start:end],
self.anode[self.to_samples(start):self.to_samples(end)]
)

def to_samples(self, frame):
return int((self.anode.sample_rate/self.vnode.fps)*frame)

def __add__(self, other):
return Pair(self.vnode + other.vnode, self.anode + other.anode)

def __mul__(self, multiple):
return Pair(self.vnode*multiple, self.anode*multiple)

vnode = core.lsmas.LibavSMASHSource(r"video_source.mp4")
anode = core.bas.Source(r"video_source.mp4")
#some custom vnode filtering
vnode = vnode.std.Levels(min_in=16, max_in=235, min_out=0, max_out=255, planes=0)
#to pair video and audio for trimming or multiplying
pair = Pair(vnode, anode)
pair = pair.trim(20, 1000) + pair.trim(1001, 2000) + pair.trim(2500, vnode.num_frames) #adding
pair = pair * 2 # or pair + pair, doubling the length
print(pair.vnode, pair.anode)
pair.vnode.set_output()
pair.anode.set_output(1)
#or just continue script with vnode fix ups only using vnode = pair.vnode, or just using pair.vnode all the time

_Al_
23rd February 2023, 21:54
How about this, to drag audio along video. Trim arguments have the same syntax as vapoursynth' Trim. But also slicing could be used:


class Clip:
def __init__(self, video = None, audio=None, attribute_audio_path=None):
self.video = video
self.audio = audio
if self.video is None:
self.video = core.std.BlankClip()
if self.audio is None:
if attribute_audio_path is None:
raise ValueError('argument attribute_audio_path is needed to get default audio for images (could be a really short video')
attr_audio = core.bas.Source(attribute_audio_path)
length = int(attr_audio.sample_rate/self.video.fps*self.video.num_frames)
self.audio = attr_audio.std.BlankAudio(length=length)

def trim(self, first=0, last=None, length=None):
afirst = self.to_samples(first) if first is not None else None
alast = self.to_samples(last+1)-1 if last is not None else None
alength = self.to_samples(length) if length is not None else None
return Clip( self.video.std.Trim(first=first, last=last, length=length),
self.audio.std.AudioTrim(first=afirst,last=alast,length=alength)
)
def to_samples(self, frame):
return int((self.audio.sample_rate/self.video.fps)*frame)

def __add__(self, other):
return Clip(self.video + other.video, self.audio + other.audio)

def __mul__(self, multiple):
return Clip(self.video*multiple, self.audio*multiple)

def __getitem__(self, val):
if isinstance(val, slice):
if val.step is not None:
raise ValueError('Using steps while slicing AudioNode together with VideoNode makes no sense')
start = self.to_samples(val.start) if val.start is not None else None
stop = self.to_samples(val.stop) if val.stop is not None else None
return Clip( self.video.__getitem__(val),
self.audio.__getitem__(slice(start,stop))
)
elif isinstance(val, int):
start = self.to_samples(val)
stop = int(start + self.audio.sample_rate/self.video.fps)
return Clip( self.video[val],
self.audio.__getitem__(slice(start,stop))
)
def __repr__(self):
return '{}\n{}\n{}'.format('Clip():\n-------', repr(self.video), repr(self.audio))

def __str__(self):
return '{}\n{}\n{}'.format('Clip():\n-------', str(self.video), str(self.audio))

Creates class that carries both vs.VideoNode and vs.AudioNode.
Using the same syntax as for vs.VideoNode on this class object for: trim/slice, add, multiply or print,
the same operation is also done to the audio at the same time
examples:
from vapoursynth import core
video = core.lsmas.LibavSMASHSource('video.mp4')
audio = core.bas.Source('video.mp4')
clip = Clip(video, audio)
clip = clip[0] + clip[20:1001] + clip[1500:2000] + clip[2500:]
#or same trimming calling trim method:
clip = clip.trim(first=0, length=1) + clip.trim(firt=20, last=1000) + clip.trim(1500, length=500) + clip.trim(2500)
clip = clip*2
clip = clip + clip
clip.video.set_output(0)
clip.audio.set_output(1)

Does anyone know how to change samplerate from integer to float for BlankAudio?

kolak
24th February 2023, 12:07
The issue was with the output with ffmpeg

mov file with prores doesn't support PCM audio. I changed it to mkv and it works. Hopefully DaVinci Premiere opens the MKV file fine.


vspipe -o 1 -c wav "denoise.vpy" - | ffmpeg -i pipe: -c:a pcm_s16le Output.wav
vspipe -o 0 -c y4m "denoise.vpy" - | ffmpeg -i pipe: -i Output.wav -c:v prores -c:a copy "Output.mkv"
rm Output.wav


Since when MOV doesn't support PCM?
It supports it perfectly and video codec has nothing to do with it either.
There must be some other issue. MOV can store audio outside video and this may be some hint. There is some issue with piping probably.