Concatenating audio files and superimposing over video - python

I'm trying to write a program that takes a bunch of videos from a folder, compiles them, and then takes another bunch of audio files from a folder and concatenates them to then overlay the combined audio on top of the final video (hope that makes sense). I'm able to process the videos into one final output video, but I keep running into trouble combining the audio. Below I've provided two small sections of code pertaining to audio comp, I'm using moviepy to process the videos and attempting to use it for audio as well.
songDirectory = './songs/'
songList = []
songs = []
audioclip=''
def makeCompilation():
for filename in os.listdir(songDirectory):
f = os.path.join(songDirectory, filename)
if os.path.isfile(f) and filename.endswith(".mp3"):
audioclip = moviepy.editor.AudioFileClip(f)
songList.append(filename)
for song in os.listdir(songDirectory):
c = os.path.join(songDirectory, song)
audio_clips = moviepy.editor.AudioFileClip(c)
audio_output = moviepy.editor.concatenate_audioclips(audio_clips)
finalClip = concatenate_videoclips(videos, method="compose")
final_duration = finalClip.duration
final_audio_output = audio_output.set_duration(final_duration)
final_output= finalClip.set_audio(final_audio_output).fx(afx.audio_fadein, 3.0)
audio_path = "./songs/tempaudiofile.m4a"
#print(description)
# Create compilation
final_output.write_videofile(outputFile, threads=8, temp_audiofile=audio_path, remove_temp=True, codec="libx264", audio_codec="aac")
return description
The program appeared to be able to find the audio directory, but I needed to be able to use os.path.join(songDirectory, filename) to point directly to each mp3 file so I messed around with it until I got the above code. And when I attempted to iterate through songList, I, as expected, got an error saying that 'str' wasn't iterable, and other errors saying that 'str' has no attribute 'duration'. Essentially, all I need it to do is iterate though the input folder and combine the files by whatever means. Code currently returns the output:
🌲Free Fredobagz x Aflacko x Flint type beat - 'Default_Dance' [prod. kah]-jro0v6ogZ0Y.mp4
225.05
Total Length: 225.05
225.05
Traceback (most recent call last):
File "/Users/daddyK/Desktop/comp_ bot/make_compilation3.py", line 127, in <module>
makeCompilation(path = "./videos/",
File "/Users/daddyK/Desktop/comp_ bot/make_compilation3.py", line 110, in makeCompilation
audio_output = moviepy.editor.concatenate_audioclips(audio_clips)
File "/Users/daddyK/Library/Python/3.10/lib/python/site-packages/moviepy/audio/AudioClip.py", line 315, in concatenate_audioclips
durations = [c.duration for c in clips]
TypeError: 'AudioFileClip' object is not iterable
At this point I'm a bit stumped, so if anybody could offer some insight as to what I should do to resolve the error and/or if I'm headed in the right direction code-wise I'd greatly appreciate it! Sorry if the code doesn't make any sense I'll post the whole .py file if needed

As per the comment by #kesh, you need to replace
songList.append(filename)
with
songList.append(audioclip)

Related

pydub AudioSegment.export is locking a file on smb share. Can't delete that file

I am running into an issue when trying to extract mono audio from a stereo file using pydub.
Here is the code:
import wave
import audioop
from pydub import AudioSegment
def cantDeleteLockedFile():
audiofile = "/Volumes/test/stereotest.wav"
audiostrip = AudioFileClip(audiofile)
if audiostrip.nchannels > 1:
with open(audiofile, "rb") as oaudiofile:
mono_audios = AudioSegment.from_file(oaudiofile, format="wav")
# List of AudioSegments, mono files, which can be accessed via [0] and [1]
mono_audios = mono_audios.split_to_mono()
audioChannelOne = str(audiofile.rsplit(".", 1)[0]) + "a.wav"
# This line is locking the stereo file
mono_left = mono_audios[0].export(audioChannelOne, format="wav")
# This extracts the mono left track from the stereo track
# On the same location a file will be created, in this example:
# "/Volumes/test/stereotesta.wav"
# This should unlock the file, but doesnt
mono_left.close()
# When trying to delete the file here, it will fail
# without exception raised
os.remove(audiofile)
if os.path.exists(audiofile):
return True
else:
return False
return False
After executing this code, which in my case is embedded into an API microservice system, that does not exit the code. Then the stereo audio file will be locked, for as long as that micro service is running. The file will not be deleted and function return value will be "False". If you later manually on the filesystem navigate to that file and try to delete it manually, it will also fail. It will first delete it, but then it will magically pop back up.
I am aware of this issue being discussed on other boards before. However the proposed solution does not work.
ref: https://github.com/jiaaro/pydub/issues/305
Either I am missing something completely. However, perhaps there is a workaround to forcibly unlock a file, so it can be deleted? I did not find a reference online.
Basically I know, that pydub is locking the resource, I can't get it to unlock the wav file behind the Audio Segment.
Happy to read your feedback and suggestions.
Thank you!
The Audiosegment used to check for stereo files also needs to be closed.
This was blocking the file on the storage side.
Adding a simple:
audiostrip.close()
solved the problem.

RedVox Python SDK | Not Reading in .rdvxz Files

I'm attempting to read in a series of files for processing contained in a single directory using RedVox:
input_directory = "/home/ben/Documents/Data/F1D1/21" # file location
rdvx_data = DataWindow(input_dir=input_directory, apply_correction=False, debug=True) # using RedVox to read in the files
print(os.listdir(input_directory)) # verifying the files actually exist...
# returns "['file1.rdvxz', 'file2.rdvxz', file3.rdvxz', ...etc]", they exist
# write audio portion to file
rdvx_data.to_json_file(base_dir=output_rpd_directory,
file_name=output_filename)
# this never runs, because rdvx_data.stations = [] (verified through debugging)
for station in rdvx_data.stations:
# some code here
Enabling debugging through arguments as seen above does not provide an extra details. In fact, there is no error message whatsoever. It writes the JSON file and pickle to disk, but the JSON file is full of null values and the pickle object is just a shell, no contents. So the files definitely exist, os.listdir() sees them, but RedVox does not.
I assume this is some very silly error or lack of understanding on my part. Any help is greatly appreciated. I have not worked with RedVox previously, nor do I have much understanding of what these files contain other than some audio data and some other data. I've simply been tasked with opening them to work on a model to analyze the data within.
SOLVED: Not sure why the previous code doesn't work (it was handed to me), however, I worked around the DataWindow call and went straight to calling the "redvox.api900.reader" object:
from redvox.api900 import reader
dataset_dir = "/home/*****/Documents/Data/F1D1/21/"
rdvx_files = glob(dataset_dir+"*.rdvxz")
for file in rdvx_files:
wrapped_packet = reader.read_rdvxz_file(file)
From here I can view all of the sensor data within:
if wrapped_packet.has_microphone_sensor():
microphone_sensor = wrapped_packet.microphone_sensor()
print("sample_rate_hz", microphone_sensor.sample_rate_hz())
Hope this helps anyone else who's confused.

Why is my output different when the code is same?

I am currently putting together a band-pass filter using the following code: https://ipython-books.github.io/116-applying-digital-filters-to-speech-sounds/
I made few edits to the above code, namely the file is no longer pulled from an url but instead from a local WAV file. Here are the associated edits
def speak(voice):
audio = pydub.AudioSegment.from_wav(BytesIO(voice))
with tempfile.temporaryfile() as fn:
wavef = audio.export(fn, format='wav')
wavef.seek(0)
wave = wavef.read()
...
voice = open("C:\\Users\\tkim1\\Documents\\librosa\\NEUT 41s
shaking_gold.wav", "rb").read
Currently the "audio = pydub.AudioSegment.from_wav" line of the code outputs the following error: a bytes-like object is required, not 'builtin_function_or_method." I have gone over the two scripts line by line and cannot determine why this error is surfacing. Thank you so much for reading everyone. Any insights would be greatly appreciated!
You didn't call read, which is a method.
voice = open("...", "rb").read()
# ^

TypeError when using MoviePy

In trying to learn a little about MoviePy, I copied some sample code (which I modified slightly) that cuts a 10 second section out of a movie file, overlays text on it, and writes it as a different file. The code works perfectly...only for certain files. I have two video files that I wanted to use the code on (just for practice). Both are .mov files, both are on the same drive and both of the paths are correct (I have verified them multiple times). The problem is I'm getting a TypeError on one of the files while it works perfectly on the other. Here's the code:
from moviepy.editor import *
x = int(input("When do you want the cut to start? "))
y = int(input("When do you want the cut to end? "))
video = VideoFileClip("D:\Videos\Gatlinburgh Drone River 2.MOV").subclip(x,y)
##video = VideoFileClip("D:\SF_ep\T_R_D.mov").subclip(x,y) #Path is correct
txt_clip = ( TextClip("The Red Dot episode",fontsize=70,color='white')
.set_position('center')
.set_duration(10) )
result = CompositeVideoClip([video, txt_clip])
result.write_videofile("Text on Screen.webm",fps=25)
The above example works perfectly. However, when I comment it out and uncomment the video right below it, I get the following error:
Traceback (most recent call last):
File "C:\Users\Sam\Python Projects\MoviePy\Example3c.py", line 15, in <module>
video = VideoFileClip("D:\\Seinfeld_All_Episodes\\The_Red_Dot.mov").subclip(x,y)
File "C:\Python34\lib\site-packages\moviepy\video\io\VideoFileClip.py", line 82, in __init__
nbytes = audio_nbytes)
File "C:\Python34\lib\site-packages\moviepy\audio\io\AudioFileClip.py", line 63, in __init__
buffersize=buffersize)
File "C:\Python34\lib\site-packages\moviepy\audio\io\readers.py", line 70, in __init__
self.buffer_around(1)
File "C:\Python34\lib\site-packages\moviepy\audio\io\readers.py", line 234, in buffer_around
self.buffer = self.read_chunk(self.buffersize)
File "C:\Python34\lib\site-packages\moviepy\audio\io\readers.py", line 123, in read_chunk
self.nchannels))
TypeError: 'float' object cannot be interpreted as an integer
I'm not changing any code, I'm just pointing to a different file. I've tried the same with different files and gotten the same error. Why would it work on one and not the other? Any thoughts?
A similar question has been asked Stack Overflow before but there weren't any solid answers (at least none that applied to my particular situation).
Any help would be great. Thanks!
After searching around a bit more, I found a solution here. Line 122 of code in Readers.py was returning a float instead of an integer because it was using a single "/" instead of the double "//". I changed that line and it seems to have solved the problem. Details are at the link.
For the record, I still don't understand why it happened on certain files and not others. Nevertheless, the fix was simple.

How to get what is stored in 'data' field of las point using liblas?

I am working with mulipulse lidar data that collects points along a number of lines within the flight path. I am trying to determine the name and number of individual lines within the las file. I am using liblas module in python.
I found this documentation that explains the different fields stored in an las file. It mentions a data field (get_data and set_data) at the very bottom of the page.
The 'point data format' and 'point data record length' in the header set aside space for this 'data' field. My header says I have 28 bytes set aside for the data field, and there are 28 values stored in the data field. The 19th value (at least in two datasets from two different sensors) refers to the line number. I have a single value in single pulse data and 4 in multi-pulse data.
I was wondering if there is a standard for what is stored in this field or if it is proprietary.
Also, as a way to get the name of each scan line, I wrote the following code:
import liblas
from liblas import file as lasfile
# Get parameters
las_file = r"E:\Testing\00101.las"
f = lasfile.File(las_file, mode='r')
line_list = []
counter = 0
for p in f:
line_num = p.data[18]
if line_num not in line_list:
line_list.append(line_num)
counter += 1
print line_list
It results with the following error:
Traceback (most recent call last):
File "D:\Tools\Python_Scripts\point_info.py", line 46, in <module>
line_num = p.data[18]
File "C:\Python27\ArcGIS10.1\lib\site-packages\liblas\point.py", line 560, in get_data
length = self.header.data_record_length
File "C:\Python27\ArcGIS10.1\lib\site-packages\liblas\point.py", line 546, in get_header
return header.Header(handle=core.las.LASPoint_GetHeader(self.handle))
WindowsError: [Error -529697949] Windows Error 0xE06D7363
Does anyone know more about the line numbers stored in the las point/header? Can anyone explain the error? It seems to allocate nearly 2gb of ram before I get the error. I am on win xp, so I'm guessing its a memory error, but I don't understand why accessing this 'data' field hogs memory. Any help is greatly appreciated.
I don't pretend to be an expert in any of this, but I'm intrigued by GIS data so this caught my interest. I installed liblas and its dependencies on my Fedora 19 system and played with the example data files that came with liblas.
Using your code I ran into the same problem of watching all my memory get eaten up. I don't know why that should happen - perhaps unwanted references hanging around preventing the garbage collector from working as we'd hope. This could probably be fixed, but I won't try it.
I did notice some interesting features of the liblas module and decided to try them. I believe you can get the data you seek.
After opening your file, have a look at the XML description from the header.
h = f.get_header()
print(h.get_xml())
It's hard to look at (feel free to play with xml.dom.minidom or lxml.etree), but in my example files it showed the byte layout of the point data (mine had 28 bytes too). In mine, offset 18 was a single short (2 bytes) assigned to Point Source ID. You should be able to retrieve this with p.data[18:19], p.get_data()[18:19], p.point_source_id, or p.get_point_source_id(). Unfortunately the data references chew up memory and p.point_source_id has a bug (bug fix pull request submitted to developers). If we change your code to use the last access method, everything seems to work fine. So, try this in your for loop instead:
for p in f:
line_num = p.get_point_source_id()
if line_num not in line_list:
line_list.append(line_num)
counter += 1
Note that
counter == h.get_count()
If you just want the set of unique Point Source ID values ...
line_set = set(p.get_point_source_id() for p in f)
Hopefully your data value is also available as p.get_point_source_id(). Let me know how it works for you in the comments. Cheers!

Categories

Resources