Playing audio in python at given timestamp - python

I am trying to find a way in python to play a section of an audio file given a start and end time.
For example, say I have an audio file that is 1 min in duration. I want to play the section from 0:30 to 0:45 seconds.
I do not want to process or splice the file, only playback of the given section.
Any suggestions would be greatly appreciated!
Update:
I found a great solution using pydub:
https://github.com/jiaaro/pydub
from pydub import AudioSegment
from pydub.playback import play
audiofile = #path to audiofile
start_ms = #start of clip in milliseconds
end_ms = #end of clip in milliseconds
sound = AudioSegment.from_file(audiofile, format="wav")
splice = sound[start_ms:end_ms]
play(splice)

step one is to get your python to play entire audio file ... several libraries are available for this ... see if the library has a time specific api call ... you can always roll up your sleeves and implement this yourself after you read the audio file into a buffer or possibly stream the file and stop streaming at end of chosen time section
Another alternative is to leverage command line tools like ffmpeg which is the Swiss Army Knife of audio processing ... ffmpeg has command line input parms to do time specific start and stop ... also look at its sibling ffplay
Similar to ffplay/ffmpeg is another command line audio tool called sox

Use PyMedia and Player. Look at the functions SeekTo() and SeekEndTime(). I think you will be able to find a right solution after playing around with these functions.

I always have trouble installing external libraries and if you are running your code on a server and you don't have sudo privileges then it becomes even more cumbersome. Don't even get me started on ffmpeg installation.
So, here's an alternative solution with scipy and native IPython that avoids the hassle of installing some other library.
from scipy.io import wavfile # to read and write audio files
import IPython #to play them in jupyter notebook without the hassle of some other library
def PlayAudioSegment(filepath, start, end, channel='none'):
# get sample rate and audio data
sample_rate, audio_data = wavfile.read(filepath) # where filepath = 'directory/audio.wav'
#get length in minutes of audio file
print('duration: ', audio_data.shape[0] / sample_rate / 60,'min')
## splice the audio with prefered start and end times
spliced_audio = audio_data[start * sample_rate : end * sample_rate, :]
## choose left or right channel if preferred (0 or 1 for left and right, respectively; or leave as a string to keep as stereo)
spliced_audio = spliced_audio[:,channel] if type(channel)==int else spliced_audio
## playback natively with IPython; shape needs to be (nChannel,nSamples)
return IPython.display.Audio(spliced_audio.T, rate=sample_rate)
Use like this:
filepath = 'directory_with_file/audio.wav'
start = 30 # in seconds
end = 45 # in seconds
channel = 0 # left channel
PlayAudioSegment(filepath,start,end,channel)

Related

How to merge/layer two audio files using Python AudioSegment without it breaking the wav header

I am more or less following the code below to merge two audio files. It mostly works, where audio segment can export both the original files and the combined file to a folder. These play fine in finder (Mac). However, when brought into a music app like Ableton, the waveform is distorted and sounds like digital garbage. I have a feeling this is because this code is messing with the wav header.
I have also noted the combined sound is showing a bitrate of 32 in the finder file info, whereas I am specifically outputting it as bitrate='24'
Any theories?
from pydub import AudioSegment
sound1 = AudioSegment.from_file("1.wav", format="wav")
sound2 = AudioSegment.from_file("2.wav", format="wav")
# Overlay sound2 over sound1 at position 0
overlay = sound1.overlay(sound2, position=0)
# simple export
file_handle = overlay.export("output.wav", format="wav", bitrate='24')
Note to others, I solved this by moving to using Sox (or PySox) instead of AudioSegment, which seams to work much more reliably with all the features I was looking for.

Python - Reading a large audio file to a stream?

The Question
I want to load an audio file of any type (mp3, m4a, flac, etc) and write it to an output stream.
I tried using pydub, but it loads the entire file at once which takes forever and runs out of memory easily.
I also tried using python-vlc, but it's been unreliable and too much of a black box.
So, how can I open large audio files chunk-by-chunk for streaming?
Edit #1
I found half of a solution here, but I'll need to do more research for the other half.
TL;DR: Use subprocess and ffmpeg to convert the file to wav data, and pipe that data into np.frombuffer. The problem is, the subprocess still has to finish before frombuffer is used.
...unless it's possible to have the pipe written to on 1 thread while np reads it from another thread, which I haven't tested yet. For now, this problem is not solved.
I think the python package https://github.com/irmen/pyminiaudio can be of helpful. You can stream an audio file like this
import miniaudio
audio_path = "my_audio_file.mp3"
target_sampling_rate = 44100 #the input audio will be resampled a this sampling rate
n_channels = 1 #either 1 or 2
waveform_duration = 30 #in seconds
offset = 15 #this means that we read only in the interval [15s, duration of file]
waveform_generator = miniaudio.stream_file(
filename = audio_path,
sample_rate = target_sampling_rate,
seek_frame = int(offset * target_sampling_rate),
frames_to_read = int(waveform_duration * target_sampling_rate),
output_format = miniaudio.SampleFormat.FLOAT32,
nchannels = n_channels)
for waveform in waveform_generator:
#do something with the waveform....
I know for sure that this works on mp3, ogg, wav, flac but for some reason it does not on mp4/acc and I am actually looking for a way to read mp4/acc

Speech Recognizer return listens to just the beginning of the audio

I am using Python's SpeechRecognition to extract text from an audio file. The issue I'm having is that it only works on the very few seconds of the clip. This is my code:
import speech_recognition as spr
AUDIO_FILE = "file.wav"
recognizer = spr.Recognizer()
with spr.AudioFile(AUDIO_FILE) as source:
recognizer.adjust_for_ambient_noise(source, duration=0.2)
audio = recognizer.record(source)
text = recognizer.recognize_google(audio, language='es-CL')
print(text)
The recognition works alright, but just for the very few seconds. Namely, text contains the words corresponding to the first 7 seconds of an audio clip that is 59 seconds long.
I don't see how this could be happening, maybe because there is a silence in the clip and the recognizer assumes that the audio has ended? Is there any way to configure this parameter (if it exists)? If it's not the case, what could be the issue?
As far as I could google it, I found out that you need to either give a duration for the system to listen to the whole file or iterate the recog1.listen(source) part until the end of the file, because in this link you are able to inspect the source code and it says on line 215 something like "If you specify 10 seconds duration, then in every iteration you will get the next 10 seconds of the file". I hope it works out.

Generating Silence with pyDub

I've found pyDub, and it seems like just what I need:
http://pydub.com/
The only issue is with generating silence. Can pyDub do this?
Essentially the workflow I want is:
Take all the WAV files in a directory
Piece them together in filename order with 1 sec of silence in between
Generate a single MP3 of the result
Is this possible? I realize I could create a WAV of silence and do it that way (spacer GIF flashback, anyone?), but I'd prefer to generate the silence programmatically, because I may want to experiment with the duration of silence and/or the bitrate of the MP3.
I greatly appreciate any responses.
The pydub sequences are composed of pydub.AudioSegment instances. The pydub quickstart documentation only shows how to create AudioSegments from files.
However, reading the source, or even more easily, running pydoc pydub.AudioSequence reveals
pydub.AudioSegment = class AudioSegment(__builtin__.object)
| AudioSegments are *immutable* objects representing segments of audio
| that can be manipulated using python code.
…
| silent(cls, duration=1000) from __builtin__.type
| Generate a silent audio segment.
| duration specified in milliseconds (default: 1000ms).
which would be called like (following the usage in the quick start guide):
from pydub import AudioSegment
second_of_silence = AudioSegment.silent() # use default
second_of_silence = AudioSegment.silent(duration=1000) # or be explicit
now second_of_silence would be an AudioSegement just like song in the example
song = AudioSegment.from_wav("never_gonna_give_you_up.wav")
and could be manipulated, composed, etc. with no blank audio files needed.

Using pyDub to chop up a long audio file

I'd like to use pyDub to take a long WAV file of individual words (and silence in between) as input, then strip out all the silence, and output the remaining chunks is individual WAV files. The filenames can just be sequential numbers, like 001.wav, 002.wav, 003.wav, etc.
The "Yet another Example?" example on the Github page does something very similar, but rather than outputting separate files, it combines the silence-stripped segments back together into one file:
from pydub import AudioSegment
from pydub.utils import db_to_float
# Let's load up the audio we need...
podcast = AudioSegment.from_mp3("podcast.mp3")
intro = AudioSegment.from_wav("intro.wav")
outro = AudioSegment.from_wav("outro.wav")
# Let's consider anything that is 30 decibels quieter than
# the average volume of the podcast to be silence
average_loudness = podcast.rms
silence_threshold = average_loudness * db_to_float(-30)
# filter out the silence
podcast_parts = (ms for ms in podcast if ms.rms > silence_threshold)
# combine all the chunks back together
podcast = reduce(lambda a, b: a + b, podcast_parts)
# add on the bumpers
podcast = intro + podcast + outro
# save the result
podcast.export("podcast_processed.mp3", format="mp3")
Is it possible to output those podcast_parts fragments as individual WAV files? If so, how?
Thanks!
The example code is pretty simplified, you'll probably want to look at the strip_silence function:
https://github.com/jiaaro/pydub/blob/2644289067aa05dbb832974ac75cdc91c3ea6911/pydub/effects.py#L98
And then just export each chunk instead of combining them.
The main difference between the example and the strip_silence function is the example looks at one millisecond slices, which doesn't count low frequency sound very well since one waveform of a 40hz sound, for example, is 25 milliseconds long.
The answer to your original question though, is that all those slices of the original audio segment are also audio segments, so you can just call the export method on them :)
update: you may want to take a look at the silence utilities I've just pushed up into the master branch; especially split_on_silence() which could do this (assuming the right specific arguments) like so:
from pydub import AudioSegment
from pydub.silence import split_on_silence
sound = AudioSegment.from_mp3("my_file.mp3")
chunks = split_on_silence(sound,
# must be silent for at least half a second
min_silence_len=500,
# consider it silent if quieter than -16 dBFS
silence_thresh=-16
)
you could export all the individual chunks as wav files like this:
for i, chunk in enumerate(chunks):
chunk.export("/path/to/ouput/dir/chunk{0}.wav".format(i), format="wav")
which would make output each one named "chunk0.wav", "chunk1.wav", "chunk2.wav", and so on

Categories