From 20988db521214069a13f050511cec9a92e386e62 Mon Sep 17 00:00:00 2001 From: Ivan Maslov Date: Fri, 7 Oct 2022 15:26:27 +0300 Subject: [PATCH] robot.audio added and tested! --- Sources/GuideSphinx/Robot/01_Robot.rst | 4 + Sources/GuideSphinx/Robot/08_audio.rst | 49 +++++ .../{08_HowToUse.rst => 09_HowToUse.rst} | 2 +- Sources/GuideSphinx/make_RUS_Guide_PDF.cmd | 2 +- Sources/pyOpenRPA/Robot/Audio.py | 171 +++++++++++++++--- Tools/Jupyter-notebooks/Audio.ipynb | 22 +++ 6 files changed, 226 insertions(+), 24 deletions(-) create mode 100644 Sources/GuideSphinx/Robot/08_audio.rst rename Sources/GuideSphinx/Robot/{08_HowToUse.rst => 09_HowToUse.rst} (99%) mode change 100755 => 100644 diff --git a/Sources/GuideSphinx/Robot/01_Robot.rst b/Sources/GuideSphinx/Robot/01_Robot.rst index dddf9006..0332b664 100755 --- a/Sources/GuideSphinx/Robot/01_Robot.rst +++ b/Sources/GuideSphinx/Robot/01_Robot.rst @@ -25,6 +25,10 @@ - Mouse: инструменты взаимодействия с мышью. Перейти к описанию функций: :ref:`module.robot.mouse` - Screen: инструменты взаимодействия с эраном рабочего стола. Перейти к описанию функций: :ref:`module.robot.screen` + +- **Уровень доступа к звуковым каналам передачи данных (микрофон, динамик)** + + - Audio: инструменты взаимодействия с аудио. Перейти к описанию функций: :ref:`module.robot.audio` Дорогие коллеги! diff --git a/Sources/GuideSphinx/Robot/08_audio.rst b/Sources/GuideSphinx/Robot/08_audio.rst new file mode 100644 index 00000000..dc03a03c --- /dev/null +++ b/Sources/GuideSphinx/Robot/08_audio.rst @@ -0,0 +1,49 @@ +.. _module.robot.audio: + +#################################### +7. Функции Audio +#################################### + +************************ +Общее +************************ +Дорогие коллеги! + +Мы знаем, что с pyOpenRPA вы сможете существенно улучшить качество вашего бизнеса. Платформа роботизации pyOpenRPA - это разработка, которая дает возможность делать виртуальных сотрудников (программных роботов RPA) выгодными, начиная от эффекта всего в **10 тыс. руб.** И управлять ими будете только Вы! + +Если у вас останутся вопросы, то вы всегда можете обратиться в центр поддержки клиентов pyOpenRPA. Контакты: :ref:`3.-Copyrights-&-Contacts` + +pyOpenRPA - роботы помогут! + +************************ +Класс Recorder +************************ +Экземпляр класса pyOpenRPA.Robot.Audio.Recorder, который обеспечивает захват звука (с микрофона или из приложений) и сохраняет в виде аудиофайла (множества аудиофайлов) + + +************************************************** +Описание функций +************************************************** + +Описание каждой функции начинается с обозначения L-,W+, что означает, что функция не поддерживается в ОС Linux (L) и поддерживается в Windows (W) + +.. automodule:: pyOpenRPA.Robot.Audio + :members: + :autosummary: + + +.. autoclass:: pyOpenRPA.Robot.Audio.Recorder + :members: + :autosummary: + + +****************************** +Быстрая навигация +****************************** + +- `Сообщество pyOpenRPA (telegram) `_ +- `Сообщество pyOpenRPA (tenchat) `_ +- `Сообщество pyOpenRPA (вконтакте) `_ +- `Презентация pyOpenRPA `_ +- `Портал pyOpenRPA `_ +- `Репозиторий pyOpenRPA `_ \ No newline at end of file diff --git a/Sources/GuideSphinx/Robot/08_HowToUse.rst b/Sources/GuideSphinx/Robot/09_HowToUse.rst old mode 100755 new mode 100644 similarity index 99% rename from Sources/GuideSphinx/Robot/08_HowToUse.rst rename to Sources/GuideSphinx/Robot/09_HowToUse.rst index 5c23544e..3b184900 --- a/Sources/GuideSphinx/Robot/08_HowToUse.rst +++ b/Sources/GuideSphinx/Robot/09_HowToUse.rst @@ -1,5 +1,5 @@ #################################### -8. Как использовать? +9. Как использовать? #################################### Модуль РОБОТ - это ключевое звено, которое отвечает за продуктивную роботизацию процесса. Данный модуль не имеет графический или консольный интерфейс - он подключается в качестве библиотеки в проект робота, что позволяет выполнять операции максимально быстро. А также позволяет с легкостью интегрировать робота в другие проекты. diff --git a/Sources/GuideSphinx/make_RUS_Guide_PDF.cmd b/Sources/GuideSphinx/make_RUS_Guide_PDF.cmd index 5b0448e7..5b15aac7 100755 --- a/Sources/GuideSphinx/make_RUS_Guide_PDF.cmd +++ b/Sources/GuideSphinx/make_RUS_Guide_PDF.cmd @@ -1 +1 @@ -"..\..\Resources\wkhtmltopdf\bin\wkhtmltopdf.exe" --javascript-delay 5000 --load-error-handling ignore --enable-local-file-access ..\..\Wiki\RUS_Guide\html\index.html ..\..\Wiki\RUS_Guide\html\01_HowToInstall.html ..\..\Wiki\RUS_Guide\html\03_Copyrights_Contacts.html ..\..\Wiki\RUS_Guide\html\Robot\01_Robot.html ..\..\Wiki\RUS_Guide\html\Robot\02_uidesktop.html ..\..\Wiki\RUS_Guide\html\Robot\03_uiweb.html ..\..\Wiki\RUS_Guide\html\Robot\04_keyboard.html ..\..\Wiki\RUS_Guide\html\Robot\05_clipboard.html ..\..\Wiki\RUS_Guide\html\Robot\06_mouse.html ..\..\pyOpenRPA\Wiki\RUS_Guide\html\Robot\07_screen.html ..\..\Wiki\RUS_Guide\html\Robot\08_HowToUse.html ..\..\Wiki\RUS_Guide\html\Studio\01_Studio.html ..\..\Wiki\RUS_Guide\html\Studio\02_HowToUse.html ..\..\Wiki\RUS_Guide\html\Orchestrator\01_Orchestrator.html ..\..\Wiki\RUS_Guide\html\Orchestrator\02_Defs.html ..\..\Wiki\RUS_Guide\html\Orchestrator\03_gSettingsTemplate.html ..\..\Wiki\RUS_Guide\html\Orchestrator\04_HowToUse.html ..\..\Wiki\RUS_Guide\html\Orchestrator\05_UAC.html ..\..\Wiki\RUS_Guide\pdf\pyOpenRPA_Guide_RUS.pdf \ No newline at end of file +"..\..\Resources\wkhtmltopdf\bin\wkhtmltopdf.exe" --javascript-delay 5000 --load-error-handling ignore --enable-local-file-access ..\..\Wiki\RUS_Guide\html\index.html ..\..\Wiki\RUS_Guide\html\01_HowToInstall.html ..\..\Wiki\RUS_Guide\html\03_Copyrights_Contacts.html ..\..\Wiki\RUS_Guide\html\Robot\01_Robot.html ..\..\Wiki\RUS_Guide\html\Robot\02_uidesktop.html ..\..\Wiki\RUS_Guide\html\Robot\03_uiweb.html ..\..\Wiki\RUS_Guide\html\Robot\04_keyboard.html ..\..\Wiki\RUS_Guide\html\Robot\05_clipboard.html ..\..\Wiki\RUS_Guide\html\Robot\06_mouse.html ..\..\pyOpenRPA\Wiki\RUS_Guide\html\Robot\07_screen.html ..\..\Wiki\RUS_Guide\html\Robot\08_audio.html ..\..\Wiki\RUS_Guide\html\Robot\09_HowToUse.html ..\..\Wiki\RUS_Guide\html\Studio\01_Studio.html ..\..\Wiki\RUS_Guide\html\Studio\02_HowToUse.html ..\..\Wiki\RUS_Guide\html\Orchestrator\01_Orchestrator.html ..\..\Wiki\RUS_Guide\html\Orchestrator\02_Defs.html ..\..\Wiki\RUS_Guide\html\Orchestrator\03_gSettingsTemplate.html ..\..\Wiki\RUS_Guide\html\Orchestrator\04_HowToUse.html ..\..\Wiki\RUS_Guide\html\Orchestrator\05_UAC.html ..\..\Wiki\RUS_Guide\pdf\pyOpenRPA_Guide_RUS.pdf \ No newline at end of file diff --git a/Sources/pyOpenRPA/Robot/Audio.py b/Sources/pyOpenRPA/Robot/Audio.py index b8e9871d..4eeda69d 100644 --- a/Sources/pyOpenRPA/Robot/Audio.py +++ b/Sources/pyOpenRPA/Robot/Audio.py @@ -86,7 +86,6 @@ def DeviceListGet(): return l_result class Recorder: - mT=[] mStatusStr = "0_READY" # "0_READY", "1_RECORDING" mAudio = None mCaptureThread = None @@ -106,8 +105,9 @@ class Recorder: mFileAvailableChunkInt = None mFileNameList=None + mFileInfoDict=None # {"file.mp3":{StartSecFloat:, EndSecFloat:, Extra:,PathStr, }} + mChunkSecFloat = None - mChunkSilentSecFloat = None mStartSecFloat = None mStartChunkSecFloat = None @@ -119,6 +119,16 @@ class Recorder: mIsMicrophoneBool=None def __init__(self, inDeviceInt=None): + """L-,W+: Инициализация экземпляра класса записи звука + + .. code-block:: python + from pyOpenRPA.Robot import Audio + lRec = Audio.Recorder() + + :param inDeviceInt: , по умолчанию None (использование устройства, полученного от DeviceSystemSoundIndex()) + :type inDeviceInt: int, опционально + """ + self.mDeviceInt = inDeviceInt if inDeviceInt == None: inDeviceInt = DeviceSystemSoundIndex() self.mDeviceInt = inDeviceInt @@ -126,17 +136,46 @@ class Recorder: else: self.mIsMicrophoneBool = False def StatusGet(self): + """L-,W+: Вернуть статус записи звука + + .. code-block:: python + from pyOpenRPA.Robot import Audio + lRec = Audio.Recorder() + lRec.StatusGet() + + :return: "0_READY" или "1_RECORDING" + :rtype: str + """ return self.mStatusStr - def CaptureStart(self, inFolderPathStr="",inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 300.0, inChunkSilentSecFloat = 10.0): + def CaptureStart(self, inFolderPathStr="",inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 300.0): + """L-,W+: Начать запись звука + + .. code-block:: python + from pyOpenRPA.Robot import Audio + lRec = Audio.Recorder() + lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 5.0, inChunkSilentSecFloat = 10.0) + lRec.CaptureStop() + + :param inFolderPathStr: Путь к папке, в которую сохранять аудиофайлы захвата , по умолчанию "" + :type inFolderPathStr: str, опционально + :param inFileNameStr: Наименование файла без расширения, по умолчанию "out" + :type inFileNameStr: str, опционально + :param inFileFormatStr: Наименование формата, в который будет происходить сохранение ("mp3" или "wav" или "raw" или "aif"), по умолчанию "mp3" + :type inFileFormatStr: str, опционально + :param inDurationSecFloat: Длительность захвата аудио, по умолчанию None (пока не поступит команда CaptureStop() ) + :type inDurationSecFloat: float, опционально + :param inChunkSecFloat: Максимальная длина части аудиофайла, по умолчанию 300.0 + :type inChunkSecFloat: float, опционально + """ # CHECK AUX.mp3 + self.mFileInfoDict = {} self.mFileNameList=[] self.mRecordedFramesList=[] self.mStatusStr = "1_RECORDING" - if inChunkSecFloat != None or inChunkSilentSecFloat != None: self.mFileAvailableChunkInt = 0 + if inChunkSecFloat != None: self.mFileAvailableChunkInt = 0 self.mDurationSecFloat = inDurationSecFloat self.mChunkSecFloat = inChunkSecFloat - self.mChunkSilentSecFloat = inChunkSilentSecFloat self.mSilentLastCheckTimeFloat=time.time() self.mFolderPathStr = inFolderPathStr self.mFileNameStr = inFileNameStr @@ -164,21 +203,60 @@ class Recorder: self.mRecordedFramesList.append(self.mStream.read(self.mFramesInt)) self.__TriggerCheck__() - def CaptureStop(self, inWaitStream=True): + def CaptureWait(self): + """L-,W+: Ожидать окончания захвата аудио + + .. code-block:: python + from pyOpenRPA.Robot import Audio + lRec = Audio.Recorder() + lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = 10.0, inChunkSecFloat = 5.0, inChunkSilentSecFloat = 10.0) + lRec.CaptureWait() + """ + self.mCaptureThread.join() + + def CaptureStop(self, inWaitStream=True, inExtra=None): + """L-,W+: Остановить захват аудио + + .. code-block:: python + from pyOpenRPA.Robot import Audio + lRec = Audio.Recorder() + lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 5.0, inChunkSilentSecFloat = 10.0) + lRec.CaptureStop() + + :param inWaitStream: True - выполнить ожидание окончания потока захвата перед окончанием, по умолчанию True + :type inWaitStream: bool, опционально + :param inExtra: Дополнительный контент, необходимый для идентификации файла. В дальнейшем получить структуру можно с помощью функции FileInfoGet()['Extra'], по умолчанию None + :type inExtra: any, опционально + """ self.mCaptureBool=False if inWaitStream == True: self.mCaptureThread.join() self.mStream.stop_stream() self.mStream.close() #Close module self.mAudio.terminate() - self.CaptureChunk() + self.CaptureChunk(inExtra=inExtra, inForceChunkBool=False) self.mStatusStr = "0_READY" - def CaptureChunk(self): + def CaptureChunk(self, inExtra=None, inForceChunkBool=True): + """L-,W+: Зафиксировать захват аудио в виде промежуточного файла вида: <имя файла>_00000.mp3 + + .. code-block:: python + from pyOpenRPA.Robot import Audio + lRec = Audio.Recorder() + lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 5.0, inChunkSilentSecFloat = 10.0) + lRec.CaptureChunk() + + :param inExtra: Дополнительный контент, необходимый для идентификации файла. В дальнейшем получить структуру можно с помощью функции FileInfoGet()['Extra'], по умолчанию None + :type inExtra: any, опционально + :param inForceChunkBool: True - вне зависимости от текущего режима перейти на режим сохранения по частям, по умолчанию True + :type inForceChunkBool: bool, опционально + """ + if inForceChunkBool==True and self.mFileAvailableChunkInt==None: self.mFileAvailableChunkInt=0 lFileNameStr = self.mFileNameStr if self.mFileAvailableChunkInt!=None: lFileNameStr+=f"_{self.mFileAvailableChunkInt:05}" self.mFileAvailableChunkInt = self.mFileAvailableChunkInt + 1 + lFilePathStr = os.path.abspath(os.path.join(self.mFolderPathStr,f"{lFileNameStr}.{self.mFileFormatStr}")) # Advanced usage, if you have raw audio data: sound = AudioSegment( # raw audio data (bytes) @@ -190,37 +268,86 @@ class Recorder: # stereo channels=self.mChannelCountInt ) - sound.export(os.path.join(self.mFolderPathStr,f"{lFileNameStr}.{self.mFileFormatStr}"), format=f"{self.mFileFormatStr}") - self.mFileNameList.append(f"{lFileNameStr}.{self.mFileFormatStr}") self.mRecordedFramesList = [] - self.mStartChunkSecFloat = time.time() + lTimeSecFloat = time.time() + sound.export(lFilePathStr, format=f"{self.mFileFormatStr}") + self.mFileNameList.append(f"{lFileNameStr}.{self.mFileFormatStr}") + self.mFileInfoDict[f"{lFileNameStr}.{self.mFileFormatStr}"]= { + "StartSecFloat": self.mStartChunkSecFloat, + "EndSecFloat": lTimeSecFloat, + "Extra": inExtra, + "PathStr": lFilePathStr + } + self.mStartChunkSecFloat = lTimeSecFloat + + def FileInfoGet(self, inFileNameStr=None): + """L-,W+: Вернуть информацию по аудиофайлу inFileNameStr. Если inFileNameStr == None -> Функция вернет информацию по последнему записанному файлу + + .. code-block:: python + from pyOpenRPA.Robot import Audio + lRec = Audio.Recorder() + lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 5.0, inChunkSilentSecFloat = 10.0) + lRec.CaptureChunk() + lFileInfoDict = lRec.FileInfoGet() + + :param inFileNameStr: Наименование аудиофайла с указанием расширения, по умолчанию None (взять последний записанный файл) + :type inFileNameStr: str, опционально + :return: {StartSecFloat:, EndSecFloat:, Extra:, PathStr:, } или None + :rtype: dict + """ + if inFileNameStr == None: inFileNameStr = self.FileLastGet() + return self.mFileInfoDict.get(inFileNameStr, None) def FileListGet(self): + """L-,W+: Вернуть список сохраненных аудиофайлов (наименования) + + .. code-block:: python + from pyOpenRPA.Robot import Audio + lRec = Audio.Recorder() + lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 5.0, inChunkSilentSecFloat = 10.0) + lRec.CaptureChunk() + lRec.CaptureChunk() + lRec.FileListGet() + + :return: ["out_00000.mp3", "out_00001.mp3", ...] + :rtype: list + """ return self.mFileNameList def FileLastGet(self): + """L-,W+: Вернуть наименование последнего сохраненного аудиофайла + + .. code-block:: python + from pyOpenRPA.Robot import Audio + lRec = Audio.Recorder() + lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 5.0, inChunkSilentSecFloat = 10.0) + lRec.CaptureChunk() + lRec.CaptureChunk() + lRec.FileListGet() + + :return: ["out_00000.mp3", "out_00001.mp3", ...] + :rtype: list + """ return self.mFileNameList[-1] def __Callback__(self, inDefList): pass - def __CallbackIsSilent__(self): - pass def __CallbackIsChunked__(self): pass def __CallbackIsStopped__(self): pass - def IsSilent(self, inLastSecFloat=None): - "Returns 'True' if below the 'silent' threshold" - self.mSilentLastCheckTimeFloat = time.time() - if inLastSecFloat == None: inLastSecFloat = self.mChunkSilentSecFloat - lFrameLenInt = int(self.mSampleSizeInt*inLastSecFloat) - if lFrameLenInt\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mos\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mos\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpath\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mabspath\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"\"\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;34m\"\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m: abspath() takes 1 positional argument but 2 were given" + ] + } + ], + "source": [ + "import os\n", + "os.path.abspath(\"\",\"\")" + ] + }, { "cell_type": "code", "execution_count": null,