#!/usr/bin/env python # PYTHON_ARGCOMPLETE_OK import os import sys import zlib import logging import argparse if sys.version_info.major == 2: _stdin = sys.stdin _stdout = sys.stdout else: _stdin = sys.stdin.buffer _stdout = sys.stdout.buffer try: import argcomplete except ImportError: argcomplete = None log = logging.getLogger('__name__') from amodem import recv, send, calib, audio from amodem.config import bitrates bitrate = os.environ.get('BITRATE', 1) config = bitrates.get(int(bitrate)) class Compressor(object): def __init__(self, stream): self.obj = zlib.compressobj() self.stream = stream def read(self, size): data = self.stream.read(size) if data: data = self.obj.compress(data) elif self.obj: data = self.obj.flush() self.obj = None return data class Decompressor(object): def __init__(self, stream): self.obj = zlib.decompressobj() self.stream = stream def write(self, data): self.stream.write(self.obj.decompress(bytes(data))) def flush(self): self.stream.write(self.obj.flush()) def FileType(mode, audio_interface=None): def opener(fname): assert 'r' in mode or 'w' in mode if audio_interface is None and fname is None: fname = '-' if fname is None: assert audio_interface is not None if 'r' in mode: return audio_interface.recorder() if 'w' in mode: return audio_interface.player() if fname == '-': if 'r' in mode: return _stdin if 'w' in mode: return _stdout return open(fname, mode) return opener def main(): fmt = ('Audio OFDM MODEM: {0:.1f} kb/s ({1:d}-QAM x {2:d} carriers) ' 'Fs={3:.1f} kHz') description = fmt.format(config.modem_bps / 1e3, len(config.symbols), config.Nfreq, config.Fs / 1e3) interface = audio.Interface('libportaudio.so', config=config) p = argparse.ArgumentParser(description=description) subparsers = p.add_subparsers() def wrap(cls, stream, enable): return cls(stream) if enable else stream # Modulator sender = subparsers.add_parser( 'send', help='modulate binary data into audio signal.') sender.add_argument( '-i', '--input', help='input file (use "-" for stdin).') sender.add_argument( '-o', '--output', help='output file (use "-" for stdout).' ' if not specified, `aplay` tool will be used.') sender.add_argument( '-c', '--calibrate', default=False, action='store_true') sender.set_defaults( main=lambda config, args: send.main( config, src=wrap(Compressor, args.src, args.zip), dst=args.dst ), calib=lambda config, args: calib.send( config, dst=args.dst ), input_type=FileType('rb'), output_type=FileType('wb', interface) ) # Demodulator receiver = subparsers.add_parser( 'recv', help='demodulate audio signal into binary data.') receiver.add_argument( '-i', '--input', help='input file (use "-" for stdin).' ' if not specified, `arecord` tool will be used.') receiver.add_argument( '-o', '--output', help='output file (use "-" for stdout).') receiver.add_argument( '-c', '--calibrate', default=False, action='store_true') receiver.add_argument( '-d', '--dump', type=FileType('wb'), help='Filename to save recorded audio') receiver.add_argument( '--plot', action='store_true', default=False, help='plot results using pylab module') receiver.set_defaults( main=lambda config, args: recv.main( config, src=args.src, dst=wrap(Decompressor, args.dst, args.zip), pylab=args.pylab, dump_audio=args.dump ), calib=lambda config, args: calib.recv( config, src=args.src, verbose=args.verbose ), input_type=FileType('rb', interface), output_type=FileType('wb') ) for sub in subparsers.choices.values(): sub.add_argument('-z', '--zip', default=False, action='store_true') g = sub.add_mutually_exclusive_group() g.add_argument('-v', '--verbose', default=0, action='count') g.add_argument('-q', '--quiet', default=False, action='store_true') if argcomplete: argcomplete.autocomplete(p) with interface: args = p.parse_args() if args.verbose == 0: level, format = 'INFO', '%(message)s' elif args.verbose == 1: level, format = 'DEBUG', '%(message)s' elif args.verbose >= 2: level, format = ('DEBUG', '%(asctime)s %(levelname)-10s ' '%(message)-100s ' '%(filename)s:%(lineno)d') if args.quiet: level, format = 'WARNING', '%(message)s' logging.basicConfig(level=level, format=format) # Parsing and execution log.debug(description) args.pylab = None if getattr(args, 'plot', False): import pylab args.pylab = pylab args.src = args.input_type(args.input) args.dst = args.output_type(args.output) if args.calibrate: args.calib(config=config, args=args) else: return args.main(config=config, args=args) if __name__ == '__main__': success = main() sys.exit(0 if success else 1)