From 1fee2088a543402d2b8a53ac850ec2a0c871ca7a Mon Sep 17 00:00:00 2001 From: Andrzej Grymkowski Date: Tue, 5 May 2015 23:57:14 +0200 Subject: [PATCH 01/10] initial commit --- examples/audio/androidaudio.py | 173 +++++++++++++++++++++++++++++ examples/audio/audio.py | 92 ++++++++++++++++ examples/audio/buildozer.spec | 193 +++++++++++++++++++++++++++++++++ examples/audio/main.py | 16 +++ 4 files changed, 474 insertions(+) create mode 100644 examples/audio/androidaudio.py create mode 100644 examples/audio/audio.py create mode 100644 examples/audio/buildozer.spec create mode 100644 examples/audio/main.py diff --git a/examples/audio/androidaudio.py b/examples/audio/androidaudio.py new file mode 100644 index 000000000..5e8153d4b --- /dev/null +++ b/examples/audio/androidaudio.py @@ -0,0 +1,173 @@ +from time import sleep + +from jnius import autoclass +from jnius import cast +from jnius import java_method +from jnius import PythonJavaClass + +# from plyer.facades import Microphone +# from plyer.facades import MICROPHONE_STATUS +from audio import Microphone, MICROPHONE_STATUS +from plyer.platforms.android import activity +from plyer.platforms.android import SDK_INT + +# Java Classes +FileInputStream = autoclass('java.io.FileInputStream') +SequenceInputStream = autoclass('java.io.SequenceInputStream') + +# Android Classes + +# Recorder Classes +MediaRecorder = autoclass('android.media.MediaRecorder') +AudioSource = autoclass('android.media.MediaRecorder$AudioSource') +OutputFormat = autoclass('android.media.MediaRecorder$OutputFormat') +AudioEncoder = autoclass('android.media.MediaRecorder$AudioEncoder') + +# Player Classes +MediaPlayer = autoclass('android.media.MediaPlayer') + +OUTPUT_FORMAT = { + 'default': OutputFormat.DEFAULT, # 0x0000 + + # 3GPP media file format + 'three_gpp': OutputFormat.THREE_GPP, # 0x0001 + + # MPEG4 media file format + 'mpeg_4': OutputFormat.MPEG_4, # 0x0002 + + # AMR NB file format, deprecated in api>=16 + 'raw_amr': OutputFormat.RAW_AMR, # 0x0003 + + # AMR NB file format, SDK_INT >= 10 + 'amr_nb': OutputFormat.AMR_NB, # 0x0003 + + # AMR WB file format, SDK_INT >= 10 + 'amr_wb': OutputFormat.AMR_WB, # 0x0004 + + # AAC ADTS file format, SDK_INT >= 16 + 'acc_adts': OutputFormat.AAC_ADTS, # 0x0006 + + # VP8/VORBIS data in a WEBM container, SDK_INT >= 21 + 'webm': OutputFormat.WEBM, # 0x0009 +} + + +AUDIO_ENCODER = { + 'default': AudioEncoder.DEFAULT, # 0x0000 + + # AMR (Narrowband) audio codec + 'amr_nb': AudioEncoder.AMR_NB, # 0x0001 + + # AMR (Wideband) audio codec + 'amr_wb': AudioEncoder.AMR_WB, # 0x0002 + + # AAC Low Complexity (AAC-LC) audio codec + 'acc': AudioEncoder.AAC, # 0x0003 + + # High Efficiency AAC (HE-AAC) audio codec + 'he_aac': AudioEncoder.HE_AAC, # 0x0004 + + # Enhanced Low Delay AAC (AAC-ELD) audio codec + 'acc_eld': AudioEncoder.AAC_ELD, # 0x0005 + + # Ogg Vorbis audio codec + 'vorbis': AudioEncoder.VORBIS, # 0x0006 +} + +AUDIO_SOURCE = { + # Default audio source + 'default': AudioSource.DEFAULT, # 0x0000, + + # Microphone audio source + 'microphone': AudioSource.MIC, # 0x0001 + + # Voice call uplink (Tx) audio source + 'voice_uplink': AudioSource.VOICE_UPLINK, # 0x0002 + + # Voice call downlink (Rx) audio source + 'voice_downlink': AudioSource.VOICE_DOWNLINK, # 0x0003 + + # # Voice call uplink + downlink audio source + 'voice_call': AudioSource.VOICE_CALL, # 0x0004 + + # Microphone audio source with same orientation as camera if available, + # the main device microphone otherwise + 'camcorder': AudioSource.CAMCORDER, # 0x0005 + + # Microphone audio source tuned for voice recognition if available, + # behaves like DEFAULT otherwise. + 'voice_recognition': AudioSource.VOICE_RECOGNITION, # 0x0006 + +} + + +class AndroidMicrophone(Microphone): + """Microphone for android. + .. note:: + Android recorder doesnt handles `pause` and `resume` by default. + In order to have these features, recorder saves recording in + temporary file. + On pause or on stop, recorder merges content from temporary file + with file from given location. + """ + + # needed for pause and resume + _temp_path = '/sdcard/temp.3gp' + + # keeps current options like source, format or encoders + _options = {} + + def __init__(self, file_path=None): + default_path = '/sdcard/testrecorder.3gp' + super(AndroidMicrophone, self).__init__(file_path or default_path) + + def _start(self): + self._recorder = MediaRecorder() + self._recorder.setAudioSource(AUDIO_SOURCE['microphone']) + self._recorder.setOutputFormat(OUTPUT_FORMAT['three_gpp']) + self._recorder.setAudioEncoder(AUDIO_ENCODER['default']) + self._recorder.setOutputFile(self._temp_path) + + self._recorder.prepare() + self._recorder.start() + self.status = MICROPHONE_STATUS['recording'] + + def _stop(self): + self._recorder.stop() + self._recorder.release() + self._recorder = None + self._merge() + self.status = MICROPHONE_STATUS['stopped'] + + def _pause(self): + if self.status == MICROPHONE_STATUS['pause']: + # do un-pause if there is pause already + self._start() + else: + self._stop() + self.status = MICROPHONE_STATUS['pause'] + + def _resume(self): + self._start() + + def _play(self): + media_player = MediaPlayer(activity) + media_player.setDataSource(self.file_path) + media_player.prepare() + media_player.start() + self.status = MICROPHONE_STATUS['play'] + + def _info(self): + return self._options + + def _merge(self): + """Merge temporary recording with original recording.""" + temp = FileInputStream(self._temp_path) + orig = FileInputStream(self._file_path) + + stream = SequenceInputStream(temp, orig) + while True: + data = stream.read() + if data == -1: + break + orig.write(data) diff --git a/examples/audio/audio.py b/examples/audio/audio.py new file mode 100644 index 000000000..ffff72e9d --- /dev/null +++ b/examples/audio/audio.py @@ -0,0 +1,92 @@ + +MICROPHONE_STATUS = { + 'prepare': 'prepare', + 'ready': 'ready', + 'pause': 'pause', + 'play': 'play', + 'recording': 'recording', + 'stopped': 'stopped', +} + + +class Microphone(object): + """Microphone Facade. + Used for recording audio. + Use method `start` to start record and `stop` for stop recording. + For hear, what you have just recorded use method `play`. + Use `pause` to pause record and again or `resume` for un-pause. + Method `info` will inform you about source destination and quality + of the record. + Status will tell you about current job of the Microphone. + .. note:: + You need android permissions: RECORD_AUDIO + """ + + _state = MICROPHONE_STATUS['prepare'] + _file_path = '' + + def __init__(self, file_path): + super(Microphone, self).__init__() + self._file_path = file_path + + def start(self): + """Start record.""" + self._start() + + def _start(self): + raise NotImplementedError() + + def stop(self): + """Stop record.""" + self._stop() + + def _stop(self): + raise NotImplementedError() + + def play(self): + """Play current recording.""" + self._play() + + def _play(self): + raise NotImplementedError() + + def pause(self): + """Pause Record.""" + self._pause() + + def resume(self): + """Resume recording.""" + self._resume() + + def _resume(self): + raise NotImplementedError() + + def _pause(self): + raise NotImplementedError() + + @property + def info(self): + """Give info about quality and source destination.""" + return self._info() + + def _info(self): + raise NotImplementedError() + + @property + def status(self): + """Return status of Microphone.""" + return MICROPHONE_STATUS[self._state] + + @status.setter + def status(self, state): + self._state = state + + @property + def file_path(self): + return self._file_path + + @file_path.setter + def file_path(self, location): + assert isinstance(location, (basestring, unicode)), \ + 'Location must be string or unicode' + self._file_path = location diff --git a/examples/audio/buildozer.spec b/examples/audio/buildozer.spec new file mode 100644 index 000000000..90d8ca25d --- /dev/null +++ b/examples/audio/buildozer.spec @@ -0,0 +1,193 @@ +[app] + +# (str) Title of your application +title = Plyer Audio Example + +# (str) Package name +package.name = audioexample + +# (str) Package domain (needed for android/ios packaging) +package.domain = org.test + +# (str) Source code where the main.py live +source.dir = . + +# (list) Source files to include (let empty to include all the files) +source.include_exts = py,png,jpg,kv,atlas + +# (list) Source files to exclude (let empty to not exclude anything) +#source.exclude_exts = spec + +# (list) List of directory to exclude (let empty to not exclude anything) +#source.exclude_dirs = tests, bin + +# (list) List of exclusions using pattern matching +#source.exclude_patterns = license,images/*/*.jpg + +# (str) Application versioning (method 1) +#version.regex = __version__ = ['"](.*)['"] +#version.filename = %(source.dir)s/main.py + +# (str) Application versioning (method 2) +version = 0.1 + +# (list) Application requirements +# comma seperated e.g. requirements = sqlite3,kivy +requirements = kivy,plyer + +# (str) Custom source folders for requirements +# Sets custom source for any requirements with recipes +# requirements.source.kivy = ../../kivy + +# (list) Garden requirements +#garden_requirements = + +# (str) Presplash of the application +#presplash.filename = %(source.dir)s/data/presplash.png + +# (str) Icon of the application +#icon.filename = %(source.dir)s/data/icon.png + +# (str) Supported orientation (one of landscape, portrait or all) +orientation = portrait + +# (bool) Indicate if the application should be fullscreen or not +fullscreen = 0 + + +# +# Android specific +# + +# (list) Permissions +android.permissions = RECORD_AUDIO,WAKE_LOCK + +# (int) Android API to use +#android.api = 14 + +# (int) Minimum API required (8 = Android 2.2 devices) +#android.minapi = 8 + +# (int) Android SDK version to use +#android.sdk = 21 + +# (str) Android NDK version to use +#android.ndk = 9c + +# (bool) Use --private data storage (True) or --dir public storage (False) +#android.private_storage = True + +# (str) Android NDK directory (if empty, it will be automatically downloaded.) +#android.ndk_path = + +# (str) Android SDK directory (if empty, it will be automatically downloaded.) +#android.sdk_path = + +# (str) python-for-android git clone directory (if empty, it will be automatically cloned from github) +#android.p4a_dir = + +# (list) python-for-android whitelist +#android.p4a_whitelist = + +# (str) Android entry point, default is ok for Kivy-based app +#android.entrypoint = org.renpy.android.PythonActivity + +# (list) List of Java .jar files to add to the libs so that pyjnius can access +# their classes. Don't add jars that you do not need, since extra jars can slow +# down the build process. Allows wildcards matching, for example: +# OUYA-ODK/libs/*.jar +#android.add_jars = foo.jar,bar.jar,path/to/more/*.jar + +# (list) List of Java files to add to the android project (can be java or a +# directory containing the files) +#android.add_src = + +# (str) python-for-android branch to use, if not master, useful to try +# not yet merged features. +#android.branch = master + +# (str) OUYA Console category. Should be one of GAME or APP +# If you leave this blank, OUYA support will not be enabled +#android.ouya.category = GAME + +# (str) Filename of OUYA Console icon. It must be a 732x412 png image. +#android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png + +# (str) XML file to include as an intent filters in tag +#android.manifest.intent_filters = + +# (list) Android additionnal libraries to copy into libs/armeabi +#android.add_libs_armeabi = libs/android/*.so +#android.add_libs_armeabi_v7a = libs/android-v7/*.so +#android.add_libs_x86 = libs/android-x86/*.so +#android.add_libs_mips = libs/android-mips/*.so + +# (bool) Indicate whether the screen should stay on +# Don't forget to add the WAKE_LOCK permission if you set this to True +#android.wakelock = False + +# (list) Android application meta-data to set (key=value format) +#android.meta_data = + +# (list) Android library project to add (will be added in the +# project.properties automatically.) +#android.library_references = + +# +# iOS specific +# + +# (str) Name of the certificate to use for signing the debug version +# Get a list of available identities: buildozer ios list_identities +#ios.codesign.debug = "iPhone Developer: ()" + +# (str) Name of the certificate to use for signing the release version +#ios.codesign.release = %(ios.codesign.debug)s + + +[buildozer] + +# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output)) +log_level = 1 + +# (int) Display warning if buildozer is run as root (0 = False, 1 = True) +warn_on_root = 1 + + +# ----------------------------------------------------------------------------- +# List as sections +# +# You can define all the "list" as [section:key]. +# Each line will be considered as a option to the list. +# Let's take [app] / source.exclude_patterns. +# Instead of doing: +# +#[app] +#source.exclude_patterns = license,data/audio/*.wav,data/images/original/* +# +# This can be translated into: +# +#[app:source.exclude_patterns] +#license +#data/audio/*.wav +#data/images/original/* +# + + +# ----------------------------------------------------------------------------- +# Profiles +# +# You can extend section / key with a profile +# For example, you want to deploy a demo version of your application without +# HD content. You could first change the title to add "(demo)" in the name +# and extend the excluded directories to remove the HD content. +# +#[app@demo] +#title = My Application (demo) +# +#[app:source.exclude_patterns@demo] +#images/hd/* +# +# Then, invoke the command line with the "demo" profile: +# +#buildozer --profile demo android debug diff --git a/examples/audio/main.py b/examples/audio/main.py new file mode 100644 index 000000000..dae886865 --- /dev/null +++ b/examples/audio/main.py @@ -0,0 +1,16 @@ +from time import sleep +from androidaudio import AndroidMicrophone + +if __name__ == "__main__": + + microphone = AndroidMicrophone() + microphone.start() + print 'Status', microphone.status + sleep(5) + microphone.stop() + print 'Status', microphone.status + sleep(1) + microphone.start() + print 'Status', microphone.status + sleep(10) + microphone.stop() From 9508c96a3db6671e2455b3c890dcdd413b1bfbee Mon Sep 17 00:00:00 2001 From: Andrzej Grymkowski Date: Mon, 11 May 2015 00:46:11 +0200 Subject: [PATCH 02/10] added almost working example --- examples/audio/androidaudio.py | 192 ++++++++++++++++----------------- examples/audio/audio.py | 55 ++++------ examples/audio/main.py | 53 ++++++--- 3 files changed, 154 insertions(+), 146 deletions(-) diff --git a/examples/audio/androidaudio.py b/examples/audio/androidaudio.py index 5e8153d4b..8f7e824e0 100644 --- a/examples/audio/androidaudio.py +++ b/examples/audio/androidaudio.py @@ -7,7 +7,7 @@ # from plyer.facades import Microphone # from plyer.facades import MICROPHONE_STATUS -from audio import Microphone, MICROPHONE_STATUS +from audio import Audio from plyer.platforms.android import activity from plyer.platforms.android import SDK_INT @@ -25,83 +25,82 @@ # Player Classes MediaPlayer = autoclass('android.media.MediaPlayer') - -OUTPUT_FORMAT = { - 'default': OutputFormat.DEFAULT, # 0x0000 - - # 3GPP media file format - 'three_gpp': OutputFormat.THREE_GPP, # 0x0001 - - # MPEG4 media file format - 'mpeg_4': OutputFormat.MPEG_4, # 0x0002 - - # AMR NB file format, deprecated in api>=16 - 'raw_amr': OutputFormat.RAW_AMR, # 0x0003 - - # AMR NB file format, SDK_INT >= 10 - 'amr_nb': OutputFormat.AMR_NB, # 0x0003 - - # AMR WB file format, SDK_INT >= 10 - 'amr_wb': OutputFormat.AMR_WB, # 0x0004 - - # AAC ADTS file format, SDK_INT >= 16 - 'acc_adts': OutputFormat.AAC_ADTS, # 0x0006 - - # VP8/VORBIS data in a WEBM container, SDK_INT >= 21 - 'webm': OutputFormat.WEBM, # 0x0009 -} - - -AUDIO_ENCODER = { - 'default': AudioEncoder.DEFAULT, # 0x0000 - - # AMR (Narrowband) audio codec - 'amr_nb': AudioEncoder.AMR_NB, # 0x0001 - - # AMR (Wideband) audio codec - 'amr_wb': AudioEncoder.AMR_WB, # 0x0002 - - # AAC Low Complexity (AAC-LC) audio codec - 'acc': AudioEncoder.AAC, # 0x0003 - - # High Efficiency AAC (HE-AAC) audio codec - 'he_aac': AudioEncoder.HE_AAC, # 0x0004 - - # Enhanced Low Delay AAC (AAC-ELD) audio codec - 'acc_eld': AudioEncoder.AAC_ELD, # 0x0005 - - # Ogg Vorbis audio codec - 'vorbis': AudioEncoder.VORBIS, # 0x0006 -} - -AUDIO_SOURCE = { - # Default audio source - 'default': AudioSource.DEFAULT, # 0x0000, - - # Microphone audio source - 'microphone': AudioSource.MIC, # 0x0001 - - # Voice call uplink (Tx) audio source - 'voice_uplink': AudioSource.VOICE_UPLINK, # 0x0002 - - # Voice call downlink (Rx) audio source - 'voice_downlink': AudioSource.VOICE_DOWNLINK, # 0x0003 - - # # Voice call uplink + downlink audio source - 'voice_call': AudioSource.VOICE_CALL, # 0x0004 - - # Microphone audio source with same orientation as camera if available, - # the main device microphone otherwise - 'camcorder': AudioSource.CAMCORDER, # 0x0005 - - # Microphone audio source tuned for voice recognition if available, - # behaves like DEFAULT otherwise. - 'voice_recognition': AudioSource.VOICE_RECOGNITION, # 0x0006 - -} - - -class AndroidMicrophone(Microphone): +# +# OUTPUT_FORMAT = { +# 'default': OutputFormat.DEFAULT, # 0x0000 +# +# # 3GPP media file format +# 'three_gpp': OutputFormat.THREE_GPP, # 0x0001 +# +# # MPEG4 media file format +# 'mpeg_4': OutputFormat.MPEG_4, # 0x0002 +# +# # AMR NB file format, deprecated in api>=16 +# 'raw_amr': OutputFormat.RAW_AMR, # 0x0003 +# +# # AMR NB file format, SDK_INT >= 10 +# 'amr_nb': OutputFormat.AMR_NB, # 0x0003 +# +# # AMR WB file format, SDK_INT >= 10 +# 'amr_wb': OutputFormat.AMR_WB, # 0x0004 +# +# # AAC ADTS file format, SDK_INT >= 16 +# 'acc_adts': OutputFormat.AAC_ADTS, # 0x0006 +# +# # VP8/VORBIS data in a WEBM container, SDK_INT >= 21 +# 'webm': OutputFormat.WEBM, # 0x0009 +# } +# +# +# AUDIO_ENCODER = { +# 'default': AudioEncoder.DEFAULT, # 0x0000 +# +# # AMR (Narrowband) audio codec +# 'amr_nb': AudioEncoder.AMR_NB, # 0x0001 +# +# # AMR (Wideband) audio codec +# 'amr_wb': AudioEncoder.AMR_WB, # 0x0002 +# +# # AAC Low Complexity (AAC-LC) audio codec +# 'acc': AudioEncoder.AAC, # 0x0003 +# +# # High Efficiency AAC (HE-AAC) audio codec +# 'he_aac': AudioEncoder.HE_AAC, # 0x0004 +# +# # Enhanced Low Delay AAC (AAC-ELD) audio codec +# 'acc_eld': AudioEncoder.AAC_ELD, # 0x0005 +# +# # Ogg Vorbis audio codec +# 'vorbis': AudioEncoder.VORBIS, # 0x0006 +# } +# +# AUDIO_SOURCE = { +# # Default audio source +# 'default': AudioSource.DEFAULT, # 0x0000, +# +# # Microphone audio source +# 'microphone': AudioSource.MIC, # 0x0001 +# +# # Voice call uplink (Tx) audio source +# 'voice_uplink': AudioSource.VOICE_UPLINK, # 0x0002 +# +# # Voice call downlink (Rx) audio source +# 'voice_downlink': AudioSource.VOICE_DOWNLINK, # 0x0003 +# +# # # Voice call uplink + downlink audio source +# 'voice_call': AudioSource.VOICE_CALL, # 0x0004 +# +# # Microphone audio source with same orientation as camera if available, +# # the main device microphone otherwise +# 'camcorder': AudioSource.CAMCORDER, # 0x0005 +# +# # Microphone audio source tuned for voice recognition if available, +# # behaves like DEFAULT otherwise. +# 'voice_recognition': AudioSource.VOICE_RECOGNITION, # 0x0006 +# } + + +class AndroidAudio(Audio): """Microphone for android. .. note:: Android recorder doesnt handles `pause` and `resume` by default. @@ -119,46 +118,32 @@ class AndroidMicrophone(Microphone): def __init__(self, file_path=None): default_path = '/sdcard/testrecorder.3gp' - super(AndroidMicrophone, self).__init__(file_path or default_path) + super(AndroidAudio, self).__init__(file_path or default_path) def _start(self): self._recorder = MediaRecorder() - self._recorder.setAudioSource(AUDIO_SOURCE['microphone']) - self._recorder.setOutputFormat(OUTPUT_FORMAT['three_gpp']) - self._recorder.setAudioEncoder(AUDIO_ENCODER['default']) + self._recorder.setAudioSource(AudioSource.DEFAULT) + self._recorder.setOutputFormat(OutputFormat.DEFAULT) + self._recorder.setAudioEncoder(AudioEncoder.DEFAULT) self._recorder.setOutputFile(self._temp_path) self._recorder.prepare() self._recorder.start() - self.status = MICROPHONE_STATUS['recording'] def _stop(self): self._recorder.stop() self._recorder.release() self._recorder = None self._merge() - self.status = MICROPHONE_STATUS['stopped'] def _pause(self): - if self.status == MICROPHONE_STATUS['pause']: - # do un-pause if there is pause already - self._start() - else: - self._stop() - self.status = MICROPHONE_STATUS['pause'] - - def _resume(self): - self._start() + self._stop() def _play(self): media_player = MediaPlayer(activity) media_player.setDataSource(self.file_path) media_player.prepare() media_player.start() - self.status = MICROPHONE_STATUS['play'] - - def _info(self): - return self._options def _merge(self): """Merge temporary recording with original recording.""" @@ -170,4 +155,15 @@ def _merge(self): data = stream.read() if data == -1: break - orig.write(data) + orig.writeShort(data) + + stream.close() + temp.close() + orig.close() + + + +audio = AndroidAudio() + +def instance(): + return AndroidAudio() \ No newline at end of file diff --git a/examples/audio/audio.py b/examples/audio/audio.py index ffff72e9d..619bf3570 100644 --- a/examples/audio/audio.py +++ b/examples/audio/audio.py @@ -1,15 +1,4 @@ - -MICROPHONE_STATUS = { - 'prepare': 'prepare', - 'ready': 'ready', - 'pause': 'pause', - 'play': 'play', - 'recording': 'recording', - 'stopped': 'stopped', -} - - -class Microphone(object): +class Audio(object): """Microphone Facade. Used for recording audio. Use method `start` to start record and `stop` for stop recording. @@ -22,16 +11,17 @@ class Microphone(object): You need android permissions: RECORD_AUDIO """ - _state = MICROPHONE_STATUS['prepare'] + _state = 'stopped' _file_path = '' def __init__(self, file_path): - super(Microphone, self).__init__() + super(Audio, self).__init__() self._file_path = file_path def start(self): """Start record.""" self._start() + self._state = 'recording' def _start(self): raise NotImplementedError() @@ -39,6 +29,7 @@ def _start(self): def stop(self): """Stop record.""" self._stop() + self._state = 'stopped' def _stop(self): raise NotImplementedError() @@ -46,40 +37,31 @@ def _stop(self): def play(self): """Play current recording.""" self._play() + self._state = 'playing' def _play(self): raise NotImplementedError() def pause(self): - """Pause Record.""" - self._pause() - - def resume(self): - """Resume recording.""" - self._resume() - - def _resume(self): - raise NotImplementedError() + """Pause Recording.""" + if self.state == 'paused': + # state when we want have paused already. + self.start() + else: + self._pause() + self._state = 'paused' def _pause(self): raise NotImplementedError() @property - def info(self): - """Give info about quality and source destination.""" - return self._info() - - def _info(self): - raise NotImplementedError() - - @property - def status(self): + def state(self): """Return status of Microphone.""" - return MICROPHONE_STATUS[self._state] + return self._state - @status.setter - def status(self, state): - self._state = state + @state.setter + def state(self, status): + self._state = status @property def file_path(self): @@ -87,6 +69,7 @@ def file_path(self): @file_path.setter def file_path(self, location): + """Location of the recording.""" assert isinstance(location, (basestring, unicode)), \ 'Location must be string or unicode' self._file_path = location diff --git a/examples/audio/main.py b/examples/audio/main.py index dae886865..e972d8888 100644 --- a/examples/audio/main.py +++ b/examples/audio/main.py @@ -1,16 +1,45 @@ from time import sleep -from androidaudio import AndroidMicrophone + +from kivy.app import App +from kivy.lang import Builder +from kivy.uix.boxlayout import BoxLayout + +kv = ''' +#:import audio androidaudio.audio +BoxLayout: + orientation: 'vertical' + padding: '50dp' + spacing: '20dp' + Label: + size_hint_y: None + height: sp(40) + text: 'AudioPlayer State: ' + str(audio.state) + Button: + text: 'Record' if audio.state == 'stopped' else 'Pause' + on_release: audio.start() if audio.state == 'stopped' else audio.pause() + + Button: + text: 'Play' + #on_release: + # vibrator.pattern([float(n) for n in ti.text.split(',')]) + + TextInput: + id: ti + text: '0.5,0.5,1,2,0.1,0.1,0.1,0.1,0.1,0.1' + +''' + + +class AudioApp(App): + + def build(self): + return Builder.load_string(kv) + + def on_pause(self): + return True + if __name__ == "__main__": + AudioApp().run() + - microphone = AndroidMicrophone() - microphone.start() - print 'Status', microphone.status - sleep(5) - microphone.stop() - print 'Status', microphone.status - sleep(1) - microphone.start() - print 'Status', microphone.status - sleep(10) - microphone.stop() From f2de6d81a49bb6e4c7a3a9591a2e079600eb7284 Mon Sep 17 00:00:00 2001 From: "andrzej.grymkowski" Date: Mon, 11 May 2015 13:19:34 +0200 Subject: [PATCH 03/10] impoved example --- examples/audio/androidaudio.py | 20 ++++++++++---------- examples/audio/main.py | 9 ++++----- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/examples/audio/androidaudio.py b/examples/audio/androidaudio.py index 8f7e824e0..78d660431 100644 --- a/examples/audio/androidaudio.py +++ b/examples/audio/androidaudio.py @@ -9,10 +9,12 @@ # from plyer.facades import MICROPHONE_STATUS from audio import Audio from plyer.platforms.android import activity -from plyer.platforms.android import SDK_INT +# from plyer.platforms.android import SDK_INT # Java Classes FileInputStream = autoclass('java.io.FileInputStream') +FileOutputStream = autoclass('java.io.FileOutputStream') +ByteBuffer = autoclass('java.nio.ByteBuffer') SequenceInputStream = autoclass('java.io.SequenceInputStream') # Android Classes @@ -147,19 +149,17 @@ def _play(self): def _merge(self): """Merge temporary recording with original recording.""" - temp = FileInputStream(self._temp_path) - orig = FileInputStream(self._file_path) - - stream = SequenceInputStream(temp, orig) + input = FileInputStream(self._temp_path) + output = FileOutputStream(self._file_path, True) + buf = ByteBuffer.allocate(1024).array() while True: - data = stream.read() + data = input.read() if data == -1: break - orig.writeShort(data) + output.write(buf, 0, data) - stream.close() - temp.close() - orig.close() + output.close() + input.close() diff --git a/examples/audio/main.py b/examples/audio/main.py index e972d8888..ebcd65dff 100644 --- a/examples/audio/main.py +++ b/examples/audio/main.py @@ -20,12 +20,11 @@ Button: text: 'Play' - #on_release: - # vibrator.pattern([float(n) for n in ti.text.split(',')]) + on_release: audio.play() - TextInput: - id: ti - text: '0.5,0.5,1,2,0.1,0.1,0.1,0.1,0.1,0.1' + Button: + text: 'Stop' + on_release: audio.stop() ''' From 6001c8717b4982fbd93f1bc079315e4d5a18c6f9 Mon Sep 17 00:00:00 2001 From: Andrzej Grymkowski Date: Tue, 12 May 2015 00:44:56 +0200 Subject: [PATCH 04/10] moved example class to facades --- examples/audio/androidaudio.py | 143 ++++--------------------------- examples/audio/audio.py | 28 ++---- examples/audio/main.py | 86 ++++++++++++++++--- plyer/__init__.py | 5 +- plyer/facades/__init__.py | 3 +- plyer/facades/audio.py | 56 ++++++++++++ plyer/platforms/android/audio.py | 59 +++++++++++++ 7 files changed, 222 insertions(+), 158 deletions(-) create mode 100644 plyer/facades/audio.py create mode 100644 plyer/platforms/android/audio.py diff --git a/examples/audio/androidaudio.py b/examples/audio/androidaudio.py index 78d660431..d5efb64f7 100644 --- a/examples/audio/androidaudio.py +++ b/examples/audio/androidaudio.py @@ -1,23 +1,9 @@ -from time import sleep - from jnius import autoclass from jnius import cast from jnius import java_method from jnius import PythonJavaClass -# from plyer.facades import Microphone -# from plyer.facades import MICROPHONE_STATUS from audio import Audio -from plyer.platforms.android import activity -# from plyer.platforms.android import SDK_INT - -# Java Classes -FileInputStream = autoclass('java.io.FileInputStream') -FileOutputStream = autoclass('java.io.FileOutputStream') -ByteBuffer = autoclass('java.nio.ByteBuffer') -SequenceInputStream = autoclass('java.io.SequenceInputStream') - -# Android Classes # Recorder Classes MediaRecorder = autoclass('android.media.MediaRecorder') @@ -27,93 +13,12 @@ # Player Classes MediaPlayer = autoclass('android.media.MediaPlayer') -# -# OUTPUT_FORMAT = { -# 'default': OutputFormat.DEFAULT, # 0x0000 -# -# # 3GPP media file format -# 'three_gpp': OutputFormat.THREE_GPP, # 0x0001 -# -# # MPEG4 media file format -# 'mpeg_4': OutputFormat.MPEG_4, # 0x0002 -# -# # AMR NB file format, deprecated in api>=16 -# 'raw_amr': OutputFormat.RAW_AMR, # 0x0003 -# -# # AMR NB file format, SDK_INT >= 10 -# 'amr_nb': OutputFormat.AMR_NB, # 0x0003 -# -# # AMR WB file format, SDK_INT >= 10 -# 'amr_wb': OutputFormat.AMR_WB, # 0x0004 -# -# # AAC ADTS file format, SDK_INT >= 16 -# 'acc_adts': OutputFormat.AAC_ADTS, # 0x0006 -# -# # VP8/VORBIS data in a WEBM container, SDK_INT >= 21 -# 'webm': OutputFormat.WEBM, # 0x0009 -# } -# -# -# AUDIO_ENCODER = { -# 'default': AudioEncoder.DEFAULT, # 0x0000 -# -# # AMR (Narrowband) audio codec -# 'amr_nb': AudioEncoder.AMR_NB, # 0x0001 -# -# # AMR (Wideband) audio codec -# 'amr_wb': AudioEncoder.AMR_WB, # 0x0002 -# -# # AAC Low Complexity (AAC-LC) audio codec -# 'acc': AudioEncoder.AAC, # 0x0003 -# -# # High Efficiency AAC (HE-AAC) audio codec -# 'he_aac': AudioEncoder.HE_AAC, # 0x0004 -# -# # Enhanced Low Delay AAC (AAC-ELD) audio codec -# 'acc_eld': AudioEncoder.AAC_ELD, # 0x0005 -# -# # Ogg Vorbis audio codec -# 'vorbis': AudioEncoder.VORBIS, # 0x0006 -# } -# -# AUDIO_SOURCE = { -# # Default audio source -# 'default': AudioSource.DEFAULT, # 0x0000, -# -# # Microphone audio source -# 'microphone': AudioSource.MIC, # 0x0001 -# -# # Voice call uplink (Tx) audio source -# 'voice_uplink': AudioSource.VOICE_UPLINK, # 0x0002 -# -# # Voice call downlink (Rx) audio source -# 'voice_downlink': AudioSource.VOICE_DOWNLINK, # 0x0003 -# -# # # Voice call uplink + downlink audio source -# 'voice_call': AudioSource.VOICE_CALL, # 0x0004 -# -# # Microphone audio source with same orientation as camera if available, -# # the main device microphone otherwise -# 'camcorder': AudioSource.CAMCORDER, # 0x0005 -# -# # Microphone audio source tuned for voice recognition if available, -# # behaves like DEFAULT otherwise. -# 'voice_recognition': AudioSource.VOICE_RECOGNITION, # 0x0006 -# } - class AndroidAudio(Audio): - """Microphone for android. - .. note:: - Android recorder doesnt handles `pause` and `resume` by default. - In order to have these features, recorder saves recording in - temporary file. - On pause or on stop, recorder merges content from temporary file - with file from given location. - """ + """Audio for android. - # needed for pause and resume - _temp_path = '/sdcard/temp.3gp' + For recording audio we use MediaRecorder Android c + """ # keeps current options like source, format or encoders _options = {} @@ -121,46 +26,36 @@ class AndroidAudio(Audio): def __init__(self, file_path=None): default_path = '/sdcard/testrecorder.3gp' super(AndroidAudio, self).__init__(file_path or default_path) + + self._recorder = None + self._player = None def _start(self): self._recorder = MediaRecorder() self._recorder.setAudioSource(AudioSource.DEFAULT) self._recorder.setOutputFormat(OutputFormat.DEFAULT) self._recorder.setAudioEncoder(AudioEncoder.DEFAULT) - self._recorder.setOutputFile(self._temp_path) + self._recorder.setOutputFile(self.file_path) self._recorder.prepare() self._recorder.start() def _stop(self): - self._recorder.stop() - self._recorder.release() - self._recorder = None - self._merge() + if self._recorder: + self._recorder.stop() + self._recorder.release() + self._recorder = None - def _pause(self): - self._stop() + if self._player: + self._player.stop() + self._player.release() + self._player = None def _play(self): - media_player = MediaPlayer(activity) - media_player.setDataSource(self.file_path) - media_player.prepare() - media_player.start() - - def _merge(self): - """Merge temporary recording with original recording.""" - input = FileInputStream(self._temp_path) - output = FileOutputStream(self._file_path, True) - buf = ByteBuffer.allocate(1024).array() - while True: - data = input.read() - if data == -1: - break - output.write(buf, 0, data) - - output.close() - input.close() - + self._player = MediaPlayer() + self._player.setDataSource(self.file_path) + self._player.prepare() + self._player.start() audio = AndroidAudio() diff --git a/examples/audio/audio.py b/examples/audio/audio.py index 619bf3570..9c4eaf80f 100644 --- a/examples/audio/audio.py +++ b/examples/audio/audio.py @@ -1,23 +1,25 @@ class Audio(object): - """Microphone Facade. + """Audio Facade. Used for recording audio. Use method `start` to start record and `stop` for stop recording. For hear, what you have just recorded use method `play`. - Use `pause` to pause record and again or `resume` for un-pause. - Method `info` will inform you about source destination and quality - of the record. - Status will tell you about current job of the Microphone. + + Status will tell you about current job of the Audio. + .. note:: You need android permissions: RECORD_AUDIO """ - _state = 'stopped' + _state = 'ready' _file_path = '' def __init__(self, file_path): super(Audio, self).__init__() self._file_path = file_path + def _prepare(self): + raise NotImplementedError + def start(self): """Start record.""" self._start() @@ -29,7 +31,7 @@ def _start(self): def stop(self): """Stop record.""" self._stop() - self._state = 'stopped' + self._state = 'ready' def _stop(self): raise NotImplementedError() @@ -42,18 +44,6 @@ def play(self): def _play(self): raise NotImplementedError() - def pause(self): - """Pause Recording.""" - if self.state == 'paused': - # state when we want have paused already. - self.start() - else: - self._pause() - self._state = 'paused' - - def _pause(self): - raise NotImplementedError() - @property def state(self): """Return status of Microphone.""" diff --git a/examples/audio/main.py b/examples/audio/main.py index ebcd65dff..e5cc8adae 100644 --- a/examples/audio/main.py +++ b/examples/audio/main.py @@ -3,41 +3,101 @@ from kivy.app import App from kivy.lang import Builder from kivy.uix.boxlayout import BoxLayout +from kivy.properties import ObjectProperty +from kivy.properties import NumericProperty -kv = ''' -#:import audio androidaudio.audio -BoxLayout: + +Builder.load_string(''' +#:import audio_player plyer.audio +: + audio: audio_player orientation: 'vertical' padding: '50dp' spacing: '20dp' Label: + id: state_label size_hint_y: None height: sp(40) - text: 'AudioPlayer State: ' + str(audio.state) + text: 'AudioPlayer State: ' + str(root.audio.state) + Label: + id: location_label + size_hint_y: None + height: sp(40) + text: 'Recording Location: ' + str(root.audio.file_path) + Button: - text: 'Record' if audio.state == 'stopped' else 'Pause' - on_release: audio.start() if audio.state == 'stopped' else audio.pause() + id: record_button + text: 'Start Recording' + on_release: root.start_recording() Button: + id: play_button text: 'Play' - on_release: audio.play() + on_release: root.play_recording() - Button: - text: 'Stop' - on_release: audio.stop() +''') + + +class AudioInterface(BoxLayout): + '''Root Widget.''' + + audio = ObjectProperty() + time = NumericProperty(0) + + has_record = False -''' + def start_recording(self): + state = self.audio.state + if state == 'ready': + self.audio.start() + if state == 'recording': + self.audio.stop() + self.has_record = True + + self.update_labels() + + def play_recording(self): + state = self.audio.state + if state == 'playing': + self.audio.stop() + else: + self.audio.play() + + self.update_labels() + + def update_labels(self): + record_button = self.ids['record_button'] + play_button = self.ids['play_button'] + state_label = self.ids['state_label'] + + state = self.audio.state + state_label.text = 'AudioPlayer State: ' + state + + play_button.disabled = not self.has_record + + if state == 'ready': + record_button.text = 'Start Recording' + + if state == 'recording': + record_button.text = 'Press to Stop Recording' + play_button.disabled = True + + if state == 'playing': + play_button.text = 'Stop' + record_button.disabled = True + else: + play_button.text = 'Press to play' + record_button.disabled = False class AudioApp(App): def build(self): - return Builder.load_string(kv) + return AudioInterface() def on_pause(self): return True - if __name__ == "__main__": AudioApp().run() diff --git a/plyer/__init__.py b/plyer/__init__.py index c3994d5b2..0da2916fe 100644 --- a/plyer/__init__.py +++ b/plyer/__init__.py @@ -4,7 +4,7 @@ ''' -__all__ = ('accelerometer', 'battery', 'camera', 'compass', 'email', +__all__ = ('accelerometer', 'audio', 'battery', 'camera', 'compass', 'email', 'filechooser', 'gps', 'gyroscope', 'irblaster', 'orientation', 'notification', 'sms', 'tts', 'uniqueid', 'vibrator') @@ -17,6 +17,9 @@ #: Accelerometer proxy to :class:`plyer.facades.Accelerometer` accelerometer = Proxy('accelerometer', facades.Accelerometer) +#: Accelerometer proxy to :class:`plyer.facades.Audio` +audio = Proxy('audio', facades.Audio) + #: Battery proxy to :class:`plyer.facades.Battery` battery = Proxy('battery', facades.Battery) diff --git a/plyer/facades/__init__.py b/plyer/facades/__init__.py index 7135f082a..e5560c08f 100644 --- a/plyer/facades/__init__.py +++ b/plyer/facades/__init__.py @@ -6,11 +6,12 @@ ''' -__all__ = ('Accelerometer', 'Battery', 'Camera', 'Compass', 'Email', +__all__ = ('Accelerometer', 'Audio', 'Battery', 'Camera', 'Compass', 'Email', 'FileChooser', 'GPS', 'Gyroscope', 'IrBlaster', 'Orientation', 'Notification', 'Sms', 'TTS', 'UniqueID', 'Vibrator') from plyer.facades.accelerometer import Accelerometer +from plyer.facades.audio import Audio from plyer.facades.battery import Battery from plyer.facades.camera import Camera from plyer.facades.compass import Compass diff --git a/plyer/facades/audio.py b/plyer/facades/audio.py new file mode 100644 index 000000000..2b9c101c9 --- /dev/null +++ b/plyer/facades/audio.py @@ -0,0 +1,56 @@ +class Audio(object): + """Audio Facade. + + Used for recording audio. + Use method `start` to start record and `stop` for stop recording. + For hear, what you have just recorded use method `play`. + + Status will tell you about current job of the Audio. + + Default path for recording is set in platform implementation. + + .. note:: + You need android permissions: RECORD_AUDIO + """ + + state = 'ready' + _file_path = '' + + def __init__(self, file_path): + super(Audio, self).__init__() + self._file_path = file_path + + def start(self): + """Start record.""" + self._start() + self.state = 'recording' + + def _start(self): + raise NotImplementedError() + + def stop(self): + """Stop record.""" + self._stop() + self.state = 'ready' + + def _stop(self): + raise NotImplementedError() + + def play(self): + """Play current recording.""" + self._play() + self.state = 'playing' + + def _play(self): + raise NotImplementedError() + + @property + def file_path(self): + return self._file_path + + @file_path.setter + def file_path(self, location): + """Location of the recording.""" + assert isinstance(location, (basestring, unicode)), \ + 'Location must be string or unicode' + self._file_path = location diff --git a/plyer/platforms/android/audio.py b/plyer/platforms/android/audio.py new file mode 100644 index 000000000..1e8a2cac0 --- /dev/null +++ b/plyer/platforms/android/audio.py @@ -0,0 +1,59 @@ +from jnius import autoclass + +from plyer.facades.audio import Audio + +# Recorder Classes +MediaRecorder = autoclass('android.media.MediaRecorder') +AudioSource = autoclass('android.media.MediaRecorder$AudioSource') +OutputFormat = autoclass('android.media.MediaRecorder$OutputFormat') +AudioEncoder = autoclass('android.media.MediaRecorder$AudioEncoder') + +# Player Classes +MediaPlayer = autoclass('android.media.MediaPlayer') + + +class AndroidAudio(Audio): + """Audio for android. + + For recording audio we use MediaRecorder Android class. + For playing audio we use MediaPlayer Android class. + + """ + + def __init__(self, file_path=None): + default_path = '/sdcard/testrecorder.3gp' + super(AndroidAudio, self).__init__(file_path or default_path) + + self._recorder = None + self._player = None + + def _start(self): + self._recorder = MediaRecorder() + self._recorder.setAudioSource(AudioSource.DEFAULT) + self._recorder.setOutputFormat(OutputFormat.DEFAULT) + self._recorder.setAudioEncoder(AudioEncoder.DEFAULT) + self._recorder.setOutputFile(self.file_path) + + self._recorder.prepare() + self._recorder.start() + + def _stop(self): + if self._recorder: + self._recorder.stop() + self._recorder.release() + self._recorder = None + + if self._player: + self._player.stop() + self._player.release() + self._player = None + + def _play(self): + self._player = MediaPlayer() + self._player.setDataSource(self.file_path) + self._player.prepare() + self._player.start() + + +def instance(): + return AndroidAudio() From 7e610b63313d521f6b16ee6229857fe4a8354378 Mon Sep 17 00:00:00 2001 From: Andrzej Grymkowski Date: Tue, 12 May 2015 00:45:40 +0200 Subject: [PATCH 05/10] removing temporary files --- examples/audio/androidaudio.py | 64 --------------------------------- examples/audio/audio.py | 65 ---------------------------------- 2 files changed, 129 deletions(-) delete mode 100644 examples/audio/androidaudio.py delete mode 100644 examples/audio/audio.py diff --git a/examples/audio/androidaudio.py b/examples/audio/androidaudio.py deleted file mode 100644 index d5efb64f7..000000000 --- a/examples/audio/androidaudio.py +++ /dev/null @@ -1,64 +0,0 @@ -from jnius import autoclass -from jnius import cast -from jnius import java_method -from jnius import PythonJavaClass - -from audio import Audio - -# Recorder Classes -MediaRecorder = autoclass('android.media.MediaRecorder') -AudioSource = autoclass('android.media.MediaRecorder$AudioSource') -OutputFormat = autoclass('android.media.MediaRecorder$OutputFormat') -AudioEncoder = autoclass('android.media.MediaRecorder$AudioEncoder') - -# Player Classes -MediaPlayer = autoclass('android.media.MediaPlayer') - -class AndroidAudio(Audio): - """Audio for android. - - For recording audio we use MediaRecorder Android c - """ - - # keeps current options like source, format or encoders - _options = {} - - def __init__(self, file_path=None): - default_path = '/sdcard/testrecorder.3gp' - super(AndroidAudio, self).__init__(file_path or default_path) - - self._recorder = None - self._player = None - - def _start(self): - self._recorder = MediaRecorder() - self._recorder.setAudioSource(AudioSource.DEFAULT) - self._recorder.setOutputFormat(OutputFormat.DEFAULT) - self._recorder.setAudioEncoder(AudioEncoder.DEFAULT) - self._recorder.setOutputFile(self.file_path) - - self._recorder.prepare() - self._recorder.start() - - def _stop(self): - if self._recorder: - self._recorder.stop() - self._recorder.release() - self._recorder = None - - if self._player: - self._player.stop() - self._player.release() - self._player = None - - def _play(self): - self._player = MediaPlayer() - self._player.setDataSource(self.file_path) - self._player.prepare() - self._player.start() - - -audio = AndroidAudio() - -def instance(): - return AndroidAudio() \ No newline at end of file diff --git a/examples/audio/audio.py b/examples/audio/audio.py deleted file mode 100644 index 9c4eaf80f..000000000 --- a/examples/audio/audio.py +++ /dev/null @@ -1,65 +0,0 @@ -class Audio(object): - """Audio Facade. - Used for recording audio. - Use method `start` to start record and `stop` for stop recording. - For hear, what you have just recorded use method `play`. - - Status will tell you about current job of the Audio. - - .. note:: - You need android permissions: RECORD_AUDIO - """ - - _state = 'ready' - _file_path = '' - - def __init__(self, file_path): - super(Audio, self).__init__() - self._file_path = file_path - - def _prepare(self): - raise NotImplementedError - - def start(self): - """Start record.""" - self._start() - self._state = 'recording' - - def _start(self): - raise NotImplementedError() - - def stop(self): - """Stop record.""" - self._stop() - self._state = 'ready' - - def _stop(self): - raise NotImplementedError() - - def play(self): - """Play current recording.""" - self._play() - self._state = 'playing' - - def _play(self): - raise NotImplementedError() - - @property - def state(self): - """Return status of Microphone.""" - return self._state - - @state.setter - def state(self, status): - self._state = status - - @property - def file_path(self): - return self._file_path - - @file_path.setter - def file_path(self, location): - """Location of the recording.""" - assert isinstance(location, (basestring, unicode)), \ - 'Location must be string or unicode' - self._file_path = location From 36a612ca21ab4e03a35b72af44e0e0db7d86cef0 Mon Sep 17 00:00:00 2001 From: Andrzej Grymkowski Date: Tue, 12 May 2015 00:47:42 +0200 Subject: [PATCH 06/10] update readme --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 7da50265d..56860dbdc 100644 --- a/README.rst +++ b/README.rst @@ -34,4 +34,5 @@ Gyroscope X X X Battery X X X X X X Native file chooser X X X Orientation X X +Audio recording X X ================================== ============= ============= === ======= === ===== From 5530c78bf30ac7c3699c41674a1722b968426146 Mon Sep 17 00:00:00 2001 From: Andrzej Grymkowski Date: Tue, 12 May 2015 00:51:54 +0200 Subject: [PATCH 07/10] flake8 --- examples/audio/main.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/audio/main.py b/examples/audio/main.py index e5cc8adae..be453e4c0 100644 --- a/examples/audio/main.py +++ b/examples/audio/main.py @@ -1,10 +1,8 @@ -from time import sleep - from kivy.app import App from kivy.lang import Builder -from kivy.uix.boxlayout import BoxLayout -from kivy.properties import ObjectProperty from kivy.properties import NumericProperty +from kivy.properties import ObjectProperty +from kivy.uix.boxlayout import BoxLayout Builder.load_string(''' @@ -90,6 +88,7 @@ def update_labels(self): play_button.text = 'Press to play' record_button.disabled = False + class AudioApp(App): def build(self): @@ -100,5 +99,3 @@ def on_pause(self): if __name__ == "__main__": AudioApp().run() - - From 883ff406484d1585288cffb615a2fba708ed5f2f Mon Sep 17 00:00:00 2001 From: Andrzej Grymkowski Date: Tue, 12 May 2015 00:59:43 +0200 Subject: [PATCH 08/10] corrected "" in docstring --- plyer/facades/audio.py | 12 ++++++------ plyer/platforms/android/audio.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plyer/facades/audio.py b/plyer/facades/audio.py index 2b9c101c9..c44a8e66c 100644 --- a/plyer/facades/audio.py +++ b/plyer/facades/audio.py @@ -1,5 +1,5 @@ class Audio(object): - """Audio Facade. + '''Audio Facade. Used for recording audio. Use method `start` to start record and `stop` for stop recording. @@ -11,7 +11,7 @@ class Audio(object): .. note:: You need android permissions: RECORD_AUDIO - """ + ''' state = 'ready' _file_path = '' @@ -21,7 +21,7 @@ def __init__(self, file_path): self._file_path = file_path def start(self): - """Start record.""" + '''Start record.''' self._start() self.state = 'recording' @@ -29,7 +29,7 @@ def _start(self): raise NotImplementedError() def stop(self): - """Stop record.""" + '''Stop record.''' self._stop() self.state = 'ready' @@ -37,7 +37,7 @@ def _stop(self): raise NotImplementedError() def play(self): - """Play current recording.""" + '''Play current recording.''' self._play() self.state = 'playing' @@ -50,7 +50,7 @@ def file_path(self): @file_path.setter def file_path(self, location): - """Location of the recording.""" + '''Location of the recording.''' assert isinstance(location, (basestring, unicode)), \ 'Location must be string or unicode' self._file_path = location diff --git a/plyer/platforms/android/audio.py b/plyer/platforms/android/audio.py index 1e8a2cac0..6ff7c2fb8 100644 --- a/plyer/platforms/android/audio.py +++ b/plyer/platforms/android/audio.py @@ -13,12 +13,12 @@ class AndroidAudio(Audio): - """Audio for android. + '''Audio for android. For recording audio we use MediaRecorder Android class. For playing audio we use MediaPlayer Android class. - """ + ''' def __init__(self, file_path=None): default_path = '/sdcard/testrecorder.3gp' From 4b8068e38a5ca0b919b308b7205bcc9531e5ec59 Mon Sep 17 00:00:00 2001 From: "andrzej.grymkowski" Date: Wed, 13 May 2015 13:11:01 +0200 Subject: [PATCH 09/10] improving docs --- plyer/facades/audio.py | 7 ++----- plyer/platforms/android/audio.py | 2 ++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/plyer/facades/audio.py b/plyer/facades/audio.py index c44a8e66c..196edbdca 100644 --- a/plyer/facades/audio.py +++ b/plyer/facades/audio.py @@ -3,14 +3,11 @@ class Audio(object): Used for recording audio. Use method `start` to start record and `stop` for stop recording. - For hear, what you have just recorded use method `play`. + To hear what you have just recorded, use method `play`. - Status will tell you about current job of the Audio. + `status` will tell you about current job of the Audio. Default path for recording is set in platform implementation. - - .. note:: - You need android permissions: RECORD_AUDIO ''' state = 'ready' diff --git a/plyer/platforms/android/audio.py b/plyer/platforms/android/audio.py index 6ff7c2fb8..3e8b35991 100644 --- a/plyer/platforms/android/audio.py +++ b/plyer/platforms/android/audio.py @@ -18,6 +18,8 @@ class AndroidAudio(Audio): For recording audio we use MediaRecorder Android class. For playing audio we use MediaPlayer Android class. + .. note:: + Needed permissions: RECORD_AUDIO ''' def __init__(self, file_path=None): From 959454eb38a5b541e8d0916699f02b000e8377d7 Mon Sep 17 00:00:00 2001 From: "andrzej.grymkowski" Date: Wed, 13 May 2015 16:05:59 +0200 Subject: [PATCH 10/10] update docs facade with permissions --- plyer/facades/audio.py | 3 +++ plyer/platforms/android/audio.py | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plyer/facades/audio.py b/plyer/facades/audio.py index 196edbdca..56788418e 100644 --- a/plyer/facades/audio.py +++ b/plyer/facades/audio.py @@ -8,6 +8,9 @@ class Audio(object): `status` will tell you about current job of the Audio. Default path for recording is set in platform implementation. + + .. note:: + On Android the `RECORD_AUDIO` permission is needed. ''' state = 'ready' diff --git a/plyer/platforms/android/audio.py b/plyer/platforms/android/audio.py index 3e8b35991..2115f198a 100644 --- a/plyer/platforms/android/audio.py +++ b/plyer/platforms/android/audio.py @@ -17,9 +17,6 @@ class AndroidAudio(Audio): For recording audio we use MediaRecorder Android class. For playing audio we use MediaPlayer Android class. - - .. note:: - Needed permissions: RECORD_AUDIO ''' def __init__(self, file_path=None):