#!/usr/bin/python

#Audio Tools, a module and set of tools for manipulating audio data
#Copyright (C) 2008-2010  Brian Langenberger

#This program is free software; you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation; either version 2 of the License, or
#(at your option) any later version.

#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.

#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA


import audiotools
import sys
import os
import gettext

gettext.install("audiotools",unicode=True)

class OSSReader(audiotools.PCMReader):
    NAME = "oss"

    def __init__(self, sample_rate, channels, bits_per_sample):
        import ossaudiodev

        audiotools.PCMReader.__init__(
            self,
            ossaudiodev.open('r'),
            sample_rate=sample_rate,
            channels=channels,
            channel_mask=audiotools.ChannelMask.from_channels(channels),
            bits_per_sample=bits_per_sample)
        self.file.channels(channels)
        self.file.speed(sample_rate)
        if (bits_per_sample == 8):
            self.file.setfmt(ossaudiodev.AFMT_U8)
        elif (bits_per_sample == 16):
            self.file.setfmt(ossaudiodev.AFMT_S16_LE)
        else:
            raise ValueError("unsupported number of bits per sample")

    @classmethod
    def available(cls):
        return True

class PulseAudioReader(audiotools.PCMReader):
    NAME = "pulse"

    def __init__(self, sample_rate, channels, bits_per_sample):
        import subprocess

        proc = subprocess.Popen([audiotools.BIN["pacat"],
                                 "-r","-n","record2track",
                                 "--rate=%d" % (sample_rate),
                                 "--format=%s" % {8:"u8",16:"s16le"}.get(
                    bits_per_sample,"float32le"),
                                 "--channels=%d" % (channels)],
                                stdout=subprocess.PIPE)
        audiotools.PCMReader.__init__(
            self,
            proc.stdout,
            sample_rate=sample_rate,
            channels=channels,
            channel_mask=audiotools.ChannelMask.from_channels(channels),
            bits_per_sample=bits_per_sample,
            process=proc)

    @classmethod
    def server_alive(cls):
        import subprocess

        dev = subprocess.Popen([audiotools.BIN["pactl"],"stat"],
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
        dev.stdout.read()
        dev.stderr.read()
        return (dev.wait() == 0)

    @classmethod
    def available(cls):
        return (audiotools.BIN.can_execute(audiotools.BIN["pacat"]) and
                audiotools.BIN.can_execute(audiotools.BIN["pactl"]) and
                cls.server_alive())

if (__name__ == '__main__'):
    parser = audiotools.OptionParser(
        _(u"%prog [options] [-o track]"),
        version="Python Audio Tools %s" % (audiotools.VERSION))

    inputs = [r for r in [OSSReader,PulseAudioReader] if r.available()]
    inputs_map = dict([(r.NAME,r) for r in inputs])

    parser.add_option('-t','--type',
                      action='store',
                      dest='type',
                      choices=audiotools.TYPE_MAP.keys(),
                      help=_(u'the type of audio value to convert to'))

    parser.add_option('-q','--quality',
                      action='store',
                      type='string',
                      dest='quality',
                      help=_(u'the quality to store audio values at'))

    parser.add_option('-i','--input',
                      action='store',
                      dest='input',
                      choices=inputs_map.keys(),
                      default=inputs[-1].NAME,
                      help=_(u"the method to record audio (choose from: %s)")%\
                          ", ".join(["\"%s\"" % (k) for k in
                                     inputs_map.keys()]))

    parser.add_option('-o','--output',
                      action='store',
                      dest='output',
                      help=_(u'output filename to use'))

    parser.add_option('--sample-rate',
                      action='store',
                      dest='sample_rate',
                      type='int',
                      default=44100,
                      help=_(u'the sample rate to use when recording'))

    parser.add_option('--bits-per-sample',
                      action='store',
                      dest='bits_per_sample',
                      type='int',
                      default=16,
                      help=_(u'the number of bits-per-sample to use when recording'))
    parser.add_option('--channels',
                      action='store',
                      dest='channels',
                      type='int',
                      default=2,
                      help=_(u'the number of channels to use when recording'))

    parser.add_option('-V','--verbose',
                      action='store',
                      dest='verbosity',
                      choices=["quiet","normal","debug"],
                      default="normal",
                      help=_(u'the verbosity level to execute at'))

    (options,args) = parser.parse_args()
    msg = audiotools.Messenger("record2track",options)

    if (options.output is None):
        msg.error(_(u"You must specify an output file"))
        sys.exit(1)

    #get the AudioFile class we are converted to
    if (options.type is not None):
        AudioType = audiotools.TYPE_MAP[options.type]
    else:
        try:
            AudioType = audiotools.filename_to_type(options.output)
        except audiotools.UnknownAudioType,exp:
            exp.error_msg(msg)
            sys.exit(1)

    #ensure the selected compression is compatible with that class
    if (options.quality == 'help'):
        if (len(AudioType.COMPRESSION_MODES) > 1):
            msg.info(_(u"Available compression types for %s:") % \
                         (AudioType.NAME))
            for mode in AudioType.COMPRESSION_MODES:
                msg.info(mode.decode('ascii'))
        else:
            msg.error(_(u"Audio type %s has no compression modes") % \
                          (AudioType.NAME))
        sys.exit(0)
    elif (options.quality is None):
        options.quality = AudioType.DEFAULT_COMPRESSION
    elif (options.quality not in AudioType.COMPRESSION_MODES):
        msg.error(_(u"\"%(quality)s\" is not a supported compression mode for type \"%(type)s\"") % \
                        {"quality":options.quality,
                         "type":AudioType.NAME})
        sys.exit(1)

    #ensure we have an output file to write to
    if (options.output is None):
        msg.error(_(u"You must specify an output file"))
        sys.exit(1)

    #I'd like to offer "kill -s SIGINT $MYPID" instructions at this point,
    #but that method works inconsistently on different operating systems
    #whereas Ctrl-C always seems to act as it should.
    #It might be the SIGINT signal handler interacting poorly with
    #InterruptableReader's background thread, but those are Python/OS
    #issues beyond my control.
    msg.partial_info(_(u"Recording (Ctrl-C to stop) "))

    input = audiotools.InterruptableReader(inputs_map[options.input](
        sample_rate=options.sample_rate,
        channels=options.channels,
        bits_per_sample=options.bits_per_sample),
                                           options.verbosity != 'quiet')

    try:
        audiofile = AudioType.from_pcm(options.output,input,options.quality)

        msg.info(_(u"Done"))
    except audiotools.InvalidFormat,err:
        msg.error(unicode(err))
        sys.exit(1)
    except audiotools.EncodingError,err:
        msg.error(_(u"Unable to write \"%s\"") % (msg.filename(options.output)))
        sys.exit(1)

