Unable to properly increment variable and convert to .wav to .mp3 - python-3.x

I am trying to create a new file recording every time this program runs and also convert those .wav files to .mp3. When I run this, it only creates a output.wav and output0.mp3 file and then when I run it again, no further files are created. Also the output0.mp3 that was converted is 0KB and cannot be played.
I do not get an error but it seems its not grabbing the output.wav properly that was originally created. I am running Python 3.7.
import os
import sounddevice as sd
from scipy.io.wavfile import write
from pydub import AudioSegment #for converting WAV to MP3
fs = 44100 # Sample rate
seconds = 3 # Duration of recording
myrecording = sd.rec(int(seconds * fs), samplerate=fs, channels=2)
sd.wait() # Wait until recording is finished
write('output.wav', fs, myrecording ) # Save as WAV file
#Increments file name by 1 so it writes a new file every time it runs
i = 0
while os.path.exists("output%s.wav" % i):
i += 1
# files for converting WAV to Mp3
src = ("output%s.wav" % i)
dst = ("output%s.mp3" % i)
# convert wav to mp3
sound = AudioSegment.from_mp3(src)
sound.export(dst, format="wav")
writefile = open("output%s.mp3" % i, "w")
EDIT:
Updated while loop to:
#Increments file name by 1 so it writes a new file every time it runs
i = 0
while os.path.exists("output%s.wav" % i):
# files for converting WAV to Mp3
src = ("output%s.wav" % i)
dst = ("output%s.mp3" % i)
# convert wav to mp3
sound = AudioSegment.from_mp3(src)
sound.export(dst, format="wav")
write("output%s.mp3" % i, "w")
i += 1

"create a new file recording every time this program runs " - To what I understand you just need to check for existing files and get a counter to reach +1 then the last file. Once you get that just create/convert file based on that.
I am not familiar with working of sound module, but in general below should be the code structure.
## This will create new recording file called output.wav
myrecording = sd.rec(int(seconds * fs), samplerate=fs, channels=2)
sd.wait() # Wait until recording is finished
write('output.wav', fs, myrecording ) # Save as WAV file
# Get the counter to reach the last file that has been created.
# For e.g. if last file generated was output5.wav then below loop will run 5 times
# and should change the value of i = 6.
i = 0
while os.path.exists("output%s.wav" % i):
i += 1
# Code for creating new file using value of 'i'
# Below code is outside of while loop and will run only once,
# as 1 file needs to be created per run of the program.
src = ("output.wav")
dst = ("output%s.mp3" % i)
# convert wav to mp3
sound = AudioSegment.from_mp3(src)
sound.export(dst, format="wav")
# Not sure if this is needed.
# Check working of sound module and what does sound.export do
writefile = open("output%s.mp3" % i, "w")

SOLUTION: Updated my while loop and changed the conversion method
i = 0
while not os.path.exists("output.wav"):
i += 1
fs = 44100 # Sample rate
seconds = 3 # Duration of recording
myrecording = sd.rec(int(seconds * fs), samplerate=fs, channels=2)
sd.wait() # Wait until recording is finished
write('output{0}.wav'.format(i), fs, myrecording ) # Save as WAV file
print("recording has finished")
datadir = str(Path(r"FilePathtoFolderWhereAudioFileIs"))
filetopen = datadir + "/" + 'output{0}.wav'.format(i)
sound = pydub.AudioSegment.from_wav(r"FilePathtoFolderWhereAudioFileIs""" + "\\output{0}.wav".format(i))
sound.export(r"FilePathtoFolderWhereAudioFileIs""" + "\\output{0}.mp3".format(i), format="mp3")
print("Converted wav to mp3")
time.sleep(3)

Related

How to convert grayscale photo pixel values ​from 10bit to 8bit in Python?

I have a binary file with pixels whose grayscale depth is 10 bits. How can I make an image out of such pixels?
from bitstring import ConstBitStream
import bitstring, time, sys
start_time = time.time()
b = ConstBitStream(filename=str(sys.argv[1]))
while True:
try:
output = b.read(10)
except bitstring.ReadError:
print("--- %s seconds ---" % (time.time() - start_time))
sys.exit()
with open(str(sys.argv[2]), 'ab') as file:
file.write((int(output.uint/4)).to_bytes(1, byteorder='big', signed=False))
I tried this code to convert 10bit pixels to 8bit to save it as an 8bit grayscale image, but converting such a file from 10bit to 8bit takes a very long time. How can I make it faster?
UPDATE
I decided to redo this script. I decided to convert the file to a bit stream and then divide it into values ​​of 10 bits -> deleting the last 2 bits and writing to the file. It works very fast!
Total chunks: 104751130
Input file weight: 130938912
Output file weight: 104751130
Total time: 58.524515867233276
And new code:
import bitstring, time, os
start_time = time.time()
file = open('file.bin', 'rb').read() # Read input file
b = bitstring.BitStream(file).bin # Make bitstream
chunks = [b[i:i+10] for i in range(0, len(b), 10)] # Make list with bin data and with len = 10 bit
chunks = [str[:-2] for str in chunks] # cut 2 bit from end (10->8 bit)
with open('outdata.new', 'wb') as file:
bitstring.BitArray(bin=''.join(chunks)).tofile(file) # Convert List to Str and write bits to file
print('Total chunks: '+str(len(chunks)))
print('Input file weight: '+str(os.path.getsize("file.bin")))
print('Output file weight: '+str(os.path.getsize("outdata.new")))
print('Total time: '+str(time.time()-start_time))
I hope this can also help someone!

How to use audio frame after decode mp3 file using pyav, ffmpeg, python

I am using python with pyav, ffmpeg to decode mp3 in the memory. I know there is some other way to do it, like the pipe ffmpeg command. However, I would like to explore pyav and ffmpeg API. So I have the following code. It works but the sound is very noisy, although hearable:
import numpy as np
import av # to convert mp3 to wav using ffmpeg
import pyaudio # to play music
mp3_path = 'D:/MyProg/python/SauTimThiepHong.mp3'
def decodeStream(mp3_path):
# Run NOT OK
container = av.open(mp3_path)
stream = next(s for s in container.streams if s.type == 'audio')
frame_count = 0
data = bytearray()
for packet in container.demux(stream):
# <class 'av.packet.Packet'>
# We need to skip the "flushing" packets that `demux` generates.
#if frame_count == 5000 : break
if packet.dts is None:
continue
for frame in packet.decode():
#<av.AudioFrame 0, pts=1843200, 1152 samples at 44100Hz, stereo, fltp at 0x1d074bd2e48>
# type(frame) : <class 'av.audio.frame.AudioFrame'>
#frame.samples = 1152 : 1152 diem du lieu : Number of audio samples (per channel)
# moi frame co size = 1152 (diem) * 2 (channels) * 4 (bytes / diem) = 9216 bytes
# 11021 frames
#arr = frame.to_ndarray() # arr.nbytes = 9216
#channels = []
channels = frame.to_ndarray().astype("float16")
#for plane in frame.planes:
#channels.append(plane.to_bytes()) #plane has 4 bytes / sample, but audio has only 2 bytes
# channels.append(np.frombuffer(plane, dtype=np.single).astype("float16"))
#channels.append(np.frombuffer(plane, dtype=np.single)) # kieu np.single co 4 bytes
if not frame.is_corrupt:
#data.extend(np.frombuffer(frame.planes[0], dtype=np.single).astype("float16")) # 1 channel: noisy
# type(planes) : <class 'av.audio.plane.AudioPlane'>
frame_count += 1
#print( '>>>> %04d' % frame_count, frame)
#if frame_count == 5000 : break
# mix channels:
for i in range(frame.samples):
for ch in channels: # dec_ctx->channels
data.extend(ch[i]) #noisy
#fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile)
return bytes(data)
I use pipe ffmpeg to get decoded data to compare and find they are different:
def RunFFMPEG(mp3_path, target_fs = "44100"):
# Run OK
import subprocess
# init command
ffmpeg_command = ["ffmpeg", "-i", mp3_path,
"-ab", "128k", "-acodec", "pcm_s16le", "-ac", "0", "-ar", target_fs, "-map",
"0:a", "-map_metadata", "-1", "-sn", "-vn", "-y",
"-f", "wav", "pipe:1"]
# excute ffmpeg command
pipe = subprocess.run(ffmpeg_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize= 10**8)
# debug
#print(pipe.stdout, pipe.stderr)
# read signal as numpy array and assign sampling rate
#audio_np = np.frombuffer(buffer=pipe.stdout, dtype=np.uint16, offset=44)
#audio_np = np.frombuffer(buffer=pipe.stdout, dtype=np.uint16)
#sig, fs = audio_np, target_fs
#return audio_np
return pipe.stdout[78:]
Then I use pyaudio to play data and find it very noisy
p = pyaudio.PyAudio()
streamOut = p.open(format=pyaudio.paInt16, channels=2, rate= 44100, output=True)
#streamOut = p.open(format=pyaudio.paInt16, channels=1, rate= 44100, output=True)
mydata = decodeStream(mp3_path)
print("bytes of mydata = ", len(mydata))
#print("bytes of mydata = ", mydata.nbytes)
ffMpegdata = RunFFMPEG(mp3_path)
print("bytes of ffMpegdata = ", len(ffMpegdata))
#print("bytes of ffMpegdata = ", ffMpegdata.nbytes)
minlen = min(len(mydata), len(ffMpegdata))
print("mydata == ffMpegdata", mydata[:minlen] == ffMpegdata[:minlen]) # ffMpegdata.tobytes()[:minlen] )
#bytes of mydata = 50784768
#bytes of ffMpegdata = 50784768
#mydata == ffMpegdata False
streamOut.write(mydata)
streamOut.write(ffMpegdata)
streamOut.stop_stream()
streamOut.close()
p.terminate()
Please help me to understand decoded frame of pyav api (after for frame in packet.decode()). Should it be processed more? or I have some error?

Multiprocessing queue of files in folders

Question is how to properly process files in Python 3.7 multiprocessing in case when I'm crawling directories recursively.
My code is as following:
def f(directoryout,directoryoutfailed,datafile,filelist_failed,imagefile,rootpath,extension,debug):
[...] some processing
if __name__ == '__main__':
import csv
import os
debug = 0
timeout = 20
if debug == 0:
folder = '/home/debian/Desktop/environments/dedpul/files/fp/'
datafile = 'fpdata.csv' # file with results
directoryout = 'fp_out' # out directory for debugging
directoryoutfailed = 'fp_out_failed' # out directory for wrongly processed for debuggin mode
filelist = 'filelist.csv' # list of processed files
filelist_failed = 'filelist_failed.csv' # list of wrongly processed files
counter = 0
pool = Pool(processes=4)
for root, subFolders, files in os.walk(folder):
for imagefile in files:
rootpath = root+'/'
fullpath = root+'/'+imagefile
extension = os.path.splitext(imagefile)[1]
imagefilesplit = imagefile.split('.')[0]
counter += 1
print('\033[93m ## ',counter,' ## \033[0m',rootpath)
fileexist = 0
with open(filelist) as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
for row in csv_reader:
if row[0] == fullpath:
fileexist = 1
if fileexist == 1:
print(' File was processed, skipping...')
continue
with open(filelist, mode='a') as csv_file:
writer = csv.writer(csv_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
writer.writerow([fullpath])
# print(directoryout,directoryoutfailed,datafile,filelist_failed,imagefile,rootpath,extension,debug)
res = pool.apply(f, (directoryout,directoryoutfailed,datafile,filelist_failed,imagefile,rootpath,extension,debug))
pool.close()
pool.join()
1st, when I'm using pool.apply_async it uses all cores, however it doesn't process function f() correctly. With pool.apply() it works single-threading.
2nd, as you see, I'm crawling recursively list of files in folders in loop. If file was found as processed, this loop should continue. Should I do that in __ main __ function, or it should be moved to f() function? If yes, how to exchange what is during processing, which takes a few seconds per file?
3rd, function f() is independent, so if it will process image file and then it will add results to fpdata.csv file (or add name of not-well-processed file to filelist_failed.csv) and just close processing without any problems, so no real output is needed. I need just to start this function in multiprocessing.
What I am doing wrong? Should I use
with Pool(processes=4) as pool:
statement?
Before asking this query I've browsed tons of answers, but apparently it was extremely hard to find such file processing, in Python Manual as well.

I want to merge two codes but keep giving me an error

Hi I am new to python and I am trying to record the sound when there is loudness is over a certain value (in the code 200).
Initially, I had two different pieces of code. one for recording and one for the detecting loudness. I want to merge together and record the sound when the loudness is over 200.
But This keeps giving me an error so I am wondering which part am I missing.
I would very appreciate if some one help me to figure this out.
import time
import grovepi
import os
import sys
sys.path.insert(0,'.')
from audio import soundrecord
loudness_sensor = 0
while True:
try:
# Read the sound level
sensor_value = grovepi.analogRead(loudness_sensor)
print("sensor_value = %d" %sensor_value)
time.sleep(.5)
if sensor_value> 200:
soundrecord()
time.sleep(10)
except IOError:
print ("Error")
I defined the below recording code as a soundrecord function and put it in the same directory
import pyaudio
import wave
def soundrecord():
form_1 = pyaudio.paInt16 # 16-bit resolution
chans = 1 # 1 channel
samp_rate = 44100 # 44.1kHz sampling rate
chunk = 4096 # 2^12 samples for buffer
record_secs = 3 # seconds to record
dev_index = 2 # device index found by p.get_device_info_by_index(ii)
wav_output_filename = 'test1.wav' # name of .wav file
audio = pyaudio.PyAudio() # create pyaudio instantiation
# create pyaudio stream
stream = audio.open(format = form_1,rate = samp_rate,channels = chans, \
input_device_index = dev_index,input = True, \
frames_per_buffer=chunk)
print("recording")
frames = []
# loop through stream and append audio chunks to frame array
for ii in range(0,int((samp_rate/chunk)*record_secs)):
data = stream.read(chunk)
frames.append(data)
print("finished recording")
# stop the stream, close it, and terminate the pyaudio instantiation
stream.stop_stream()
stream.close()
audio.terminate()
# save the audio frames as .wav file
wavefile = wave.open(wav_output_filename,'wb')
wavefile.setnchannels(chans)
wavefile.setsampwidth(audio.get_sample_size(form_1))
wavefile.setframerate(samp_rate)
wavefile.writeframes(b''.join(frames))
wavefile.close()
expecting: Record the sound when the loudness is over 200
actual :
sensor_value = 75
sensor_value = 268
Error
sensor_value = 360
Error
sensor_value = 48
sensor_value = 39
sensor_value = 79

Split a zip archive into multiple chunks

I'm trying to create a zip archive of a possibly huge folder.
For this purpose I'm using the python zipfile module, but as far as I can see there is no option to split the created archive into multiple chunks with a max size.
The zipped archive is supposed to be sent via Telegram, which has a size limitation of 1.5 GB per file. Thereby I need to split the resulting zip archive.
I would really like to not use a subprocess and shell commands for creating this archive.
My current code looks like this:
def create_zip(archive_name, directory):
"""Create a zip file from given dir path."""
with ZipFile(archive_name, "w", ZIP_LZMA) as target_zip_file:
for root, _, files in os.walk(directory):
for file_to_zip in files:
absolute_path = os.path.join(root, file_to_zip)
zip_file_name = absolute_path[len(directory) + len(os.sep):]
target_zip_file.write(absolute_path, zip_file_name)
return target_zip_file
Thanks in Advance
Here is what i use to send file to telegram channel by telegram bot.
The file size limit is 50MB in upload by telegram bot
The file size limit is 1500MB in upload by telegram client but you may add some text or other info so 1495 is more safe
#! /usr/bin/python3
# -*- coding:utf-8 -*-
# apt-get install p7zip-full
import subprocess
import os
import math
import logzero
logger = logzero.logger
MAX_SPLIT_SIZE = 1495
def file_split_7z(file_path, split_size=MAX_SPLIT_SIZE):
file_path_7z_list = []
# if origin file is 7z file rename it
origin_file_path = ""
if os.path.splitext(file_path)[1] == ".7z":
origin_file_path = file_path
file_path = os.path.splitext(origin_file_path)[0] + ".7zo"
os.rename(origin_file_path, file_path)
# do 7z compress
fz = os.path.getsize(file_path) / 1024 / 1024
pa = math.ceil(fz / split_size)
head, ext = os.path.splitext(os.path.abspath(file_path))
archive_head = "".join((head, ext.replace(".", "_"))) + ".7z"
for i in range(pa):
check_file_name = "{}.{:03d}".format(archive_head, i + 1)
if os.path.isfile(check_file_name):
logger.debug("remove exists file | {}".format(check_file_name))
os.remove(check_file_name)
cmd_7z = ["7z", "a", "-v{}m".format(split_size), "-y", "-mx0", archive_head, file_path]
proc = subprocess.Popen(cmd_7z, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate()
if b"Everything is Ok" not in out:
logger.error("7z output | {}".format(out.decode("utf-8")))
logger.error("7z error | {}".format(err.decode("utf-8")))
return file_path_7z_list
for i in range(pa):
file_path_7z_list.append("{}.{:03d}".format(archive_head, i + 1))
# if origin file is 7z file rename it back
if origin_file_path:
os.rename(file_path, origin_file_path)
return file_path_7z_list
def do_file_split(file_path, split_size=MAX_SPLIT_SIZE):
"""caculate split size
example max split size is 1495 file size is 2000
than the split part num should be int(2000 / 1495 + 0.5) = 2
so the split size should be 1000 + 1000 but not 1495 + 505
with the file size increase the upload risk would be increase too
"""
file_size = os.path.getsize(file_path) / 2 ** 20
split_part = math.ceil(file_size / split_size)
new_split_size = math.ceil(file_size / split_part)
logger.info("file size | {} | split num | {} | split size | {}".format(file_size, split_part, new_split_size))
file_path_7z_list = file_split_7z(file_path, split_size=new_split_size)
return file_path_7z_list
In case you don't find a better, native way with zipfile, you could still write the file splitting algorithm yourself. Something like this:
outfile = archive_name
packet_size = int(1.5 * 1024**3) # bytes
with open(outfile, "rb") as output:
filecount = 0
while True:
data = output.read(packet_size)
print(len(data))
if not data:
break # we're done
with open("{}{:03}".format(outfile, filecount), "wb") as packet:
packet.write(data)
filecount += 1
And similar to put it back together on the receiver's side.

Resources