From 1043032f980df1cf01f6d31fc5dc6774c1a3d846 Mon Sep 17 00:00:00 2001 From: Stefano Rivera Date: Fri, 16 May 2014 22:19:23 +0200 Subject: [PATCH] Pythonic sound generator --- data/sounds/SOURCES.txt | 93 +---------------------------------------- data/sounds/__init__.py | 25 +++++++++++ naja/actions.py | 3 ++ naja/gen_sound.py | 49 ++++++++++++++++++++++ tools/gen_sound.py | 90 ++++++++++++++------------------------- 5 files changed, 109 insertions(+), 151 deletions(-) create mode 100644 data/sounds/__init__.py create mode 100644 naja/gen_sound.py diff --git a/data/sounds/SOURCES.txt b/data/sounds/SOURCES.txt index b872f6c..954a21b 100644 --- a/data/sounds/SOURCES.txt +++ b/data/sounds/SOURCES.txt @@ -6,95 +6,4 @@ General Notes: sox -t raw -e signed-integer -c 2 -r 44100 -b 16 beep100.pcm my_sound.ogg on those instead. -silence.ogg ------------ - -Notes: - Generated 2 secs of silence - dd if=/dev/zero of=silence.pcm bs=176400 count=2 ; oggenc -r silence.pcm - Generated by Neil Muller, Aug 2010 - Not copyrightable. - -zzzzz.ogg ---------- - -Notes: - Generated by: tools/gen_sound.py 25 0.5 50 ; oggenc -o zzzzz.ogg -r beep100.pcm - Generate by Simon Cross, May 2014 - License: MIT-style permissive license - see LICENSE.txt - -error.ogg ---------- - -Notes: - Generated by: tools/gen_sound.py 1000 0.25 ; oggenc -o error.ogg -r beep1000.pcm - Generated by Neil Muller, May 2014 - License: MIT-style permissive license - see LICENSE.txt - -startup.ogg ------------ - -Notes: - Generated by: tools/gen_sound.py 200 0.25 ; tools/gen_sound.py 400 0.25 ; tools/gen_sound.py 600 0.25 ; tools/gen_sound.py 800 0.25 ; - cat beep200.pcm beep400.pcm beep600.pcm beep800.pcm > startup.pcm ; - oggenc -o startup.ogg -r startup.pcm - Generated by Neil Muller, May 2014 - License: MIT-style permissive license - see LICENSE.txt - - -shutdown.ogg ------------- - -Notes: - Generated by: tools/gen_sound.py 200 0.25 ; tools/gen_sound.py 400 0.25 ; tools/gen_sound.py 600 0.25 ; tools/gen_sound.py 800 0.25 ; - cat beep800.pcm beep600.pcm beep400.pcm beep200.pcm > shutdown.pcm ; - oggenc -o shutdown.ogg -r shutdown.pcm - Generated by Stefano Rivera, May 2014 - License: MIT-style permissive license - see LICENSE.txt - - -zoop.ogg --------- - -Notes: - Generated by: - for ((j=500; $j < 800; j= $j+20 )); do python ../../tools/gen_sound.py $j 0.01 50; done - cat beep5??.pcm beep6??.pcm beep7??.pcm > zoop.pcm - oggenc -o zoop.ogg -r zoop.pcm - Generated by Neil Muller, May 2014 - License: MIT-style permissive license - see LICENSE.txt - -chirp.ogg ---------- - -Notes: - Generate by: - python ../../tools/gen_sound.py 1650 0.05 50 ; python ../../tools/gen_sound.py 1450 0.05 50 - cat beep1450.pcm beep1650.pcm > chirp.pcm - oggenc -o chirp.ogg -r chirp.pcm - Generated by Neil Muller, May 2014 - License: MIT-style permissive license - see LICENSE.txt - - -grind.ogg ---------- - -Notes: - Generate by: - ./tools/gen_sound.py 25 0.01 25 ; ./tools/gen_sound.py 120 0.01 25 ; ./tools/gen_sound.py 150 0.01 25 ; ./tools/gen_sound.py 170 0.01 25 ; ./tools/gen_sound.py 200 0.01 25 ; ./tools/gen_sound.py 250 0.01 25 ; ./tools/gen_sound.py 350 0.01 25 ; ./tools/gen_sound.py 300 0.01 25 - cat beep100.pcm beep250.pcm beep100.pcm beep200.pcm beep170.pcm beep300.pcm beep170.pcm beep100.pcm beep300.pcm beep120.pcm beep100.pcm beep300.pcm beep170.pcm beep120.pcm beep200.pcm beep170.pcm beep150.pcm beep300.pcm beep250.pcm beep350.pcm beep350.pcm beep120.pcm beep150.pcm beep300.pcm beep120.pcm beep200.pcm beep150.pcm beep200.pcm beep100.pcm beep120.pcm beep100.pcm beep300.pcm beep350.pcm beep200.pcm beep100.pcm beep300.pcm beep200.pcm beep200.pcm beep120.pcm beep250.pcm beep120.pcm beep100.pcm beep150.pcm beep100.pcm beep350.pcm beep120.pcm beep170.pcm beep170.pcm beep250.pcm beep170.pcm beep250.pcm beep100.pcm beep170.pcm beep350.pcm beep250.pcm beep150.pcm beep250.pcm beep200.pcm beep350.pcm beep120.pcm beep100.pcm beep120.pcm beep350.pcm beep100.pcm beep120.pcm beep200.pcm beep170.pcm beep300.pcm beep350.pcm beep100.pcm beep300.pcm beep120.pcm beep350.pcm beep250.pcm beep350.pcm beep150.pcm beep100.pcm > grind.pcm - oggenc -o grind.ogg -r grind.pcm - - # order selected by: - - #! /bin/python - import random - files = ['beep100.pcm', 'beep150.pcm', 'beep200.pcm', 'beep350.pcm', 'beep120.pcm', 'beep170.pcm', 'beep250.pcm', 'beep300.pcm'] - cmd = 'cat beep100.pcm %s beep100.pcm > grind.pcm' - rnd = [] - for x in range(75): - rnd.append(random.choice(files)) - print cmd % ' '.join(rnd) - - Generated by Neil Muller, May 2014 - License: MIT-style permissive license - see LICENSE.txt - +See __init__.py and tools/gen_sound.py diff --git a/data/sounds/__init__.py b/data/sounds/__init__.py new file mode 100644 index 0000000..7794093 --- /dev/null +++ b/data/sounds/__init__.py @@ -0,0 +1,25 @@ +import random + +from naja.gen_sound import Chunk, scale + + +def grind(): + yield Chunk('sine', freq=100, length=0.01, volume=25) + tones = [] + for freq in (100, 150, 200, 350, 120, 170, 300): + tones.append(Chunk('sine', freq=freq, length=0.01, volume=25)) + for i in range(75): + yield random.choice(tones) + yield Chunk('sine', freq=100, length=0.01, volume=25) + + +SOUNDS = { + 'chirp': scale(1650, 1449, -200, length=0.05, volume=50), + 'error': Chunk('sine', freq=1000, length=0.25), + 'grind': grind(), + 'shutdown': scale(800, 199, -200), + 'silence': Chunk('silence', length=2), + 'startup': scale(200, 801, 200), + 'zoop': scale(500, 800, 20, length=0.01, volume=50), + 'zzzzz': Chunk('sine', freq=100, length=0.5, volume=50), +} diff --git a/naja/actions.py b/naja/actions.py index 596a505..2def1cf 100644 --- a/naja/actions.py +++ b/naja/actions.py @@ -1,4 +1,5 @@ from naja.constants import ACTION_GLYPHS, BITS, CHESS_PIECES +from naja.sound import sound from naja.utils import bit_glyphs, move_glyph @@ -141,6 +142,7 @@ class ShiftLocations(LocationAction): GLYPHS = (ACTION_GLYPHS.CHANGE_BOARD,) def perform_action(self, board, location): + sound.play_sound('change.ogg') board.shift_locations(self.data['direction']) @@ -149,6 +151,7 @@ class RotateLocations(LocationAction): GLYPHS = (ACTION_GLYPHS.CHANGE_BOARD,) def perform_action(self, board, location): + sound.play_sound('change.ogg') board.rotate_locations(self.data['rot_direction']) diff --git a/naja/gen_sound.py b/naja/gen_sound.py new file mode 100644 index 0000000..8ad0479 --- /dev/null +++ b/naja/gen_sound.py @@ -0,0 +1,49 @@ +import math +import struct + + +def gen_sine(freq, secs, volume): + """ + Generate imperfect sine waves + + Design notes. Produces ~= (user requested) s of raw audio + We're aiming for an 8-bit'ish effect, so we're going with + 8125 Hz, 8 bit sampling, but faking it out to + CDDA output (44100 Hz, 16 bit signed) for easier conversion to ogg + by multiply the value by 256 (after roundin) and repeating it 4 times. + """ + OUTPUT_RATE = 8125 + # We generate freq cycles and sample that OUTPUT_RATE times + per_cycle = OUTPUT_RATE // freq + data = [] + for x in range(per_cycle): + rad = float(x) / per_cycle * 2 * math.pi + y = 256 * int(volume * math.sin(rad)) + data.extend([struct.pack(' [] []') - print (' where is the frequency in Hz (int)') - print (' [] is the time in seconds (float) - default 0.25') - print (' and [] is the volume (integer between 0 and 127)' - ' - default %s' % DEFAULT_VOL) +def write(basename, description): + return encode('%s.ogg' % basename, gen_raw(description)) -if __name__ == "__main__": - try: - freq = int(sys.argv[1]) - if len(sys.argv) > 2: - secs = float(sys.argv[2]) - else: - secs = 0.25 - if len(sys.argv) > 3: - volume = int(sys.argv[3]) - else: - volume = DEFAULT_VOL - except Exception as exc: - usage() - print ('Error was: %s' % exc) - sys.exit(1) +def main(): + sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + from data.sounds import SOUNDS + from naja.gen_sound import Chunk - if volume > 128 or volume < 0: - usage() - print ('Invalid volume: %s' % volume) - sys.exit(1) + sounds = SOUNDS.keys() + if len(sys.argv) > 1: + sounds = (sys.argv[1:]) + for sound in sounds: + description = SOUNDS[sound] + if isinstance(description, Chunk): + description = (description,) + write(sound, description) - if freq > 2000 or freq < 100: - usage() - print ('Invalid freq: %s' % volume) - sys.exit(1) - gen_sine(freq, secs, volume) +if __name__ == '__main__': + main() -- 2.34.1