Skip to content

Instantly share code, notes, and snippets.

@SuperDoxin
Last active August 29, 2015 14:02
Show Gist options
  • Select an option

  • Save SuperDoxin/8f6b9bb63da0d9b38064 to your computer and use it in GitHub Desktop.

Select an option

Save SuperDoxin/8f6b9bb63da0d9b38064 to your computer and use it in GitHub Desktop.
quick and dirty x/y osciloscope for playing audio files such as https://www.youtube.com/watch?v=o4YyI6_y6kw
"""
Usage:
scope.py <input_file>
"""
from __future__ import division
import pygame
import audiotools
from docopt import docopt
import itertools
import time
import math
import subprocess
def frameiterator(pcm):
while True:
lst=pcm.read(pcm.sample_rate).to_float()
for frame in lst:
yield frame
FPS=60
PERSISTENCE=0.8
COLOR=(50,160,200)
BGCOLOR=(COLOR[0]*0.5,COLOR[1]*0.5,COLOR[2]*0.5)
framedelay=0
if __name__=="__main__":
opt=docopt(__doc__)
print "loading file"
afid=audiotools.open(opt["<input_file>"])
if afid.channels()!=2:
print "expected 2 channels, got",afid.channels()
print afid.sample_rate()/FPS,"samples per frame"
samplesperframe=int(afid.sample_rate()/FPS)
pcm=afid.to_pcm()
it=frameiterator(pcm)
print "creating window"
pygame.init()
W,H=480,480
HW,HH=W//2,H//2
maxdis=500
screen=pygame.display.set_mode((W,H))
buff=pygame.Surface((W,H))
buff2=pygame.Surface((W,H))
vignette=pygame.image.load("vignette.bmp").convert()
screen.fill((0,0,0))
buff2.fill((0,0,0))
pygame.display.flip()
clock=pygame.time.Clock()
px,py=None,None
print "starting audio"
audio=subprocess.Popen(["mplayer","-slave","-quiet","-input","nodefault-bindings","-noconfig","all",opt["<input_file>"]],stdout=subprocess.PIPE,stdin=subprocess.PIPE)
audio.stdin.write("pause\n")
time.sleep(0.1)#Starting playback...
buf=""
while True:
buf+=audio.stdout.read(1)
print buf
if buf.endswith("Starting playback...\n"):
break
print buf
print "unpausing audio"
audio.stdin.write("pause\n")
print "starting visual"
time_index=0
frame_index=0
while True:
pygame.event.pump()
buff2.fill((PERSISTENCE*255,PERSISTENCE*255,PERSISTENCE*255),special_flags=pygame.BLEND_MULT)
#screen.fill((250,250,250),special_flags=pygame.BLEND_MULT)
buff.lock()
buff.fill((0,0,0))
for i in range(samplesperframe):
x=int(it.next()*HW+HW)
y=int(it.next()*HH+HH)
if px!=None:
dis=(x-px)**2+(y-py)**2
if dis>maxdis:
a=0
elif dis==0:
a=1
else:
a=(maxdis-dis)/maxdis
if a<0:
a=0
if a>0:
w=int(5.5*a)
if w<1:
w=1
pygame.draw.line(buff,(int(COLOR[0]*a),int(COLOR[1]*a),int(COLOR[2]*a)),(x,y),(px,py),w)#special_flags=pygame.BLEND_ADD
px,py=x,y
buff.unlock()
buff2.blit(buff,(0,0),special_flags=pygame.BLEND_ADD)
screen.fill(COLOR)
for y in range(H//10,H,H//10):
pygame.draw.line(screen,BGCOLOR,(0,y),(W,y))
for y in range(H//60,H,H//60):
pygame.draw.line(screen,BGCOLOR,(HW-5,y),(HW+5,y))
for x in range(W//10,W,W//10):
pygame.draw.line(screen,BGCOLOR,(x,0),(x,H))
for x in range(W//60,W,W//60):
pygame.draw.line(screen,BGCOLOR,(x,HH-5),(x,HH+5))
screen.blit(buff2,(0,0),special_flags=pygame.BLEND_ADD)
screen.blit(vignette,(0,0),special_flags=pygame.BLEND_MULT)
pygame.display.flip()
clock.tick(FPS+1)#+1 to make it slowly catch up with audio if its behind
time.sleep(framedelay)
clock.tick()
time_index+=1/FPS
frame_index+=1
if frame_index%FPS==FPS-1:
#time_pos
#print "getting skew"
audio.stdin.write("get_time_pos\n")
buf=""
while len(buf)==0 or buf[-1]!="\n":
buf+=audio.stdout.read(1)
buf=buf.strip()
#print buf
rtime=float(buf.partition('=')[-1])
diff=time_index-rtime
#print "adjusting clock skew of",diff
if diff>0:
framedelay=diff/FPS
else:
framedelay=0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment