#!/usr/bin/env python # -*- coding: utf-8 -*- """ Some tools to create Morse Code audible message Some ideas was taken from https://github.com/zacharydenton/wavebender/blob/master/wavebender/ """ import sys from math import pi, sin import wave import struct from itertools import count, islice from morse_talk.utils import (FREQUENCY, WPM, FRAMERATE, AMPLITUDE, WORD, SECONDS_PER_DOT) from morse_talk.utils import (samples_nb, _seconds_per_dot, _limit_value) from morse_talk.encoding import _encode_binary BITS = 16 CHANNELS = 2 try: from itertools import zip_longest except ImportError: from itertools import imap from itertools import izip from itertools import izip_longest as zip_longest try: stdout = sys.stdout.buffer except AttributeError: stdout = sys.stdout def grouper(n, iterable, fillvalue=None): "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" args = [iter(iterable)] * n return zip_longest(fillvalue=fillvalue, *args) def compute_samples(channels, nsamples=None): ''' create a generator which computes the samples. essentially it creates a sequence of the sum of each function in the channel at each sample in the file for each channel. ''' return islice(izip(*(imap(sum, izip(*channel)) for channel in channels)), nsamples) def write_wavefile(f, samples, nframes=None, nchannels=2, sampwidth=2, framerate=44100, bufsize=2048): "Write samples to a wavefile." if nframes is None: nframes = 0 w = wave.open(f, 'wb') w.setparams((nchannels, sampwidth, framerate, nframes, 'NONE', 'not compressed')) max_amplitude = float(int((2 ** (sampwidth * 8)) / 2) - 1) # split the samples into chunks (to reduce memory consumption and improve performance) for chunk in grouper(bufsize, samples): frames = b''.join(b''.join(struct.pack('h', int(max_amplitude * sample)) for sample in channels) for channels in chunk if channels is not None) w.writeframesraw(frames) w.close() def sine_wave(i, frequency=FREQUENCY, framerate=FRAMERATE, amplitude=AMPLITUDE): """ Returns value of a sine wave at a given frequency and framerate for a given sample i """ omega = 2.0 * pi * float(frequency) sine = sin(omega * (float(i) / float(framerate))) return float(amplitude) * sine def generate_wave(message, wpm=WPM, framerate=FRAMERATE, skip_frame=0, amplitude=AMPLITUDE, frequency=FREQUENCY, word_ref=WORD): """ Generate binary Morse code of message at a given code speed wpm and framerate Parameters ---------- word : string wpm : int or float - word per minute framerate : nb of samples / seconds word_spaced : bool - calculate with spaces between 2 words (default is False) skip_frame : int - nb of frame to skip Returns ------- value : float """ lst_bin = _encode_binary(message) if amplitude > 1.0: amplitude = 1.0 if amplitude < 0.0: amplitude = 0.0 seconds_per_dot = _seconds_per_dot(word_ref) # =1.2 for i in count(skip_frame): bit = morse_bin(i=i, lst_bin=lst_bin, wpm=wpm, framerate=framerate, default_value=0.0, seconds_per_dot=seconds_per_dot) sine = sine_wave(i=i, frequency=frequency, framerate=framerate, amplitude=amplitude) yield sine * bit def morse_bin(i, lst_bin, wpm=WPM, framerate=FRAMERATE, default_value=0.0, seconds_per_dot=1.2): """ Returns value of a morse bin list at a given framerate and code speed (wpm) for a given sample i """ try: return lst_bin[int(float(wpm) * float(i) / (seconds_per_dot * float(framerate)))] except IndexError: return default_value def calculate_wave(i, lst_bin, wpm=WPM, frequency=FREQUENCY, framerate=FRAMERATE, amplitude=AMPLITUDE, seconds_per_dot=SECONDS_PER_DOT): """ Returns product of a sin wave and morse code (dit, dah, silent) """ bit = morse_bin(i=i, lst_bin=lst_bin, wpm=wpm, framerate=framerate, default_value=0.0, seconds_per_dot=seconds_per_dot) sine = sine_wave(i=i, frequency=frequency, framerate=framerate, amplitude=amplitude) return bit * sine def preview_wave(message, wpm=WPM, frequency=FREQUENCY, framerate=FRAMERATE, amplitude=AMPLITUDE, word_ref=WORD): """ Listen (preview) wave sounddevice is required http://python-sounddevice.readthedocs.org/ $ pip install sounddevice """ samp_nb = samples_nb(message=message, wpm=wpm, framerate=framerate, word_spaced=False) import sounddevice as sd lst_bin = _encode_binary(message) amplitude = _limit_value(amplitude) seconds_per_dot = _seconds_per_dot(word_ref) # 1.2 a = [calculate_wave(i, lst_bin, wpm, frequency, framerate, amplitude, seconds_per_dot) for i in range(samp_nb)] sd.play(a, framerate, blocking=True) def main(): import doctest doctest.testmod() if __name__ == '__main__': main()