Skip to content

Instantly share code, notes, and snippets.

@Headline
Last active December 15, 2019 04:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Headline/3b968f1ff0c53396b0ba7c733167a74a to your computer and use it in GitHub Desktop.
Save Headline/3b968f1ff0c53396b0ba7c733167a74a to your computer and use it in GitHub Desktop.
A python script which allows you to repeat the last sample of audio repeatedly while holding down the button
# Audio Glitch
# Michael "headline" Flaherty - michaelwflaherty@me.com - 12/14/2019
#
# This program 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.
#
# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
# Installation
# 1) Install python 3
# 2) run "pip install pyaudio"
# 3) Once pyaudio is installed run the script
# 4) rejoice
#
# If ya'll struggle too much email me and ask for an *.exe installation
import pyaudio
import _thread
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
class AudioHandler:
def __init__(self, CHUNK):
self.CHUNK = CHUNK
self.p = pyaudio.PyAudio()
self.inputdevice = -1
self.outputdevice = -1
self.monitordevice = -1
self.instream = None
self.outstream = None
self.monstream = None
def lockDevices(self):
if self.inputdevice == -1 or self.outputdevice == -1:
raise Exception('Input or Output device has not been set.')
if self.instream != None or self.outstream != None:
raise Exception('Input or Output stream already exists.')
self.instream = self.p.open(format=self.p.get_format_from_width(4), channels=1, rate=44100, input=True, input_device_index=self.inputdevice)
self.outstream = self.p.open(format=self.p.get_format_from_width(4), channels=1, rate=44100, output=True, output_device_index=self.outputdevice)
if self.monitordevice != -1:
self.monstream = self.p.open(format=self.p.get_format_from_width(4), channels=1, rate=44100, output=True, output_device_index=self.monitordevice)
def getInputDevices(self):
indexes = []
devicenames = []
info = self.p.get_host_api_info_by_index(0)
numdevices = info.get('deviceCount')
for i in range(0, numdevices):
if (self.p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0:
indexes.append(i)
devicenames.append(self.p.get_device_info_by_host_api_device_index(0, i).get('name'))
return [indexes, devicenames]
def getOutputDevices(self):
indexes = []
devicenames = []
info = self.p.get_host_api_info_by_index(0)
numdevices = info.get('deviceCount')
for i in range(0, numdevices):
if (self.p.get_device_info_by_host_api_device_index(0, i).get('maxOutputChannels')) > 0:
indexes.append(i)
devicenames.append(self.p.get_device_info_by_host_api_device_index(0, i).get('name'))
return [indexes, devicenames]
def doOverwrite(self, run, data):
while data != '' and len(run) > 0:
self.outstream.write(data)
if self.monitordevice != -1:
self.monstream.write(data)
ah = AudioHandler(2048)
window = Tk()
window.pack_propagate(0)
window.resizable(0, 0)
window.geometry("450x175")
window.title("Audio Glitch v1.1")
label = Label(window, text = "Select microphone input", font=("Consolas", 12))
label.grid(column=0, row=0)
ideviceinfo = ah.getInputDevices()
input = Combobox(window, width=35, state='readonly')
input['values'] = ideviceinfo[1]
input.current(0)
input.grid(column=0, row=2)
label = Label(window, text = "Select virtual audio cable", font=("Consolas", 12))
label.grid(column=0, row=4)
odeviceinfo = ah.getOutputDevices()
output = Combobox(window, width=35, state='readonly')
output['values'] = odeviceinfo[1]
output.current(0)
output.grid(column=0, row=6)
label = Label(window, text = "Select monitoring output", font=("Consolas", 12))
label.grid(column=0, row=7)
mondeviceinfo = ah.getOutputDevices()
mondeviceinfo[1].insert(0, 'None')
mondeviceinfo[0].insert(0, -1)
monitor = Combobox(window, width=35, state='readonly')
monitor['values'] = mondeviceinfo[1]
monitor.current(0)
monitor.grid(column=0, row=8)
Label(window, text='Buffer size: ').grid(column=1, row=0)
buffersize = Entry(window)
buffersize.grid(column=2, row=0)
buffersize.insert(0, '2048')
run = []
def audioloop(run):
data = ah.instream.read(ah.CHUNK)
while data != '':
if len(run) > 0:
ah.doOverwrite(run, data)
else:
ah.outstream.write(data)
data = ah.instream.read(ah.CHUNK)
def lock():
try:
ah.CHUNK = int(buffersize.get())
if ah.CHUNK < 1024:
raise BufferError();
if output.get() == monitor.get():
raise NameError();
except ValueError:
messagebox.showerror("Parsing Error", "Buffer size must be integer value!")
return;
except BufferError:
messagebox.showerror("Buffer Error", "Buffer size must be at least 1024!")
return;
except NameError:
messagebox.showerror("Name Error", "Monitoring output must not be the same as audio cable output!")
return;
ah.inputdevice = ideviceinfo[0][ideviceinfo[1].index(input.get())]
ah.outputdevice = odeviceinfo[0][odeviceinfo[1].index(output.get())]
if monitor.get() != 'None':
ah.monitordevice = mondeviceinfo[0][mondeviceinfo[1].index(monitor.get())]
ah.lockDevices()
#disable UI elements
input.config(state='disabled')
output.config(state='disabled')
monitor.config(state='disabled')
button.config(state='disabled')
buffersize.config(state='disabled')
glitch.config(state='normal')
try:
_thread.start_new_thread(audioloop, (run, ))
except:
print("Unable to start audio thread")
button = Button(window, text='Lock audio devices', command=lock)
button.grid(column=2, row = 4, ipady=3, ipadx=3)
def glitch_on(event):
global run
run.append(1)
def glitch_off(event):
global run
run.remove(1)
glitch = Button(window, text="Hold to glitch")
glitch.bind('<ButtonPress-1>', glitch_on)
glitch.bind('<ButtonRelease-1>', glitch_off)
glitch.grid(column=2, row = 8, ipady=10, ipadx=10)
glitch.config(state='disabled')
window.mainloop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment