class App(object): def __init__(self): ''' Returns an app object ''' pygame.display.set_caption("SoundScape") self.screen = pygame.display.set_mode((800, 600),1) self.paintScreen = self.screen.subsurface(pygame.Rect(100, 0, 600, 500)) self.paletteScreen = self.screen.subsurface(pygame.Rect(0, 0, 100, 300)) self.screen.fill((183,183,183)) self.paintScreen.fill((255,255,255)) self.paletteScreen.blit(pygame.image.load("data/img/palette.png"), (0,0)) self.paintRect = self.paintScreen.get_rect() self.paletteRect = self.paletteScreen.get_rect() self.brush = PaintBrush(self.paintScreen) self.painting = False self.undoCache = [] pygame.mixer.init(buffer = 256) self.playButton = Button("data/img/play_button.png", (350, 500)) self.playButton.draw(self.screen) self.stopButton = Button("data/img/stop_button.png", (460, 515)) self.stopButton.draw(self.screen) self.chordButton = Button("data/img/chord_button.png", (265, 515)) self.chordButton.draw(self.screen) self.demo1Button = Button("data/img/demo_1_button.png", (700, 0)) self.demo1Button.draw(self.screen) self.demo2Button = Button("data/img/demo_2_button.png", (750, 0)) self.demo2Button.draw(self.screen) self.demo3Button = Button("data/img/demo_3_button.png", (700, 50)) self.demo3Button.draw(self.screen) self.demo1 = pygame.image.load('data/img/demo_1.png') self.demo2 = pygame.image.load('data/img/demo_2.png') self.demo3 = pygame.image.load('data/img/demo_3.png') self.help = pygame.image.load('data/img/help.png') self.scanGroup = pygame.sprite.Group() colorArray = [(254,0,0), (254,63,0), (254,127,0), (254, 214, 0), (254,254,0), (127,254,0), (0,254,0), (13,152,185), (0,0,254),(75,0,130), (143,0,254), (198, 21, 133)] # red, red-orange, orange, yellow-orange, yellow, yellow-green, green, # blue-green,blue, blue-violet, violet, red-violet noteArray = [pygame.mixer.Sound("data/C.ogg"), pygame.mixer.Sound("data/C#.ogg"), pygame.mixer.Sound("data/D.ogg"), pygame.mixer.Sound("data/D#.ogg"), pygame.mixer.Sound("data/E.ogg"), pygame.mixer.Sound("data/F.ogg"), pygame.mixer.Sound("data/F#.ogg"), pygame.mixer.Sound("data/G.ogg"), pygame.mixer.Sound("data/G#.ogg"), pygame.mixer.Sound("data/A.ogg"), pygame.mixer.Sound("data/A#.ogg"), pygame.mixer.Sound("data/B.ogg")] #set the volumes at .3 to avoid gain for sound in noteArray: sound.set_volume(.3) #create a dict of notes with each notes corresponding color as its key self.noteDict = dict() for i in xrange(len(colorArray)): self.noteDict[colorArray[i]] = noteArray[i] self.playback = False self.canvasChordArray = [] self.brush.set_brush(pygame.image.load("data/img/Brush.png")) self.brush.set_follow_angle(True) self.color = (254,0,0) self.brush.set_color(self.color) self.sound = self.noteDict[self.color] def save_paper(self): ''' Make a copy of the screen and add it to the undoCache ''' self.undoCache.append(self.paintScreen.copy()) def undo_paper(self): ''' Blits the previous saved state to the screen ''' #Don't undo if there's nothing to undo if (len(self.undoCache) >= 1 and len(self.canvasChordArray) >= 1): p = self.undoCache.pop() self.canvasChordArray.pop() self.paintScreen.blit(p,(0,0)) def paint_start(self): ''' Initiates pinting and saves the state of the canvas before painting begins ''' self.painting = True self.save_paper() def paint_stop(self): ''' Ends the painting state ''' self.painting = False def scan_col(self, surface,col): ''' Scan and play the colors in a given column ''' newChordDict = dict() #get the color of each pixel in the given row for row in xrange(self.paintRect.height): x = col y = row color = self.paintScreen.get_at((x, y))[:3] #if the color has a note assigned to it and has not already been #seen add it to the newChordDict with the color as the key if ((color in self.noteDict) and (color not in newChordDict)): newChordDict[color] = (self.noteDict[color]) for note in self.noteDict.iterkeys(): #if the note was playing but is no longer present stop playing it if ((note in self.chordDict) and (note not in newChordDict)): self.noteDict[note].stop() #if the note was not playing but is now present, begin playing it. if ((note in newChordDict) and (note not in self.chordDict)): self.noteDict[note].play() self.chordDict = newChordDict #sleep for 1 ms to get a more uniform scan speed time.sleep(.01) def mouse_event_handler(self, event): ''' Handles mouse events ''' if event.type == MOUSEBUTTONDOWN: self.mouse_button_down_handler(event) elif event.type == MOUSEMOTION: self.mouse_motion_handler(event) elif event.type == MOUSEBUTTONUP: self.mouse_button_up_handler(event) else: return def mouse_button_down_handler(self, event): ''' Handles mousebuttondown events ''' if event.button == 1: #if the play button is pressed begin scanning if (self.playButton.button_pressed(event.pos, 0, 0)): if (self.playback == False): self.chordDict = dict() self.scannerLeft = 0 self.playback = True self.scanSprite = Scanner(self.paintScreen) self.scanGroup.add(self.scanSprite) self.canvas = self.paintScreen.copy() if (self.stopButton.button_pressed(event.pos, 0, 0)): if (self.playback == False): return else: #clear scanSprites from the screen and stop playing notes self.playback = False self.scanGroup.clear(self.paintScreen, self.canvas) self.scanGroup.empty() for note in self.noteDict.itervalues(): note.stop() #play all colors currently present on screen if (self.chordButton.button_pressed(event.pos, 0, 0)): #get all unique elements in canvasChordArray canvasChordSet = set(self.canvasChordArray) #play all notes in canvasChordSet for sound in canvasChordSet: sound.play(0, 1000, 0) #load demo1 or demo2 to the screen with their correct respective #canvasChordArrays if (self.demo1Button.button_pressed(event.pos, 0, 0)): self.paintScreen.blit(self.demo1, (0,0)) self.canvasChordArray = [pygame.mixer.Sound("data/C.ogg"), pygame.mixer.Sound("data/E.ogg"), pygame.mixer.Sound("data/D#.ogg"), pygame.mixer.Sound("data/G.ogg")] self.save_paper() if (self.demo2Button.button_pressed(event.pos, 0, 0)): self.paintScreen.blit(self.demo2, (0,0)) self.canvasChordArray = [pygame.mixer.Sound("data/E.ogg"), pygame.mixer.Sound("data/G#.ogg"), pygame.mixer.Sound("data/B.ogg"), pygame.mixer.Sound("data/D#.ogg"), pygame.mixer.Sound("data/F#.ogg"), pygame.mixer.Sound("data/C#.ogg"), pygame.mixer.Sound("data/A.ogg")] if (self.demo3Button.button_pressed(event.pos, 0, 0)): self.paintScreen.blit(self.demo3, (0,0)) self.canvasChordArray = [pygame.mixer.Sound("data/C.ogg"), pygame.mixer.Sound("data/E.ogg"), pygame.mixer.Sound("data/D#.ogg"), pygame.mixer.Sound("data/G.ogg")] self.save_paper() #if the user clicks on the palette, set the brush color to the #color under the mouse if (self.paletteRect.collidepoint(event.pos)): self.color = self.paletteScreen.get_at(event.pos)[:3] self.sound = self.noteDict[self.color] self.sound.play(0, 500, 0) self.brush.set_color(self.color) #get the position of the mouse with respect to the canvas brushPos = (event.pos[0]-self.paintScreen.get_abs_offset()[0], event.pos[1]-self.paintScreen.get_abs_offset()[1]) #if the mouse is over the canvas begin painting if self.paintRect.collidepoint(brushPos): self.brush.paint_from(brushPos) self.paint_start() lineFrom = event.pos def mouse_motion_handler(self, event): ''' Handles mousemotion events ''' brushPos = (event.pos[0]-self.paintScreen.get_abs_offset()[0], event.pos[1]-self.paintScreen.get_abs_offset()[1]) #if the mouse is in the canvas and there's no scanning going on #continue painting if event.buttons[0]: if (self.painting and (self.playback == False)): if (self.paintRect.collidepoint(brushPos)): self.brush.paint_to(brushPos) #set the volume at .05 to prevent gain #self.sound.set_volume(.05) self.sound.play() else: self.sound.stop() def mouse_button_up_handler(self, event): ''' Handles mousebuttonup events ''' #if there is a mousebuttonup event in the canvas #stop painting, stop playing the current note #and add the note to the canvasChordArray if (event.button == 1 and self.painting and self.playback == False): self.paint_stop() self.sound.stop() self.canvasChordArray.append(self.sound) def main_loop(self): clock = pygame.time.Clock() lineFrom = None lineTo = None showHelp = True nextUpdate = pygame.time.get_ticks() while True: if (self.playback == True): #clear the screen of scanSprites self.scanGroup.clear(self.paintScreen, self.canvas) self.scanGroup.update() self.scanGroup.draw(self.paintScreen) #scan the column at scannerLeft self.scan_col(self.paintScreen, self.scannerLeft) self.scannerLeft += 1 #When the scanner reaches the edge of the canvas #stop scanning and stop playing all of the notes if (self.scannerLeft == self.paintRect.width): self.playback = False for note in self.noteDict.itervalues(): note.stop() for event in pygame.event.get(): #if showHelp is true don't handle any event if showHelp: if event.type == QUIT: return elif event.type == KEYDOWN: if event.key == K_ESCAPE: return else: showHelp = False self.paintScreen.fill((255,255,255)) else: if event.type == QUIT: pygame.quit() return elif event.type == KEYDOWN: if event.key == K_ESCAPE: pygame.quit() return elif event.key == K_SPACE: #fill the screen with white #and empty the undoCache self.undoCache = [] self.paint_start() self.paintScreen.fill((255,255,255)) self.canvasChordArray = [] self.paint_stop() elif event.key == K_z: self.undo_paper() elif event.key == K_h: showHelp = True else: self.mouse_event_handler(event) if pygame.time.get_ticks() >= nextUpdate: nextUpdate+=33 self.screen = self.screen.copy() self.screen.blit(self.paintScreen,self.paintRect.topleft) if not showHelp: if lineFrom and lineTo: pygame.draw.line(self.screen,(0,0,0),lineFrom,lineTo) #if showHelp is true blit the help screen to the canvas if showHelp: self.paintScreen.blit(self.help,(0,0)) pygame.display.flip()