sound recording

This commit is contained in:
Frank DeMarco 2018-06-04 20:08:24 -04:00
commit 2077dabde6
14 changed files with 182 additions and 43 deletions

9
README
View File

@ -32,7 +32,7 @@ class SampleGame(Game):
if __name__ == '__main__':
SampleGame().play()
SampleGame().run()
License
@ -49,8 +49,7 @@ Todo
- Debug levels
Contact
-------
Credit
------
frank dot s dot demarco at gmail
http://A-O.in
Frank DeMarco <if.self.end@a-o.in>

View File

@ -8,7 +8,6 @@ class Animation(GameChild):
self.default_method = method or self.build_frame
self.accounts = {}
self.register(self.default_method, interval=interval)
self.current_elapsed = 0
self.last_update = 0
def build_frame(self):
@ -57,7 +56,7 @@ class Animation(GameChild):
def is_account_playing(self, account, include_delay):
return account.playing and (not include_delay or not account.delay)
def reset_timer(self, method):
def reset_timer(self, method=None):
if not method:
for account in self.accounts.values():
account.reset_timer()

View File

@ -48,3 +48,27 @@ class Audio(GameChild):
if sound not in self.original_volumes.keys():
self.original_volumes[sound] = sound.get_volume()
sound.set_volume(self.original_volumes[sound] * self.volume)
class SoundEffect(GameChild, Sound):
def __init__(self, parent, path, volume=1.0):
GameChild.__init__(self, parent)
Sound.__init__(self, path)
self.display_surface = self.get_display_surface()
self.initial_volume = volume
self.set_volume(volume)
def play(self, loops=0, maxtime=0, fade_ms=0, position=None, x=None):
self.set_volume(self.initial_volume *
self.get_configuration("audio", "sfx-volume"))
channel = Sound.play(self, loops, maxtime, fade_ms)
if x is not None:
position = float(x) / self.display_surface.get_width()
if position is not None and channel is not None:
channel.set_volume(*self.get_panning(position))
return channel
def get_panning(self, position):
return 1 - max(0, ((position - .5) * 2)), \
1 + min(0, ((position - .5) * 2))

View File

@ -124,6 +124,8 @@ class Configuration(RawConfigParser):
set_option(section, "advance", "7", False)
set_option(section, "pause", "7", False)
set_option(section, "select", "6", False)
set_option(section, "single-xy", "no", False)
set_option(section, "delay-axis", "0", False)
section = "event"
add_section(section)
set_option(section, "user-event-id", "USEREVENT", False)
@ -133,6 +135,7 @@ class Configuration(RawConfigParser):
section = "audio"
add_section(section)
set_option(section, "sfx-path", "aud/fx/", False)
set_option(section, "sfx-volume", "1.0", False)
section = "interpolator-gui"
add_section(section)
set_option(section, "margin", "80", False)
@ -411,9 +414,15 @@ class TypeDeclarations(dict):
"keys": {"list": ["up", "right", "down", "left"]},
"joy": {"int": ["advance", "pause", "select"]},
"joy": {"int": ["advance", "pause", "select"],
"audio": {"path": "sfx-path"},
"float": "delay-axis",
"bool": "single-xy"},
"audio": {"path": "sfx-path",
"float": "sfx-volume"},
"event": {"int": "command-id-offset"},

View File

@ -11,6 +11,8 @@ class Input(GameChild):
def __init__(self, game):
GameChild.__init__(self, game)
self.last_mouse_down_left = None
self.axes_cancelled = {"up": True, "right": True, "down": True, "left": True}
self.last_command_post_time = get_secs()
self.joystick = Joystick()
self.delegate = self.get_delegate()
self.load_configuration()
@ -91,28 +93,51 @@ class Input(GameChild):
self.post_any_command(event.button, cancel)
def translate_axis_motion(self, event):
if not self.suppressed:
if not self.suppressed and not self.check_command_line("-disable-joy-axis"):
axis = event.axis
value = event.value
single_xy = self.get_configuration("joy", "single-xy")
command = None
if -.01 < value < .01:
for command in "up", "right", "down", "left":
self.post_command(command, cancel=True)
if command not in self.any_press_ignored:
self.post_any_command(command, True)
for direction in "up", "right", "down", "left":
if not self.axes_cancelled[direction]:
self.post_command(direction, cancel=True)
if direction not in self.any_press_ignored:
self.post_any_command(direction, True)
self.axes_cancelled[direction] = True
if single_xy:
if direction == "up" and self.joystick.is_direction_pressed(Joystick.down):
command = "down"
elif direction == "down" and self.joystick.is_direction_pressed(Joystick.up):
command = "up"
elif direction == "right" and self.joystick.is_direction_pressed(Joystick.left):
command = "left"
elif direction == "left" and self.joystick.is_direction_pressed(Joystick.right):
command = "right"
else:
if axis == 1:
if value < 0:
command = "up"
if not single_xy or not self.joystick.is_direction_pressed(Joystick.down):
command = "up"
elif value > 0:
command = "down"
if not single_xy or not self.joystick.is_direction_pressed(Joystick.up):
command = "down"
else:
if value > 0:
command = "right"
if not single_xy or not self.joystick.is_direction_pressed(Joystick.left):
command = "right"
elif value < 0:
command = "left"
self.post_command(command)
if command not in self.any_press_ignored:
self.post_any_command(command)
if not single_xy or not self.joystick.is_direction_pressed(Joystick.right):
command = "left"
if command is not None:
delay = self.get_configuration("joy", "delay-axis")
secs = get_secs()
if not delay or secs - self.last_command_post_time > delay:
self.post_command(command)
if command not in self.any_press_ignored:
self.post_any_command(command)
self.axes_cancelled[command] = False
self.last_command_post_time = secs
def is_command_active(self, command):
if not self.suppressed:
@ -150,6 +175,11 @@ class Input(GameChild):
post("mouse-double-click-left", pos=pos)
last = get_secs()
self.last_mouse_down_left = last
if "mouse" not in self.any_press_ignored_keys:
self.post_any_command(event.button)
if event.type == MOUSEBUTTONUP:
if "mouse" not in self.any_press_ignored_keys:
self.post_any_command(event.button, True)
def get_axes(self):
axes = {}

View File

@ -404,7 +404,7 @@ class GUI(Animation):
def deactivate(self):
self.active = False
self.time_filter.open()
self.audio.muted = self.saved_mute_state
# self.audio.muted = self.saved_mute_state
self.display.set_mouse_visibility(self.saved_mouse_state)
def respond_to_command(self, event):
@ -427,8 +427,8 @@ class GUI(Animation):
def activate(self):
self.active = True
self.time_filter.close()
self.saved_mute_state = self.audio.muted
self.audio.mute()
# self.saved_mute_state = self.audio.muted
# self.audio.mute()
self.draw()
self.saved_mouse_state = self.display.set_mouse_visibility(True)

View File

@ -124,3 +124,21 @@ class Note(Samples):
if channel and panning:
channel.set_volume(*panning)
return channel
class Chord:
def __init__(self, *args):
self.notes = args
def play(self, maxtime=0, fadeout=[None], panning=None, fade_in=[0]):
if isinstance(fadeout, int):
fadeout = [fadeout]
if isinstance(fade_in, int):
fade_in = [fade_in]
for ii, note in enumerate(self.notes):
note.play(maxtime, fadeout[ii % len(fadeout)], panning, fade_in[ii % len(fade_in)])
def stop(self):
for note in self.notes:
note.stop()

View File

@ -1,17 +1,24 @@
from os.path import exists
from re import match
from setuptools import setup, find_packages
from Configuration import Configuration
from Setup import Setup
class SetupOSX(Setup):
def __init__(self):
def __init__(self, launcher_path, data_file_paths,
config_file_path="config"):
Setup.__init__(self)
self.launcher_path = launcher_path
self.data_file_paths = data_file_paths
self.config_file_path = config_file_path
def setup(self):
config = Configuration()
setup_section = config.get_section("setup")
version = setup_section["version"]
name = self.translate_title()
setup_obj = Setup()
version = config.get_section("setup")["version"]
name = setup_obj.translate_title()
plist = dict(
CFBundleIconFile=name,
CFBundleName=name,
@ -21,7 +28,17 @@ class SetupOSX(Setup):
CFBundleIdentifier='org.' + name.lower())
setup(name=name,
version=version,
app=[dict(script=setup_section["init-script"], plist=plist)],
app=[dict(script=self.launcher_path, plist=plist)],
setup_requires=["py2app"],
options=dict(py2app=dict(arch="i386",)),
data_files=setup_section["osx-includes"])
data_files=self.data_file_paths)
config_path = "dist/%s.app/Contents/Resources/%s" % \
(name, self.config_file_path)
if exists(config_path):
lines = open(config_path).readlines()
fp = open(config_path, "w")
for line in lines:
if match("^\W*fullscreen\W*=\W*yes\W*", line):
fp.write("fullscreen = no\n")
else:
fp.write(line)

View File

@ -23,6 +23,7 @@ class SetupWin(Setup):
py2exe.build_exe.isSystemDLL = isSystemDLL
def setup(self):
self.numpy_dll_paths_fix()
config = self.config.get_section("setup")
windows = [{}]
if config["init-script"]:
@ -36,6 +37,18 @@ class SetupWin(Setup):
self.copy_data_files()
self.create_archive()
# https://stackoverflow.com/questions/36191770/py2exe-errno-2-no-such-file-or-directory-numpy-atlas-dll
def numpy_dll_paths_fix(self):
import numpy
import sys
paths = set()
np_path = numpy.__path__[0]
for dirpath, _, filenames in walk(np_path):
for item in filenames:
if item.endswith('.dll'):
paths.add(dirpath)
sys.path.append(*list(paths))
def copy_data_files(self):
root = self.config.get("setup", "windows-dist-path")
for path in chain(*zip(*self.build_data_map())[1]):

View File

@ -24,7 +24,7 @@ class Sprite(Animation):
self.frameset_index = 0
self.neighbors = neighbors
self.mass = mass
self.step = EVector()
self._step = EVector()
self.set_frameset(0)
self.locations.append(Location(self))
self.motion_overflow = Vector()
@ -211,18 +211,18 @@ class Sprite(Animation):
frameset.reverse()
def set_step(self, dx=0, dy=0, magnitude=None, angle=0):
self.step.set_step(dx, dy, magnitude, angle)
self._step.set_step(dx, dy, magnitude, angle)
def cancel_step(self):
self.step.set_step(0, 0)
self._step.set_step(0, 0)
def is_stepping(self):
return bool(self.step)
return bool(self._step)
def update(self, areas=None, substitute=None):
Animation.update(self)
if self.is_stepping():
self.move(self.step.dx, self.step.dy)
self.move(self._step.dx, self._step.dy)
for neighbor in self.neighbors:
self.move(*get_step(self.location.center, neighbor.location.center,
neighbor.mass))

View File

@ -80,10 +80,11 @@ class VideoRecorder(GameChild):
save(frombuffer(frame, size, self.frame_format), path)
print "wrote video frames to " + root
if self.check_command_line("-enable-sound-recording"):
import pyaudio
import wave
self.audio_stream.stop_stream()
self.audio_stream.close()
self.audio_recorder.terminate()
import wave
wf = wave.open(join(root, "audio.wav"), "wb")
wf.setnchannels(2)
wf.setsampwidth(self.audio_recorder.get_sample_size(pyaudio.paInt16))

View File

@ -1,4 +1,4 @@
from random import randint
from random import randint, random
from math import sin, cos, atan2, radians, sqrt, pi
from pygame import Surface, PixelArray, Color
@ -6,14 +6,18 @@ from pygame.mixer import get_num_channels, Channel
from pygame.locals import *
def get_step(start, end, speed):
angle = get_angle(start, end)
x0, y0 = start
x1, y1 = end
angle = atan2(x1 - x0, y1 - y0)
return speed * sin(angle), speed * cos(angle)
def get_step_relative(start, end, step):
return get_step(start, end, get_distance(start, end) * step)
def get_angle(start, end, transpose=False):
"""counterclockwise, 0 is down"""
angle = atan2(end[0] - start[0], end[1] - start[1])
angle = atan2(end[1] - start[1], end[0] - start[0])
if transpose:
return -angle - pi
angle = -angle - pi
return angle
def get_endpoint(start, angle, magnitude, translate_angle=True):
@ -27,6 +31,9 @@ def get_delta(angle, magnitude, translate_angle=True):
angle = radians(angle)
return sin(angle) * magnitude, -cos(angle) * magnitude
def reflect_angle(angle, wall):
return wall - angle
def rotate_2d(point, center, angle, translate_angle=True):
if translate_angle:
angle = radians(angle)
@ -110,6 +117,14 @@ def collide_line_with_rect(rect, p0, p1):
if get_intersection(p0, p1, *line):
return True
def get_random_number_in_range(start, end):
return random() * (end - start) + start
def get_value_in_range(start, end, position, reverse=False):
if reverse:
position = 1 - position
return (end - start) * position + start
def render_box(font, text, antialias, color, background=None, border=None,
border_width=1, padding=0):
surface = font.render(text, antialias, color, background)
@ -157,7 +172,7 @@ def get_hue_shifted_surface(base, offset):
for y in xrange(surface.get_height()):
h, s, l, a = Color(*surface.unmap_rgb(pixels[x][y])).hsla
if a:
color.hsla = (h + offset) % 360, s, l, a
color.hsla = (int(h) + offset) % 360, int(s), int(l), int(a)
pixels[x][y] = color
del pixels
return surface

14
pgfw/gfx_extension.py Normal file
View File

@ -0,0 +1,14 @@
from pygame.gfxdraw import (aacircle, filled_circle, aatrigon, filled_trigon,
aapolygon, filled_polygon)
def aa_filled_circle(surface, cx, cy, radius, color):
aacircle(surface, cx, cy, radius, color)
filled_circle(surface, cx, cy, radius, color)
def aa_filled_trigon(surface, x1, y1, x2, y2, x3, y3, color):
aatrigon(surface, x1, y1, x2, y2, x3, y3, color)
filled_trigon(surface, x1, y1, x2, y2, x3, y3, color)
def aa_filled_polygon(surface, points, color):
aapolygon(surface, points, color)
filled_polygon(surface, points, color)

View File

@ -21,4 +21,4 @@ class SampleGame(Game):
if __name__ == '__main__':
# the play method begins the project's animation
SampleGame().play()
SampleGame().run()