Last active
August 29, 2015 14:02
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| 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