sound recording
This commit is contained in:
commit
2077dabde6
9
README
9
README
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"},
|
||||
|
||||
|
|
|
@ -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 = {}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
18
pgfw/Note.py
18
pgfw/Note.py
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]):
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue