How To Extract Sound Clips From A Podcast

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.

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.

SuperCut Screenshot
SuperCut Screenshot

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.

Total
0
Shares
Previous Post

How To Transcribe A Podcast Audio File To Text For Free

Next Post

How Convert A Live Stream Podcast To Text

Related Posts