Process 24bit wavs using pydub - python

Im trying to write a basic program that picks 2 audio files at random, layers them, and writes it out as a new file. I'm using pydub and it all works, however the results are distorted. I suspect it's because from what I've learnt, pydub cannot handle 24 bit wavs, which happen to be the norm in sample packs.
So needing some small blip of code that converts the wav to 16 bit before it enters pydub. Hopefully not one that requires writing it to disc first.
from pydub import AudioSegment
import os
import random
import shutil
def process(user_folder):
new_library_folder = user_folder + " Generated Combo Samples"
files_list = []
for root, directory, files in os.walk(user_folder):
for file in files:
if file_is_valid_ext(file):
filepath = str(root) + "/" + str(file)
# print filepath
files_list.append(filepath)
# removes previously created folder
shutil.rmtree(new_library_folder)
os.makedirs(new_library_folder)
i = 0
for number in range(gen_count): # global at 100
i = i + 1
file1 = random.choice(files_list)
file2 = random.choice(files_list)
sound1 = AudioSegment.from_file(file1)
sound2 = AudioSegment.from_file(file2)
sound1 = match_target_amplitude(sound1, -20)
sound2 = match_target_amplitude(sound2, -20)
combinedsound = sound1.overlay(sound2)
combinedsoundnormalised = match_target_amplitude(combinedsound, -6)
combinedsound_path = new_library_folder + "/" + "Sample " + str(i) + ".wav"
combinedsoundnormalised.export(combinedsound_path, format='wav')

It has been some months since you posted this question but I will answer it for others who may need a solution to this. As far as I have found, PySoundFile is the only python package that can deal with 24 bit audio (I am sure there are others but I haven't found them). My suggestion would be to initially read in the audio using PySoundFile and then create a pydub.AudioSegment using that data.

Related

Converting RGB PDFs to CMYK PDFs via GhostScript CLI / Python (quality problem)

I am trying to achieve the following using a python script:
Read in an SVG design file (with images)
Manipulate the SVG file
Convert this to a web-ready PDF and a print-ready PDF
My problem is with the conversion of the RGB PDF to the CMYK PDF. An SVG with a 15MB photo in it will export as a 15MB RGB PDF, but then convert (using GhostScript) to a 3MB CMYK PDF. When trying ImageMagic, the resolution of the output PDF is determined by the density and I can't find how to keep the PDF's canvas size while setting the density.
So far, I have a script which reads in the SVG files and does some manipulation (add a logo using svgutils, change some text by scanning through the SVG text file). It then uses Inkscape to export the web-ready PDF (using "--export-area-page" and converting the text to paths) and a temporary PDF (using "--export-margin=X" where X is the bleed size, also converting text to paths). The temporary PDF is what I need, except it is RGB rather than CMYK. So, I then want to convert this file (Inkscape does not work with CMYK).
This is the function I am using to convert the file (it is setup with GhostScript and also I was trialling ImageMagick):
converter_program = "GHOSTSCRIPT"
def convertPDFtoPrintReadyPDF(pdf_in, new_filename=None, output_location=None):
global converter_program
if (new_filename == None):
new_filename = os.path.basename(pdf_in).replace(".svg", ".pdf")
if (output_location == None):
output_location = os.path.dirname(pdf_in)
output_file = output_location + "\\" + new_filename
argument_list = []
if (converter_program == "GHOSTSCRIPT"):
pdf_tool_loc = r'"C:\Program Files\gs\gs9.55.0\bin\gswin64c.exe"' # Added "c" at end for non-window version (command line)
argument_list.append('-o "' + output_file + '"')
argument_list.append(r"-sDEVICE=pdfwrite")
argument_list.append(r"-dUseBleedBox")
argument_list.append(r"-dQUIET")
argument_list.append(r"-dPDFSETTINGS=/printer")
argument_list.append(r"-dCompressPages=false")
argument_list.append(r"-dMaxInlineImageSize=200000")
argument_list.append(r"-dDetectDuplicateImages")
#argument_list.append(r"-dJPEGQ=100")
argument_list.append(r"-dAutoFilterColorImages=false")
argument_list.append(r"-dAutoFilterGrayImages=false")
#argument_list.append(r"-sCompression=Flate")
#breaks the code: argument_list.append(r"-sColorImageFilter=/Flate")
#argument_list.append(r"-r600")
argument_list.append(r"-dColorImageResolution=600")
argument_list.append(r"-dGrayImageResolution=300")
argument_list.append(r"-dMonoImageResolution=1200")
argument_list.append(r"-dDownsampleColorImages=false")
argument_list.append(r"-sProcessColorModel=DeviceCMYK")
argument_list.append(r"-sColorConversionStrategy=CMYK")
argument_list.append(r"-sColorConversionStrategyForImages=CMYK")
argument_list.append('"' + pdf_in + '"')
elif (converter_program == "IMAGEMAGICK"):
pdf_tool_loc = 'magick'
argument_list.append(r'convert "' + pdf_in + '"')
argument_list.append(r"-density 300")
argument_list.append(r"-resize 100%")
argument_list.append(r"-colorspace CMYK")
argument_list.append('"' + output_file + '"')
#convert tp_rgb.pdf -verbose -density 300 -colorspace CMYK tp_cmyk.pdf
argument_string = " ".join(argument_list)
subprocess.run(pdf_tool_loc + " " + argument_string, shell=True, check=True)
return output_file
Versions:
Python 3.8.10
GhostScript 9.55.0
ImageMagick 7.1.0-16
I found some GhostScript parameters to add to the conversion process:
argument_list.append(r"-dAutoFilterColorImages=false")
argument_list.append(r"-dAutoFilterGrayImages=false")
argument_list.append(r"-dColorImageFilter=/FlateEncode")
argument_list.append(r"-dGrayImageFilter=/FlateEncode")
argument_list.append(r"-dDownsampleMonoImages=false")
argument_list.append(r"-dDownsampleGrayImages=false")
So, the full argument list looks like this:
argument_list.append('-o "' + output_file + '"')
argument_list.append(r"-sDEVICE=pdfwrite")
argument_list.append(r"-dUseBleedBox")
argument_list.append(r"-dQUIET")
argument_list.append(r"-dDetectDuplicateImages")
argument_list.append(r"-dAutoFilterColorImages=false")
argument_list.append(r"-dAutoFilterGrayImages=false")
argument_list.append(r"-dColorImageFilter=/FlateEncode")
argument_list.append(r"-dGrayImageFilter=/FlateEncode")
argument_list.append(r"-dDownsampleMonoImages=false")
argument_list.append(r"-dDownsampleGrayImages=false")
argument_list.append(r"-dColorImageResolution=300")
argument_list.append(r"-dGrayImageResolution=300")
argument_list.append(r"-sProcessColorModel=DeviceCMYK")
argument_list.append(r"-sColorConversionStrategy=CMYK")
argument_list.append(r"-sColorConversionStrategyForImages=CMYK")
argument_list.append('"' + pdf_in + '"')
This turned the 15MB->3MB conversion into a 15MB->53MB.
It still needs some tweaking, but is now on the right track (I will update this answer if I get the process better).
I found the information thanks to this post: http://zeroset.mnim.org/2014/07/14/save-a-pdf-to-cmyk-with-inkscape/
Documentation is here (don't forget to delete the leading letter to search ("dColorImageFilter" to "ColorImageFilter"): https://www.ghostscript.com/doc/current/VectorDevices.htm

Using Python in EventGhost

i have a script to play a random wav file from a folder works good but Il like it to only play between 9am and 5pm not sure how to do that
Thanks
from os.path import isfile, join
import random
#path you want to get wav files from
path = "C:\Windows\Media"
onlyfiles = [ f for f in listdir(path) if isfile(join(path,f)) ]
onlywavfiles = []
for f in onlyfiles:
if f[-3:] == "wav":
onlywavfiles.append(f)
#generate random number based on number of available files
randomnum = random.randint(0,len(onlywavfiles)-1)
eg.plugins.System.PlaySound(path + "/" + onlywavfiles[randomnum], 1, False)
Because you did not say what triggers that script, I' ll have to guess.
I guess: The script is triggered by an event from your home automation. Perhaps someone rings the bell, or Balto requests his meal, or a fish jumps out of the aquarium.
That triggers an event and starts the script. The script plays a random wav file from the Media folder.
But you don't want the music to be played outside the interval you specified.
It doesn't matter, if the script is still executed/triggered, only the sound must not be played.
You could simply read the time, extract the hour and test 9 < hour or hour > 16
and return from the script before the sound is played if the test is true.
(I'm from Germany so we have 24 hour time. I dont know how python returns hours for 12 hr systems.)
from os.path import isfile, join
import random
import datetime
hr = datetime.datetime.now().hour
if hr < 9 or hr > 16:
exit()
#path you want to get wav files from
path = "C:\Windows\Media"
onlyfiles = [ f for f in listdir(path) if isfile(join(path,f)) ]
onlywavfiles = []
for f in onlyfiles:
if f[-3:] == "wav":
onlywavfiles.append(f)
#generate random number based on number of available files
randomnum = random.randint(0,len(onlywavfiles)-1)
eg.plugins.System.PlaySound(path + "/" + onlywavfiles[randomnum], 1, False)

ValueError: scandir: path too long for Windows

I am writing a simple Python script to tell me file size for a set of documents which I am importing from a CSV. I verified that none of the entries are over 100 characters, so this error "ValueError: scandir: path too long for Windows" does not make sense to me.
Here is my code:
# determine size of a given folder in MBytes
import os, subprocess, json, csv, platform
# Function to check if a Drive Letter exists
def hasdrive(letter):
return "Windows" in platform.system() and os.system("vol %s: 2>nul>nul" % (letter)) == 0
# Define Drive to check for
letter = 'S'
# Check if Drive doesnt exist, if not then map drive
if not hasdrive(letter):
subprocess.call(r'net use s: /del /Y', shell=True)
subprocess.call(r'net use s: \\path_to_files', shell=True)
list1 = []
# Import spreadsheet to calculate size
with open('c:\Temp\files_to_delete_subset.csv') as f:
reader = csv.reader(f, delimiter=':', quoting=csv.QUOTE_NONE)
for row in reader:
list1.extend(row)
# Define variables
folder = "S:"
folder_size = 0
# Exporting outcome
for list1 in list1:
folder = folder + str(list1)
for root, dirs, files in os.walk(folder):
for name in files:
folder_size += os.path.getsize(os.path.join(root, name))
print(folder)
# print(os.path.join(root, name) + " " + chr(os.path.getsize(os.path.join(root, name))))
print(folder_size)
From my understanding the max path size in Windows is 260 characters, so 1 driver letter + 100 character path should NOT exceed the Windows max.
Here is an example of a path: '/Document/8669/CORRESP/1722165.doc'
The folder string you're trying to walk is growing forever. Simplifying the code to the problem area:
folder = "S:"
# Exporting outcome
for list1 in list1:
folder = folder + str(list1)
You never set folder otherwise, so it starts out as S:<firstpath>, then on the next loop it's S:<firstpath><secondpath>, then S:<firstpath><secondpath><thirdpath>, etc. Simple fix: Separate drive from folder:
drive = "S:"
# Exporting outcome
for path in list1:
folder = drive + path
Now folder is constructed from scratch on each loop, throwing away the previous path, rather than concatenating them.
I also gave the iteration value a useful name (and removed the str call, because the values should all be str already).

How can i adjust my code to start with a new number?

import os
src = "/home/user/Desktop/images/"
ext = ".jpg"
for i,filename in enumerate(os.listdir(src)):
# print(i,filename)
if filename.endswith(ext):
os.rename(src + filename, src + str(i) + ext)
print(filename, src + str(i) + ext)
else :
os.remove(src + filename)
this code will rename all the images in a folder starting with 0.jpg,1.jpg etc... and remove none jpg but what if i already had some images in that folder, let's say i had images 0.jpg, 1.jpg, 2.jpg, then i added a few others called im5.jpg and someImage.jpg.
What i want to do is adjust the code to read the value of the last image number, in this case 2 and start counting from 3 .
In other words i'll ignore the already labeled images and proceed with the new ones counting from 3.
Terse and semi-tested version:
import os
import glob
offset = sorted(int(os.path.splitext(os.path.basename(filename))[0])
for filename in glob.glob(os.path.join(src, '*' + ext)))[-1] + 1
for i, filename in enumerate(os.listdir(src), start=offset):
...
Provided all *.jpg files consist of a only a number before their extension. Otherwise you will get a ValueError.
And if there happens to be a gap in the numbering, that gap will not be filled with new files. E.g., 1.jpg, 2.jpg, 3.jpg, 123.jpg will continue with 124.jpg (which is safer anyway).
If you need to filter out filenames such as im5.jpg or someImage.jpg, you could add an if-clause to the list comprehension, with a regular expression:
import os
import glob
import re
offset = sorted(int(os.path.splitext(os.path.basename(filename))[0])
for filename in glob.glob(os.path.join(src, '*' + ext))
if re.search('\d+' + ext, filename))[-1] + 1
Of course, by now the three lines are pretty unreadable, and may not win the code beauty contest.

Raster to polygon script loop failing!! error 99999!

I am trying to make a script which selects every .png file in a folder beginning with the letters "LG". I then want the scipt create a shapefile, replacing the "LG" with "SH", and then i want the script to buffer that shapefile and rename the buffer with the first 2 letters being "SB"!
I keep getting an error 99999 error message at line 37!
( gp.RasterToPolygon_conversion(INPUT_RASTER, Output_polygon_features, "SIMPLIFY", "VALUE") )
Can anyone see why this isnt working? I am very, very new to this and have been staring at this script pulling out my hair for days!!
Here is the script:
# Load required toolboxes...
gp.AddToolbox("C:/Program Files/ArcGIS/ArcToolbox/Toolboxes/Conversion Tools.tbx")
gp.AddToolbox("C:/Program Files/ArcGIS/ArcToolbox/Toolboxes/Analysis Tools.tbx")
# Script arguments...
folder = "D:\\J04-0083\\IMAGEFILES"
for root, dirs, filenames in os.walk(folder): # returms root, dirs, and files
for filename in filenames:
filename_split = os.path.splitext(filename) # filename and extensionname (extension in [1])
filename_zero = filename_split[0]
try:
first_2_letters = filename_zero[0] + filename_zero[1]
except:
first_2_letters = "XX"
if first_2_letters == "LG":
Output_polygon_features = "D:\\J04-0083\\ShapeFiles.gdb\\" + "SH_" + filename + ".shp"
# Process: Raster to Polygon...
INPUT_RASTER = os.path.join(root + "\\" + filename_zero + ".png")
gp.RasterToPolygon_conversion(INPUT_RASTER, Output_polygon_features, "SIMPLIFY", "VALUE")
Distance__value_or_field_ = "5 Meters"
Raster_Buffer_shp = "SB_" + filename + ".shp"
# Process: Buffer...
gp.Buffer_analysis(Output_polygon_features, Raster_Buffer_shp, Distance__value_or_field_, "FULL", "ROUND", "NONE", "")
Is .png the format that this function wants? PNG is a compressed format so I would think that something like this would be expecting an uncompressed format. In fact, since the name of the function is RasterToPolygon_conversion, wouldn't the function be expecting a raster format? The docs say that the input should be an integer raster dataset. In addition, The input raster can have any cell size and may be any valid integer raster dataset. Anyway, I suspect that is the real problem.
The last thing to check, if the file is in the correct format as per above, is if there is a field VALUE in the file.
try using a GRID or TIFF file instead of a PNG.
You can convert the PNG with:
http://webhelp.esri.com/arcgiSDEsktop/9.3/index.cfm?TopicName=raster_to_other_format_(multiple)_(conversion)
and then process it's output into the Raster to Polygon conversion.
You could also check the file path of the INPUT RASTER to make sure it looks correct by:
INPUT_RASTER = os.path.join(root + "\\" + filename_zero + ".png")
print INPUT_RASTER
gp.RasterToPolygon_conversion(INPUT_RASTER, Output_polygon_features, "SIMPLIFY", "VALUE")
There is also a method of building a filepath by:
import os
root + os.sep + filename_zero + '.png'

Categories