Would you like to extract sound clips from your favorite podcast or other audio files? This post describes how to do this for free! You can mess around with audio file programs but how do you find the words or phrases you want to clip? The solution is a combination of two easy to use python programs:
- AudioText which you can read about in this post. This is an application that uses the Vosk API to transcribe audio files to a text transcript. The transcript contains start and stop times for each individual word in the podcast.
- ClipGenerator is described in this post. The program uses the transcript file generated by AudioText to provide an easy UI that allows you to select individual words or phrases and save them as a new audio file. Most importantly, you can search the transcript by word to find what you want.
Please leave a comment below if you have any issues or suggestions. The source code uses Pydub and illustrates how to work with WAV files using python.
Installation
To install a copy of SuperCut just follow these instructions.
You have two choices for installing SuperCut. The first choice is for people who have Python skills and want to work with the source code. The second choice is for people who just want to run the program (aka the easy way).
Easy Installation – WINDOWS ONLY – Coming Soon
This is the best and easiest way to install and run SuperCut. Simply download this zip file and extract the entire archive into a folder on your file system.
SuperCut Zip File
The folder you download is a complete (batteries included) python environment and python application with all the needed python libraries. This is nice as it won’t mess up any existing python installations you might have or if you know nothing about Python it does all the work for you. This “compiled” version of the SuperCut python application was created using PyInstaller which you can read about here.
The screen shot below shows the directory structure. You can see the ” SuperCut.exe” file. This is the executable that will run the SuperCut python appliication. This will only work on Windows. If you have a Linux or Mac system you will have to go the Python Installation (see below) route and build your own python environment.
Warning – The EXE is not code signed
The SuperCut.exe file is not “code signed”. This means that Windows Defender will display a message warning you not to run the program. It will also provide a button for you to run it anyway. Your anti-virus may also object to running this exe. The only way for me to avoid this is to spend $400 dollars on a Windows code signing certificate which I’m not going to do. Sorry, but this is how Windows works. Apple is better and provides code signing certs when you pay the $95 a year fee to be an Apple Developer.
Python Installation
If you performed the Easy Installation above you do not need to do this step. This is for Python gearheads that want to work with the source code. Here are the steps:
- You’ve already created a Python environment in order to run AudioText which was described in this post. If not you might as well start there as AudioText is required to produce the JSON transcription file that is used by SuperCut.
- Cut and paste the SuperCut source code (see below) into a file in your virtual environment and give it a try. The entire application is contained in one python file – SuperCut.py.
How To Use SuperCut
SuperCut will allow you to easily extract sound clips from audio files or podcasts that you have transcribed to text using AudioText. The transcription process produces an output file that contains every word along with it’s start and stop time. You have to run AudioText on your downloaded podcast file before using SuperCut. Read about AudioText here.
The screenshot above shows SuperCut when it first starts. Most of the action is controlled by the buttons on the right hand side of the screen. Let’s work our way through each button and what it does.
- Input Files – at the top of the screen you see the two input files. Use the buttons on the right to select the input files.
- Select JSON Transcript – select the “results” file produced by AudioText.
- Select Audio File – select the WAV file that was produced by AudioText from your podcast MP3 file.
- Search Bar – The search bar allows you to search for words. Type in a search term (case sensitive) and click the Search Transcript button.
- Search Transcript – Search the transcript file for the search term you entered.
- Search Next – Find the next instance of the search term.
- Transcript Grid – The transcript grid displays the transcript text produced by AudioText. Click on an item (either the entire phrase or individual words) to select it. Shift click and Ctrl click to select multiple items.
- Add Selected Items – This will copy the selected items to the SuperCut grid below.
- Play Selected Items – This will play the selected items so you can decide if you want to use them.
- SuperCut Grid – the SuperCut grid displays the phrases and words you have selected to become part of your super cut. Use the buttons on the right to further manipulate the super cut.
- Play Selected Items – This plays only the items you have selected.
- Play SuperCut – This plays the entire super cut.
- Remove Selected Items – This removes the selected items from the grid.
- Clear Entire SuperCut – This removes everything from the super cut.
- Save SuperCut Audio File – This saves the super cut as a WAV file.
- Move Up – This moves the selected item up one row.
- Move Down – This moves the selected item down one row.
The menu bar at the top has one menu option “File” with the following menu options underneath:
- New – Start a new super cut. This clears the screen so you can start over.
- Open – Open a saved super cut definition.
- Save – Save the open super cut definition.
- Save As – Save the open super cut definition with a new file name.
- Exit – quit the program.
Source Code
Here is the full source code if you’d rather cut and paste the into your python environment. I described how to setup a python environment for all the code examples here.
The source code uses Pydub and illustrates how to work with WAV files using pydub. It uses the AudioSegment object and shows:
- how to create a empty pydub audiosegment object
- how to combine two pydub audiosegment objects
- how to slice a pydub audiosegment object from a larger audiosegment object
- how to play a pydub audiosegment object.
Bookmark this post as the code will probably be updated with improvements.
'''
Copyright 2021 SingerLinks Consulting LLC
This file is part of ClipGenerator.
ClipGenerator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ClipGenerator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ClipGenerator. If not, see <https://www.gnu.org/licenses/>.
'''
import json
import datetime
import os
from pydub import AudioSegment
from pydub.playback import play
from tkinter import *
from tkinter import ttk
from tkinter.ttk import *
from tkinter import messagebox
from tkinter import filedialog as fd
START, END, FOUND, TYPE = range(4)
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.transcriptionData = None
self.sound = None
self.cutFileName = None
self.superCutDict = None
# widget can take all window
self.pack(fill=BOTH, expand=1)
# this hack fixes treeview tag configure problem
style = ttk.Style(self)
actualTheme = style.theme_use()
style.theme_create("copy", parent=actualTheme)
style.theme_use("copy")
# configure style for treeviews
style.configure("mystyle.Treeview", highlightthickness=0, bd=0, font=('Calibri', 11)) # Modify the font of the body
style.configure("mystyle.Treeview.Heading", font=('Calibri', 13,'bold')) # Modify the font of the headings
style.map('Treeview', background=[('selected', 'white')])
# menu system
menu = Menu(self.master)
self.master.config(menu=menu)
fileMenu = Menu(menu)
fileMenu.add_command(label="New...", command=self.newCutFile)
fileMenu.add_command(label="Open...", command=self.openCutFile)
fileMenu.add_command(label="Save", command=self.saveCutFile)
fileMenu.add_command(label="Save As...", command=self.saveAsCutFile)
fileMenu.add_command(label="Exit", command=self.exitProgram)
menu.add_cascade(label="File", menu=fileMenu)
# main frames
self.header = Frame(self)
self.header.grid(row = 0, column = 0, padx = 5, pady = 5, sticky=NSEW)
self.transcript = Frame(self)
self.transcript.grid(row = 1, column = 0, padx = 5, pady = 5, sticky=NSEW)
self.transcript.grid_columnconfigure(0, weight=1)
self.transcript.grid_rowconfigure(0, weight=1)
self.superCut = Frame(self)
self.superCut.grid(row = 2, column = 0, padx = 5, pady = 5, sticky=NSEW)
self.superCut.grid_columnconfigure(0, weight=1)
self.superCut.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0, weight=0)
self.grid_rowconfigure(1, weight=3)
self.grid_rowconfigure(2, weight=3)
# header widgets
# # json file controls
self.lbl1 = Label(self.header, text="Json File Name:")
self.lbl1.grid(row = 0, column = 0, padx = 5, pady = 5, sticky="w")
self.jsonFileName = StringVar()
self.txtBoxName = Label(self.header, textvariable = self.jsonFileName)
self.txtBoxName.grid(row = 0, column = 1, padx = 5, pady = 5, sticky=W)
# # audio file controls
self.lbl2 = Label(self.header, text="Audio File Name:")
self.lbl2.grid(row = 1, column = 0, padx = 5, pady = 5, sticky=W)
self.audioFileName = StringVar()
self.txtBoxAudio = Label(self.header, textvariable = self.audioFileName)
self.txtBoxAudio.grid(row = 1, column = 1, padx = 5, pady = 5, sticky=W)
# buttons
self.btnPickJson = Button(self.header, text = 'Select JSON transcript', command = self.openJsonFile)
self.btnPickAudio = Button(self.header, text = 'Select Audio file', command = self.openAudioFile)
self.btnPickJson.grid(row = 0, column = 2, padx = 5, pady = 5, sticky=E)
self.btnPickAudio.grid(row = 1, column = 2, padx = 5, pady = 5, sticky=E)
self.header.grid_columnconfigure(1, weight=1)
#Transcript Widgets
# search bar
self.searchframe = Frame(self.transcript)
self.searchframe.grid(row = 0, column = 0, padx = 5, pady = 5, sticky=NSEW)
self.lbl2 = Label(self.searchframe, text="Search Term:")
self.lbl2.grid(row = 0, column = 0, padx = 5, pady = 5, sticky="w")
self.searchTerm = Entry(self.searchframe)
self.searchTerm.grid(row=0, column=1, padx = 5, pady = 5, sticky="ew")
self.searchBtn = Button(self.searchframe, text = 'Search Transcript', command = self.searchTranscript)
self.searchBtn.grid(row=0, column=2, padx = 5, pady = 5, sticky="e")
self.nextBtn = Button(self.searchframe, text = 'Find Next', command = self.nextFind)
self.nextBtn.grid(row=0, column=3, padx = 5, pady = 5, sticky="e")
# scrollable treeview
self.jsonframe = Frame(self.transcript)
self.jsonButtonBox = Frame(self.transcript)
self.jsonframe.grid(row = 1, column = 0, padx = 5, pady = 5, sticky=NSEW)
self.jsonButtonBox.grid(row = 1, column = 1, padx = 5, pady = 5, sticky=NSEW)
self.tv = ttk.Treeview(self.jsonframe, style="mystyle.Treeview")
self.tv.grid(row = 0, column = 0, padx = 5, pady = 5, sticky=NSEW)
self.tv.selectmode='extended'
self.tv.tag_configure("FOUND", background="#000fff000")
self.sb = Scrollbar(self.jsonframe, orient=VERTICAL)
self.sb.grid(row = 0, column = 1, padx = 5, pady = 5, sticky=NS)
self.tv.grid(row = 0, column = 0, padx = 5, pady = 5, sticky=NSEW)
self.searchframe.grid_columnconfigure(1, weight=1)
self.jsonframe.grid_columnconfigure(0, weight=1)
self.jsonframe.grid_rowconfigure(0, weight=1)
self.transcript.grid_columnconfigure(0, weight=1)
self.tv.config(yscrollcommand=self.sb.set)
self.sb.config(command=self.tv.yview)
# buttons
self.btnAdd = Button(self.jsonButtonBox, text = 'Add Selected Items', command = self.addItem)
self.btnAdd.grid(row = 0, column = 0, padx = 5, pady = 5, sticky=N)
#SuperCut Widgets
# scrollable treeview grid
self.SuperCut = Frame(self.superCut)
self.SuperCut.grid(row = 0, column = 0, padx = 5, pady = 5, sticky=NSEW)
self.scButtonBox = Frame(self.superCut)
self.stv = Treeview(self.SuperCut, show="headings")
self.stv.grid(row = 0, column = 0, padx = 5, pady = 5, sticky=NSEW)
self.scButtonBox.grid(row = 0, column = 1, padx = 5, pady = 5, sticky=NSEW)
self.stv.selectmode='extended'
self.stv['columns'] = ('Start', 'End', 'Text')
self.stv.column(0, width=10, anchor=E)
self.stv.column(1, width=10, anchor=E)
self.stv.column(2, anchor=W)
self.stv.heading('Start', text='Start', anchor=E)
self.stv.heading('End', text='End', anchor=E)
self.stv.heading('Text', text='Text', anchor=W)
#vertical scrollbar
self.ssbv = Scrollbar(self.SuperCut, orient=VERTICAL)
self.ssbv.grid(row = 0, column = 1, padx = 5, pady = 5, sticky=NS)
self.SuperCut.grid_columnconfigure(0, weight=1)
self.SuperCut.grid_rowconfigure(0, weight=1)
self.stv.config(yscrollcommand=self.ssbv.set)
self.ssbv.config(command=self.stv.yview)
#horizaontal scrollbar
self.ssbh = Scrollbar(self.SuperCut, orient=HORIZONTAL)
self.ssbh.grid(row = 1, column = 0, padx = 5, pady = 5, sticky=EW)
self.SuperCut.grid_columnconfigure(0, weight=1)
self.SuperCut.grid_rowconfigure(0, weight=1)
self.superCut.grid_columnconfigure(0, weight=1)
self.stv.config(xscrollcommand=self.ssbh.set)
self.ssbh.config(command=self.stv.xview)
# buttons
self.btnPlayCut = Button(self.scButtonBox, text = 'Play Selected Items', command = self.playClips)
self.btnPlayAll = Button(self.scButtonBox, text = 'Play SuperCut', command = self.playAll)
self.btnRemove = Button(self.scButtonBox, text = 'Remove Selected Items', command = self.removeItem)
self.btnClear = Button(self.scButtonBox, text = 'Clear Entire SuperCut', command = self.clearSuperCut)
self.btnSaveCut = Button(self.scButtonBox, text = 'Save SuperCut Audio File', command = self.btnSaveCutClick)
self.btnUp = Button(self.scButtonBox, text = 'Move Up', command = self.btnUpClick)
self.btnDown = Button(self.scButtonBox, text = 'Move Down', command = self.btnDownClick)
self.btnPlayCut.grid(row = 0, column = 0, padx = 5, pady = 5, sticky=EW)
self.btnPlayAll.grid(row = 1, column = 0, padx = 5, pady = 5, sticky=EW)
self.btnRemove.grid(row = 2, column = 0, padx = 5, pady = 5, sticky=EW)
self.btnClear.grid(row = 3, column = 0, padx = 5, pady = 5, sticky=EW)
self.btnSaveCut.grid(row = 4, column = 0, padx = 5, pady = 5, sticky=EW)
self.btnUp.grid(row = 5, column = 0, padx = 5, pady = 5, sticky=EW)
self.btnDown.grid(row = 6, column = 0, padx = 5, pady = 5, sticky=EW)
self.initUI()
def initUI(self):
self.setWindowTitle()
def setWindowTitle(self):
if self.cutFileName is None:
name = "New File"
else:
name = self.cutFileName
self.master.wm_title("Super Cut - {}".format(name))
def exitProgram(self):
exit()
def clearUI(self):
self.jsonFileName.set("")
self.audioFileName.set("")
self.clearTranscript()
self.clearSuperCut()
def newCutFile(self):
'if file already open see if they want to save it'
if not self.cutFileName is None:
self.saveAsCutFile()
'initialize UI for new cut file'
self.clearUI()
self.cutFileName = None
self.setWindowTitle()
def openCutFile(self):
'open a .cut file and load the UI widgets'
try:
self.newCutFile()
'open cut file'
name= fd.askopenfilename(filetypes =[('SuperCut File', '*.cut')])
if len(name) > 0:
self.cutFileName = name
self.loadCutFile()
self.logMsg("SuperCut file {} successfully opened.".format(self.cutFileName))
except Exception as e:
self.logMsg(e)
self.logMsg("Error opening SuperCut file.")
finally:
self.setWindowTitle()
def saveCutFile(self):
'save a .cut file'
if self.cutFileName is None:
self.saveAsCutFile()
else:
self.writeCutFile()
return
def saveAsCutFile(self):
'save as a .cut file'
try:
'open cut file'
name= fd.asksaveasfilename(filetypes =[('SuperCut File', '*.cut')], defaultextension=".cut")
if len(name) > 0:
self.cutFileName = name
self.writeCutFile()
self.setWindowTitle()
except Exception as e:
self.logMsg(e)
self.logMsg("Error Save As SuperCut file.")
def writeCutFile(self):
'load ui widgets into a dictionary and write json file'
self.superCutDict = {}
self.superCutDict["jsonFileName"] = self.jsonFileName.get()
self.superCutDict["audioFileName"] = self.audioFileName.get()
try:
# write results to a file
with open(self.cutFileName, 'w') as output:
print(json.dumps(self.superCutDict, indent=4), file=output)
self.logMsg("SuperCut file {} successfully saved.".format(self.cutFileName))
except Exception as e:
self.logMsg("Error writing SuperCut file. {}".format(e))
def loadCutFile(self):
'read json cut file and load ui widgets'
try:
# Open SuperCut JSON file
f = open(self.cutFileName)
self.superCutDict = json.load(f)
self.jsonFileName.set(self.superCutDict["jsonFileName"])
self.readJsonFile()
self.audioFileName.set(self.superCutDict["audioFileName"])
self.readAudioFile()
self.logMsg("SuperCut file successfully read.")
except Exception as e:
self.logMsg("Error reading JSON transcript file. {}".format(e))
finally:
f.close()
def openAudioFile(self):
'Select audio file'
try:
name= fd.askopenfilename(filetypes =[('audio file', '*.wav *.mp3')])
self.audioFileName.set(name)
self.readAudioFile()
self.logMsg("Audio file successfully selected.")
except Exception as e:
self.logMsg("Error selecting Audio file.{}".format(e))
def readAudioFile(self):
'read the audio file so it can be cut apart'
try:
f = self.audioFileName.get()
self.sound = AudioSegment.from_file(file=f, format="wav")
self.logMsg("Audio file successfully read.")
except Exception as e:
self.logMsg("Error reading Audio file.{}".format(e))
def openJsonFile(self):
'select transcript json file'
try:
name= fd.askopenfilename(filetypes =[('json file', '*.json')])
self.jsonFileName.set(name)
self.readJsonFile()
self.logMsg("JSON file successfully selected.")
except Exception as e:
self.logMsg(e)
self.logMsg("Error selecting JSON transcript file.")
def readJsonFile(self):
'Read transcript json file'
try:
# Open JSON file
f = open(self.jsonFileName.get())
self.transcriptionData = json.load(f)
self.logMsg("Transcript JSON file successfully read.")
except Exception as e:
self.logMsg("Error reading Transcript JSON transcript file. {}".format(e))
finally:
f.close()
self.fillTv()
def btnSaveCutClick(self):
'''
Public method - save the super cut as an audio file
'''
try:
self.genSuperCut()
if not self.superCut is None:
name= fd.asksaveasfilename(filetypes =[('audio file', '*.wav' )], title="Save SuperCut Audio File")
if name:
splitFile = os.path.splitext(name)
audioExt = splitFile[1]
# Save audio file
self.superCut.export(out_f = name, format = audioExt)
self.logMsg("SuperCut audio file successfully saved.")
except Exception as e:
self.logMsg(e)
self.logMsg("Error saving audio file.")
def logMsg(self, msg):
ct = datetime.datetime.now()
print("{}:{}".format(str(ct), msg))
# self.txtLog.insert(END, str(ct) + ": " + msg + "\n")
# self.txtLog.see(END)
# Tk.update_idletasks(self)iid
def clearLog(self):
return
def fillTv(self):
if self.transcriptionData is None:
messagebox.showinfo("Clip Generator", "No json file selected. File open a json text transcript file.")
return
nextIndex = 0
for resultDict in self.transcriptionData:
# add the top level "sentence"
textIid = self.tv.insert(parent='', index=nextIndex, text=(resultDict.get("text", "no text")), open=False)
nextIndex = nextIndex + 1
# start, end, and selected tags stored with each word
self.tv.item(textIid, tags=[resultDict.get("start", "0"), resultDict.get("end", "0"), 'NO', 'Text'])
# add each word under the parent sentence
childIndex = 0
for word in resultDict.get("result", ""):
wordId = self.tv.insert(parent=textIid, index=childIndex, text=(word.get("word", "no word")), open=False)
childIndex = childIndex + 1
# start, end, and selected tags stored with each word
self.tv.item(wordId, tags=[word.get("start", "0"), word.get("end", "0"), 'NO', 'Word'])
def nextFind(self):
nextOne = False
foundOne = False
'scroll to next found item'
# scan transcript
for index in self.tv.get_children():
taglist = self.tv.item(index)['tags']
if taglist[FOUND] == 'FOUND':
if self.lastFound == None:
# 'scroll to first found'
self.tv.see(index)
self.lastFound = index
foundOne = True
elif index == self.lastFound:
nextOne = True
elif nextOne == True:
# 'scroll to this one'
self.tv.see(index)
self.lastFound = index
foundOne = True
break
if foundOne == False:
self.lastFound = None
def searchTranscript(self):
# check transcript file exists
if self.jsonFileName.get() == "":
messagebox.showinfo("Clip Generator", "No json file selected. File open a json text transcript file.")
return
# check for search term exists
searchWord = self.searchTerm.get()
if searchWord is None or len(searchWord) < 1:
messagebox.showinfo("Clip Generator", "No search term entered. Enter a word to search for.")
return
# scan transcript
for index in self.tv.get_children():
# self.tv.item(index).open = False
# clear tag if needed
taglist = self.tv.item(index)['tags']
taglist[FOUND] = 'NO'
if searchWord in self.tv.item(index)["text"]:
taglist[FOUND] = 'FOUND'
self.tv.item(index, tags=taglist)
self.lastFound = None
self.nextFind()
def addItem(self):
'''
Public method addItem
add the items selected in the transcript treeview to the supercut grid
'''
for index in self.tv.selection():
try:
curTags = self.tv.item(index)["tags"]
# is this a word or a sentence?
if curTags[TYPE] == "Word":
# get previous and next words start/end times
prevIndex = self.tv.prev(index)
nextIndex = self.tv.next(index)
if not prevIndex == "":
prevTags = self.tv.item(prevIndex)["tags"]
prevEnd = float(prevTags[END])
else:
prevEnd = 0.0
if not nextIndex == "":
nextTags = self.tv.item(nextIndex)["tags"]
nextStart = float(nextTags[START])
else:
nextStart = 0.0
# calculate the gap between words and add half of it to the start and end of the word to prevent clipping
start = float(curTags[START])
end = float(curTags[END])
if prevEnd > 0 and prevEnd < start:
start = start - ((start - prevEnd)/2)
if nextStart > 0 and nextStart > end:
end = end + ((nextStart - end)/2)
elif curTags[TYPE] == "Text":
words = self.tv.get_children(item=index)
if len(words) > 0:
startIndex = words[0]
startTags = self.tv.item(startIndex)["tags"]
start = float(startTags[START])
endIndex = words[len(words)-1]
endTags = self.tv.item(endIndex)["tags"]
end = float(endTags[END])
else:
start = 0.0
end = 0.0
else:
self.logMsg("invalid line in transcript")
return
# add selected item to super cut grid
self.stv.insert('', 'end', text='', values=(str(start), str(end), self.tv.item(index)["text"]))
except Exception as e:
self.logMsg(e)
return
def removeItem(self):
try:
for index in self.stv.selection():
self.stv.delete(index)
except Exception as e:
self.logMsg(e)
def btnUpClick(self):
'''
Public method - move selected item in supercut up one
'''
try:
for index in self.stv.selection():
prevIndex = self.stv.prev(index)
self.stv.move(index, "", self.stv.index(prevIndex))
except Exception as e:
self.logMsg(e)
return
def btnDownClick(self):
'''
Public method - move selected item in supercut down one
'''
try:
for index in self.stv.selection():
nextIndex = self.stv.next(index)
self.stv.move(index, "", self.stv.index(nextIndex))
except Exception as e:
self.logMsg(e)
def clearSuperCut(self):
'''
Public method - delete all entries in the supercut grid
'''
for item in self.stv.get_children():
self.stv.delete(item)
def clearTranscript(self):
'''
Public method - delete all entries in the transcript grid
'''
for item in self.tv.get_children():
self.tv.delete(item)
def playClips(self):
'''
this method plays the selected audio clips in the supercut grid.
'''
self.logMsg("Start Play Text")
if self.jsonFileName.get() == "":
messagebox.showinfo("Clip Generator", "No json file selected. File open a json text transcript file.")
return
if self.sound is None:
messagebox.showinfo("Clip Generator", "No audio file selected. File open an audio file.")
return
# Create an empty AudioSegment
self.superCut = AudioSegment.silent(duration=0)
try:
# loop through selected words/text and build up the super cut
for item in self.stv.selection():
start = float(self.stv.item(item)["values"][0])
end = float(self.stv.item(item)["values"][1])
cut = self.sound[start * 1000:end * 1000]
self.superCut = self.superCut+cut
play(self.superCut)
except Exception as e:
self.logMsg(e)
self.logMsg("End Play Text")
def playAll(self):
'''
this method plays the entire supercut.
'''
self.logMsg("Start Play SuperCut")
self.genSuperCut()
if not self.superCut is None:
play(self.superCut)
self.logMsg("End Play SuperCut")
def genSuperCut(self):
'''
Public method - generate the supercut audio segment and save it in self.superCut
'''
self.superCut = None
if self.jsonFileName.get() == "":
messagebox.showinfo("Clip Generator", "No json file selected. File open a json text transcript file.")
return
if self.sound is None:
messagebox.showinfo("Clip Generator", "No audio file selected. File open an audio file.")
return
try:
# Create an empty AudioSegment
self.superCut = AudioSegment.silent(duration=0)
# loop through entire grid and build up the super cut
for item in self.stv.get_children():
start = float(self.stv.item(item)["values"][0])
end = float(self.stv.item(item)["values"][1])
cut = self.sound[start * 1000:end * 1000]
self.superCut = self.superCut+cut
except Exception as e:
self.logMsg(e)
return
root = Tk()
app = Window(root)
root.geometry("1200x700")
#root.resizable(0, 0)
root.mainloop()
And Finally…
I hope you find the AudioText and ClipGenerator applications useful. Please leave a comment below if you have issues or suggestions for improvements.
My next goal is to extract semantic meaning from the transcript file to more easily identify content meaning in the podcast. Stay tuned for more work in this area.