from random import random

from pygame.mixer import Sound

from _send.pgfw.GameChild import GameChild

class Track(GameChild):

    def __init__(self, parent, path, volume):
        GameChild.__init__(self, parent)
        self.path = path
        self.volume = volume
        self.channel = None
        self.frequency = self.get_configuration("title", "pan-frequency")
        self.set_sound()

    def set_sound(self):
        sound = Sound(self.path)
        sound.set_volume(self.volume)
        self.sound = sound

    def play(self):
        self.channel = self.sound.play(-1)

    def update(self):
        if self.channel and random() < self.frequency:
            self.parent.set_random_panning(self.channel)
from re import match

from _send.pgfw.GameChild import GameChild
from _send.tartan.Tartan import Tartan

class Tartans(GameChild, dict):

    def __init__(self, parent):
        GameChild.__init__(self, parent)
        self.load()

    def load(self):
        patterns = file(self.get_resource("tartan", "path"))
        while True:
            line = patterns.readline()
            if not line:
                break
            name = line.strip()
            self[name] = Tartan(self, name, patterns)
from re import match, sub
from itertools import izip, chain

from pygame import Surface, Color, PixelArray
from pygame.image import save

from _send.pgfw.GameChild import GameChild

class Tartan(GameChild, Surface):

    def __init__(self, parent, name=None, patterns=None):
        GameChild.__init__(self, parent)
        self.fields = self.get_game().fields
        self.name = name
        self.load_configuration()
        if patterns is not None:
            self.load(patterns)
        self.init_surface(0)
        # self.generate(store=True)

    def load_configuration(self):
        config = self.get_configuration("tartan")
        self.thread_size = config["thread-size"]
        self.sample_size = config["sample-size"]

    def load(self, patterns):
        self.sett = self.parse_sett(patterns.readline())
        self.asymmetric = bool(int(patterns.readline().strip()))
        self.palette = self.parse_palette(patterns)

    def parse_sett(self, line):
        sett = []
        size = 0
        for stripe in line.strip().split(" "):
            identifier, width = match("([A-Z]+)([0-9]+)", stripe).groups(0)
            width = int(width)
            size += width
            sett.append(Stripe(identifier, width))
        self.sett_width = size
        return sett

    def parse_palette(self, patterns):
        palette = {}
        while True:
            line = patterns.readline().strip()
            if not line:
                break
            for color in line.split(";"):
                color = color.strip()
                if color:
                    assignment = map(str.strip, color.split("="))
                    palette[assignment[0]] = Color("#%sFF" % assignment[1][:6])
        return palette

    def generate(self, scale=None, store=False):
        if not scale:
            scale = self.get_default_scale()
        surface = self.select_surface(scale, store)
        self.paint(scale, surface)
        if not store:
            return surface

    def get_default_scale(self):
        return self.fields.get(self.name).tartan_scale

    def select_surface(self, scale, store):
        size = self.get_size(scale)
        if store:
            self.init_surface(size)
            surface = self
        else:
            surface = Surface((size, size))
        return surface

    def get_size(self, scale):
        sett = self.sett
        sett_width = self.sett_width
        if not self.asymmetric:
            size = scale * (sett_width * 2 - sett[-1].width - sett[0].width)
        else:
            size = scale * sett_width
        return size

    def init_surface(self, size):
        Surface.__init__(self, (size, size))

    def paint(self, scale, surface):
        offset, overflow = 0, 0
        sett = self.sett
        pixels = PixelArray(surface)
        for stripe in self.get_stripes_generator(scale):
            width = stripe.width
            color = self.palette[stripe.identifier]
            if overflow:
                width -= 1 - overflow
                blend = self.blend_colors(previous_color, color, overflow)
                self.draw_line(pixels, offset, 1, blend)
                offset += 1
            if width >= 1:
                truncated = int(width)
                self.draw_line(pixels, offset, truncated, color)
                offset += truncated
                overflow = width - truncated
            else:
                overflow = max(width, 0)
            previous_color = color
        del pixels

    def get_stripes_generator(self, scale):
        sett = self.sett
        if not self.asymmetric:
            return (stripe.get_scaled(scale) for stripe in
                    chain(sett[:-1], reversed(sett[1:])))
        return (stripe.get_scaled(scale) for stripe in sett)

    def draw_line(self, pixels, offset, width, color):
        thread_size = self.thread_size
        operator = thread_size * 2
        for y in xrange(offset, min(offset + width, len(pixels[0]))):
            for x in xrange(0, len(pixels[0])):
                if (x + y) % operator < thread_size:
                    pixels[x][y] = color
        for x in xrange(offset, min(offset + width, len(pixels[0]))):
            for y in xrange(0, len(pixels[0])):
                if (x + y) % operator >= thread_size:
                    pixels[x][y] = color

    def blend_colors(self, alpha, beta, ratio):
        alpha = (component * ratio for component in alpha)
        beta = (component * (1 - ratio) for component in beta)
        blend = [int(sum(components)) for components in izip(alpha, beta)]
        return Color(*blend)

    def write_sample(self):
        step = self.get_width()
        shift = step / 2
        count = self.sample_size
        size = step * count
        offsets = range(-shift, size + step, step)
        sample = Surface((size, size))
        for x in offsets:
            for y in offsets:
                sample.blit(self, (x, y))
        path = "resource/local/img/tartan/generated/%02i-%s.png" % \
               (self.fields.get_index(self.name) + 1,
                self.get_filesystem_formatted_name())
        save(sample, path)

    def get_filesystem_formatted_name(self):
        return sub("[^\w-]", "_", self.name.replace(" ", "-"))


class Stripe:

    def __init__(self, identifier, width):
        self.identifier = identifier
        self.width = width

    def __repr__(self):
        return "<%s at %s (%s %.3f)>" % \
               (self.__class__.__name__, hex(id(self)), self.identifier,
                self.width)

    def get_scaled(self, scale):
        return Stripe(self.identifier, self.width * scale)
54.91.121.255
54.91.121.255
54.91.121.255
 
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:


↠ RSS Feed ↞