from os import makedirs
from os.path import exists, join
from tempfile import TemporaryFile
from time import strftime

from pygame.image import tostring, frombuffer, save
from pygame.time import get_ticks

from GameChild import GameChild

class VideoRecorder(GameChild):

    def __init__(self, parent):
        GameChild.__init__(self, parent)
        self.set_requested()
        if self.requested:
            self.display_surface = self.get_display_surface()
            self.delegate = self.get_delegate()
            self.load_configuration()
            self.reset()
            self.subscribe(self.respond)

    def set_requested(self):
        self.requested = self.get_configuration("video-recordings")["enable"] \
                         or self.check_command_line("-enable-video")

    def load_configuration(self):
        config = self.get_configuration("video-recordings")
        self.root = config["path"]
        self.directory_name_format = config["directory-name-format"]
        self.file_extension = config["file-extension"]
        self.frame_format = config["frame-format"]
        self.framerate = config["framerate"]

    def reset(self):
        self.recording = False
        self.frame_length = None
        self.frames = None
        self.last_frame = 0

    def respond(self, event):
        compare = self.delegate.compare
        if compare(event, "record-video"):
            self.toggle_record()
        elif compare(event, "reset-game"):
            self.reset()

    def toggle_record(self):
        recording = not self.recording
        if recording:
            self.frame_length = len(self.get_string())
            self.frames = TemporaryFile()
        else:
            self.write_frames()
        self.recording = recording

    def get_string(self):
        return tostring(self.display_surface, self.frame_format)

    def write_frames(self):
        root = join(self.root, strftime(self.directory_name_format))
        if not exists(root):
            makedirs(root)
        size = self.display_surface.get_size()
        frames = self.frames
        frames.seek(0)
        for ii, frame in enumerate(iter(lambda: frames.read(self.frame_length),
                                        "")):
            path = join(root, "%04i.png" % ii)
            save(frombuffer(frame, size, self.frame_format), path)
        print "wrote video frames to " + root

    def update(self):
        ticks = get_ticks()
        if self.recording and ticks - self.last_frame >= self.framerate:
            self.frames.write(self.get_string())
            self.last_frame = ticks
from pygame.time import get_ticks

from GameChild import GameChild

class TimeFilter(GameChild):

    def __init__(self, parent):
        GameChild.__init__(self, parent)
        self.reset_ticks()
        self.open()

    def reset_ticks(self):
        self.ticks = self.unfiltered_ticks = self.last_ticks = get_ticks()

    def close(self):
        self.closed = True

    def open(self):
        self.closed = False

    def get_ticks(self):
        return self.ticks

    def get_unfiltered_ticks(self):
        return self.unfiltered_ticks

    def get_last_ticks(self):
        return self.last_ticks

    def get_last_frame_duration(self):
        return self.last_frame_duration

    def update(self):
        ticks = get_ticks()
        self.last_frame_duration = duration = ticks - self.last_ticks
        if not self.closed:
            self.ticks += duration
        self.unfiltered_ticks += duration
        self.last_ticks = ticks
class Vector(list):

    def __init__(self, x=0, y=0):
        list.__init__(self, (x, y))

    def __getattr__(self, name):
        if name == "x":
            return self[0]
        elif name == "y":
            return self[1]

    def __setattr__(self, name, value):
        if name == "x":
            self[0] = value
        elif name == "y":
            self[1] = value
        else:
            list.__setattr__(self, name, value)

    def __add__(self, other):
        return Vector(self.x + other[0], self.y + other[1])

    __radd__ = __add__

    def __iadd__(self, other):
        self.x += other[0]
        self.y += other[1]
        return self

    def __sub__(self, other):
        return Vector(self.x - other[0], self.y - other[1])

    def __rsub__(self, other):
        return Vector(other[0] - self.x, other[1] - self.y)

    def __isub__(self, other):
        self.x -= other[0]
        self.y -= other[1]
        return self

    def __mul__(self, other):
        return Vector(self.x * other, self.y * other)

    __rmul__ = __mul__

    def __imul__(self, other):
        self.x *= other
        self.y *= other
        return self

    def apply_to_components(self, function):
        self.x = function(self.x)
        self.y = function(self.y)

    def place(self, x=None, y=None):
        if x is not None:
            self.x = x
        if y is not None:
            self.y = y

    def move(self, dx=0, dy=0):
        if dx:
            self.x += dx
        if dy:
            self.y += dy

    def place_at_origin(self):
        self.x = 0
        self.y = 0
from os import listdir
from os.path import isfile, join
from sys import exc_info, stdout
from glob import glob

from pygame import Color, Rect, Surface, PixelArray
from pygame.image import load
from pygame.transform import flip
from pygame.locals import *

from Animation import Animation
from Vector import Vector
from extension import get_hue_shifted_surface

class Sprite(Animation):

    def __init__(self, parent, framerate=None):
        Animation.__init__(self, parent, self.shift_frame, framerate)
        self.frames = []
        self.mirrored = False
        self.alpha = 255
        self.locations = []
        self.framesets = [Frameset(self, framerate=framerate)]
        self.frameset_index = 0
        self.set_frameset(0)
        self.locations.append(Location(self))
        self.motion_overflow = Vector()
        self.stop()
        self.display_surface = self.get_display_surface()

    def __getattr__(self, name):
        if name in ("location", "rect"):
            return self.locations[0]
        if hasattr(Animation, "__getattr__"):
            return Animation.__getattr__(self, name)
        raise AttributeError, name

    def set_frameset(self, identifier):
        if isinstance(identifier, str):
            for ii, frameset in enumerate(self.framesets):
                if frameset.name == identifier:
                    identifier = ii
                    break
        if self.frameset_index != identifier:
            self.frameset_index = identifier
            self.register_interval()
            self.update_location_size()
            if self.get_current_frameset().length() > 1:
                self.play()
            self.get_current_frameset().reset()

    def register_interval(self):
        self.register(self.shift_frame,
                      interval=self.get_current_frameset().framerate)

    def get_current_frameset(self):
        return self.framesets[self.frameset_index]

    def update_location_size(self):
        size = self.get_current_frameset().rect.size
        for location in self.locations:
            location.size = size
            location.fader.init_surface()

    def set_framerate(self, framerate):
        self.get_current_frameset().set_framerate(framerate)
        self.register_interval()

    def load_from_path(self, path, transparency=False, ppa=True, key=None,
                       query=None, omit=False):
        if isfile(path):
            paths = [path]
        else:
            if query:
                paths = sorted(glob(join(path, query)))
            else:
                paths = [join(path, name) for name in sorted(listdir(path))]
        for path in paths:
            img = load(path)
            if transparency:
                if ppa:
                    frame = img.convert_alpha()
                else:
                    frame = self.fill_colorkey(img, key)
            else:
                frame = img.convert()
            self.add_frame(frame, omit)

    def fill_colorkey(self, img, key=None):
        if not key:
            key = (255, 0, 255)
        img = img.convert_alpha()
        frame = Surface(img.get_size())
        frame.fill(key)
        frame.set_colorkey(key)
        frame.blit(img, (0, 0))
        return frame

    def add_frame(self, frame, omit=False):
        self.frames.append(frame)
        frame.set_alpha(self.alpha)
        if not omit:
            frameset = self.get_current_frameset()
            frameset.add_index(self.frames.index(frame))
            self.update_location_size()
            if frameset.length() > 1:
                self.play()

    def shift_frame(self):
        self.get_current_frameset().shift()

    def get_current_frame(self):
        return self.frames[self.get_current_frameset().get_current_id()]

    def move(self, dx=0, dy=0):
        for location in self.locations:
            location.move_ip(dx, dy)

    def reset_motion_overflow(self):
        for location in self.locations:
            location.reset_motion_overflow()

    def collide(self, other):
        if not isinstance(other, Rect):
            other = other.rect
        for location in self.locations:
            if location.colliderect(other):
                return location

    def mirror(self):
        frames = self.frames
        for ii, frame in enumerate(frames):
             frames[ii] = flip(frame, True, False)
        self.mirrored = not self.mirrored

    def clear_frames(self):
        self.frames = []
        for frameset in self.framesets:
            frameset.order = []
            frameset.reset()
            frameset.measure_rect()

    def add_location(self, topleft=None, offset=(0, 0), count=1, base=0):
        if topleft is not None:
            for ii in xrange(count):
                self.locations.append(Location(
                    self, Rect(topleft, self.locations[0].size)))
        else:
            base = self.locations[base]
            current_offset = list(offset)
            for ii in xrange(count):
                self.locations.append(Location(self,
                                               base.move(*current_offset)))
                current_offset[0] += offset[0]
                current_offset[1] += offset[1]
        return self.locations[-1]

    def fade(self, length=0, out=None, index=None):
        if index is None:
            for location in self.locations:
                fader = location.fader
                fader.reset()
                fader.start(length, out)
        else:
            fader = self.locations[index].fader
            fader.reset()
            fader.start(length, out)

    def set_alpha(self, alpha):
        self.alpha = alpha
        for frame in self.frames:
            frame.set_alpha(alpha)
        for location in self.locations:
            location.fader.set_alpha()

    def add_frameset(self, order=[], framerate=None, name=None, switch=False):
        frameset = Frameset(self, order, framerate, name)
        self.framesets.append(frameset)
        if switch:
            self.set_frameset(len(self.framesets) - 1)
        return frameset

    def hide(self):
        for location in self.locations:
            location.hide()

    def unhide(self):
        for location in self.locations:
            location.unhide()

    def toggle_hidden(self):
        for location in self.locations:
            location.toggle_hidden()

    def is_hidden(self):
        return all(location.is_hidden() for location in self.locations)

    def remove_locations(self, location=None):
        if location:
            self.locations.remove(location)
        else:
            self.locations = self.locations[:1]

    def reverse(self, frameset=None):
        if frameset:
            frameset.reverse()
        else:
            for frameset in self.framesets:
                frameset.reverse()

    def go(self, dx=0, dy=0):
        self.go_vector = Vector(dx, dy)

    def stop(self):
        self.go_vector = Vector()

    def is_going(self):
        return self.go_vector != [0, 0]

    def update(self, areas=None, substitute=None):
        Animation.update(self)
        if self.is_going():
            self.move(*self.go_vector)
        if self.get_current_frameset().length():
            self.draw(areas, substitute)

    def draw(self, areas=None, substitute=None):
        for location in self.locations:
            location.fader.draw(areas, substitute)


class Location(Rect):

    def __init__(self, sprite, rect=(0, 0, 0, 0)):
        self.sprite = sprite
        Rect.__init__(self, rect)
        self.motion_overflow = Vector()
        self.fader = Fader(self)
        self.unhide()

    def move_ip(self, dx, dy):
        if isinstance(dx, float) or isinstance(dy, float):
            excess = self.update_motion_overflow(dx, dy)
            Rect.move_ip(self, int(dx) + excess[0], int(dy) + excess[1])
        else:
            Rect.move_ip(self, dx, dy)

    def update_motion_overflow(self, dx, dy):
        overflow = self.motion_overflow
        overflow.move(dx - int(dx), dy - int(dy))
        excess = map(int, overflow)
        overflow[0] -= int(overflow[0])
        overflow[1] -= int(overflow[1])
        return excess

    def reset_motion_overflow(self):
        self.motion_overflow.place_at_origin()

    def apply_motion_overflow(self, coordinates=None):
        if coordinates is None:
            coordinates = self.topleft
        return self.motion_overflow + coordinates

    def hide(self):
        self.hidden = True

    def unhide(self):
        self.hidden = False

    def toggle_hidden(self):
        self.hidden = not self.hidden

    def is_hidden(self):
        return self.hidden


class Fader(Surface):

    def __init__(self, location):
        self.location = location
        self.time_filter = location.sprite.get_game().time_filter
        self.reset()

    def reset(self):
        self.init_surface()
        self.fade_remaining = None

    def init_surface(self):
        Surface.__init__(self, self.location.size)
        if self.location.sprite.get_current_frameset().length():
            background = Surface(self.get_size())
            sprite = self.location.sprite
            key = sprite.get_current_frame().get_colorkey() or (255, 0, 255)
            self.set_colorkey(key)
            background.fill(key)
            self.background = background
            self.set_alpha()

    def set_alpha(self, alpha=None):
        if alpha is None:
            alpha = self.location.sprite.alpha
        Surface.set_alpha(self, alpha)

    def start(self, length, out=None):
        if self.fade_remaining <= 0:
            alpha = self.get_alpha()
            maximum = self.location.sprite.alpha
            if out is None:
                out = alpha == maximum
            if out and alpha > 0 or not out and alpha < maximum:
                self.fade_length = self.fade_remaining = length
                self.start_time = self.time_filter.get_ticks()
                self.fading_out = out

    def draw(self, areas=None, substitute=None):
        sprite = self.location.sprite
        if substitute is None:
            frame = sprite.get_current_frame()
        else:
            frame = substitute
        if self.fade_remaining >= 0:
            self.update_alpha()
            self.clear()
            frame.set_alpha(255)
            self.blit(frame, (0, 0))
            frame.set_alpha(sprite.alpha)
            if not self.location.is_hidden():
                if frame.get_colorkey() is None:
                    ratio = self.get_alpha() / 255.0
                    pixels = PixelArray(frame.copy())
                    color = Color(0, 0, 0)
                    for x in xrange(len(pixels)):
                        for y in xrange(len(pixels[0])):
                            h, s, l, a = Color(*frame.unmap_rgb(pixels[x][y])).hsla
                            if a:
                                color.hsla = h, s, l, int(a * ratio)
                                pixels[x][y] = color
                    self.blit_to_display(pixels.make_surface(), areas)
                    del pixels
                else:
                    self.blit_to_display(self, areas)
        elif self.fade_remaining is None or self.get_alpha() >= sprite.alpha:
            if self.fade_remaining >= 0:
                self.update_alpha()
            if not self.location.is_hidden():
                self.blit_to_display(frame, areas)

    def blit_to_display(self, frame, areas=None):
        if not isinstance(areas, list):
            areas = [areas]
        for area in areas:
            if area:
                dest = area.left + self.location.left, \
                       area.top + self.location.top
            else:
                dest = self.location
            self.location.sprite.display_surface.blit(frame, dest, area)

    def update_alpha(self):
        remaining = self.fade_remaining = self.fade_length - \
                    (self.time_filter.get_ticks() - self.start_time)
        ratio = self.fade_length and float(remaining) / self.fade_length
        if not self.fading_out:
            ratio = 1 - ratio
        maximum = self.location.sprite.alpha
        alpha = int(ratio * maximum)
        if alpha > maximum:
            alpha = maximum
        elif alpha < 0:
            alpha = 0
        self.set_alpha(alpha)

    def clear(self):
        self.blit(self.background, (0, 0))


class Frameset:

    def __init__(self, sprite, order=[], framerate=None, name=None):
        self.sprite = sprite
        self.name = name
        self.reversed = False
        self.order = []
        self.rect = Rect(0, 0, 0, 0)
        self.add_index(order)
        self.set_framerate(framerate)
        self.reset()

    def add_index(self, order):
        if isinstance(order, int):
            order = [order]
        self.order += order
        self.measure_rect()

    def set_framerate(self, framerate):
        self.framerate = framerate

    def reset(self):
        self.current_index = 0
        self.sprite.accounts[self.sprite.shift_frame].reset_interval()

    def get_current_id(self):
        return self.order[self.current_index]

    def measure_rect(self):
        max_width, max_height = 0, 0
        frames = self.sprite.frames
        for index in self.order:
            frame = frames[index]
            width, height = frame.get_size()
            max_width = max(width, max_width)
            max_height = max(height, max_height)
        self.rect.size = max_width, max_height

    def shift(self):
        if len(self.order) > 1:
            self.increment_index()

    def increment_index(self, increment=None):
        if increment is None:
            increment = 1 if not self.reversed else -1
        index = self.current_index + increment
        while index < 0:
            index += self.length()
        while index >= self.length():
            index -= self.length()
        self.current_index = index

    def length(self):
        return len(self.order)

    def reverse(self):
        self.reversed = not self.reversed


class BlinkingSprite(Sprite):

    def __init__(self, parent, blink_rate, framerate=None):
        Sprite.__init__(self, parent, framerate)
        self.register(self.blink, interval=blink_rate)
        self.play(self.blink)

    def reset(self):
        self.unhide()

    def blink(self):
        self.toggle_hidden()


class RainbowSprite(Sprite):

    def __init__(self, parent, image, hue_shift=8, framerate=None):
        Sprite.__init__(self, parent, framerate)
        for hue in xrange(0, 360, hue_shift):
            self.add_frame(get_hue_shifted_surface(image, hue))
18.97.9.172
18.97.9.172
18.97.9.172
 
September 13, 2013

from array import array
from time import sleep

import pygame
from pygame.mixer import Sound, get_init, pre_init

class Note(Sound):

    def __init__(self, frequency, volume=.1):
        self.frequency = frequency
        Sound.__init__(self, self.build_samples())
        self.set_volume(volume)

    def build_samples(self):
        period = int(round(get_init()[0] / self.frequency))
        samples = array("h", [0] * period)
        amplitude = 2 ** (abs(get_init()[1]) - 1) - 1
        for time in xrange(period):
            if time < period / 2:
                samples[time] = amplitude
            else:
                samples[time] = -amplitude
        return samples

if __name__ == "__main__":
    pre_init(44100, -16, 1, 1024)
    pygame.init()
    Note(440).play(-1)
    sleep(5)

This program generates and plays a 440 Hz tone for 5 seconds. It can be extended to generate the spectrum of notes with a frequency table or the frequency formula. Because the rewards in Send are idealized ocean waves, they can also be represented as tones. Each level has a tone in its goal and a tone based on where the player's disc lands. Both play at the end of a level, sounding harmonic for a close shot and discordant for a near miss. The game can dynamically create these tones using the program as a basis.

I'm also building an algorithmically generated song: Silk Routes (Scissored). Here is an example of how it sounds so far.