From 4ae259c2357bcfbbbf46570c21dc2fad6bce0c8d Mon Sep 17 00:00:00 2001 From: Nima Samadi Date: Fri, 16 Jun 2023 23:42:30 +0330 Subject: [PATCH 01/16] Add async to dev - test async feature --- AUTHORS.md | 1 + dev/clear.wav | Bin 0 -> 15058 bytes dev/playsound.py | 80 ++++++++++++++++++++++++++++++++++++++++++++ dev/test.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+) create mode 100644 dev/clear.wav create mode 100644 dev/playsound.py create mode 100644 dev/test.py diff --git a/AUTHORS.md b/AUTHORS.md index 27f620d..e1540c2 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -11,3 +11,4 @@ # Other Contributors # ---------- - Zahra Mobasher ([Instagram](https://www.instagram.com/littleblackoyster/?hl=en)) +- Nima Samadi ([Github](https://github.com/NimaSamadi007)) \ No newline at end of file diff --git a/dev/clear.wav b/dev/clear.wav new file mode 100644 index 0000000000000000000000000000000000000000..f825b257633d6ca6bc97f1e05c3b18eb3f248cc7 GIT binary patch literal 15058 zcmeHNv5wp}5M6gE^9eydBV7s`NSnfGT$};HV8H+^RH#rvfQ1SbTe$ESgnwyujAl$fm)8ZO(t+Cj`6fY)77pOlZKrLik0eZ{>YC1YtRxmkK$jcgd7jg7Eg zfl^l}jp#yQMANo5Zbs5(AqB%bNx%1uLGxqU9Hwv-4(MX4>^u;B2lQdMC-g61HCalT z>k54bwQ#boBS8R*MM#Qp!$2nh5dXjdP^dN1ZRG1<2x$(8+V>h55lH|O5DpwWEK48< z3qTIJh=6`@AtKOZ{e+ew7y&ES&&IH^05*q&I=;+aXOn^|_FlZ0L_rY&EP7eVDcSax zOSa973L8`k!}8K#+Cc#xvq*Hdj3jfj-h+*7o#N9xQLqgnF;FQT;QoM94wW#nS?(iC z94@ly&FkvxLbqkqPmlArW4038^l9&qCMWOy--0SuSB$A-4VAgf@;H<m^uT_~9?@XHi-4?BRwCC@FYrYYuf0w8^paz;4zh5r zJ5$mK*SUv!xWU~B*SQBnw^pm`?D4zMY@39W&^a$Bov->3e)`M9=InyY4!UEx)p>ON z#VFI}QnrW74Lww*hiZW@5BHL_j2*h*J5rafQMSIyQ0wQ}iRN0Fa7}4-2C`jslPcrC zcOom1b%PVdt63*@n{c~=g{qmI2D^>>Nhc1vaHMQ~R;gC(BO$xr3Q$(Gh_~`zH?DH$ zX8NGZ_53B{*LnSHd#NeeE@@1>p6rCAR;n}Y;YIwZ9bEyF5Z`JOuRX?6lacX zby?nP($zIpl{_p}SEo}S@tIP2rM#y+mUluh{uW{$D-h4z_7BJk;YZQOqidx6r1%>Pka?#zpCqtLI`lA?_HQ`5`^QZ7 zPt$P8IybR*Q-1SkZRX>P1$z(2i?*N9+2cDyOizy>&#!LA2^i~?8~qI$*NR5#CSCi? zHnJELYE3O!`)1f~yf6GSGY$?)`?cgBcvu)qM<9FLN(<_(v(-HlN~eQN&xH?$pu>!d z7YG^F&AWJKd7}dwTmtZ>7tfGG3~$cy|jg-2ep@ zvFW}>G*vcRemO(CBVqt&Pkn34Vqgpzr+WL!54j3MJ{_BFuOWFAj None: + """ + Start playing given sound + + :file_name: sound file name to play + :type: str + :is_async: play sound synchronously or asynchronously + :type: bool + :loop: play sound on loop + :type: bool + :return: None + """ + if is_async: + asyncio.run(play_sound_async(file_name, loop)) + else: + play_sound_sync(file_name, loop) + +def test_sound_playing(): + """ + Example function testing sound player + """ + start_sound("clear.wav", True, True) + +if __name__ == "__main__": + test_sound_playing() From 2271c8fcb604f0ce68398250b7c58d870c63a035 Mon Sep 17 00:00:00 2001 From: Nima Samadi Date: Sat, 24 Jun 2023 18:05:07 +0330 Subject: [PATCH 02/16] Test nava play --- test/__init__.py | 0 {dev => test}/clear.wav | Bin test/main.py | 3 +++ 3 files changed, 3 insertions(+) create mode 100644 test/__init__.py rename {dev => test}/clear.wav (100%) create mode 100644 test/main.py diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dev/clear.wav b/test/clear.wav similarity index 100% rename from dev/clear.wav rename to test/clear.wav diff --git a/test/main.py b/test/main.py new file mode 100644 index 0000000..696ca32 --- /dev/null +++ b/test/main.py @@ -0,0 +1,3 @@ +import nava.functions as nvf + +nvf.play("test/clear.wav") From 1310e1d31ba08b692dd101af624155fe7f599a89 Mon Sep 17 00:00:00 2001 From: Nima Samadi Date: Sun, 25 Jun 2023 12:20:25 +0330 Subject: [PATCH 03/16] Add async and loop feature --- .gitignore | 2 ++ nava/functions.py | 50 +++++++++++++++++++++++++++++++++++++++-------- test/main.py | 2 +- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 73358ad..664f91e 100644 --- a/.gitignore +++ b/.gitignore @@ -137,3 +137,5 @@ dmypy.json # Cython debug symbols cython_debug/ +# Dev realated files +dev/ diff --git a/nava/functions.py b/nava/functions.py index ea204f1..f3ea174 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -5,6 +5,7 @@ import os import shlex from functools import wraps +import asyncio from .params import OVERVIEW from .params import SOUND_FILE_PLAY_ERROR, SOUND_FILE_EXIST_ERROR @@ -60,7 +61,7 @@ def __play_win(sound_path): @quote -def __play_linux(sound_path): +def __play_linux(sound_path, loop=False, is_async=True): """ Play sound in Linux. @@ -68,12 +69,41 @@ def __play_linux(sound_path): :type sound_path: str :return: None """ + if is_async: + if loop: + while True: + asyncio.run(__play_async_linx(sound_path)) + else: + asyncio.run(__play_async_linx(sound_path)) + else: + if loop: + while True: + __play_sync_linux(sound_path) + else: + __play_sync_linux(sound_path) + +def __play_sync_linux(sound_path): + """ + Play sound synch in Linux + """ _ = subprocess.check_call(["aplay", - sound_path], - shell=False, - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) + sound_path], + shell=False, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + +async def __play_async_linx(sound_path): + """ + Play sound async in Linux + """ + play_cmd = f"aplay {sound_path}" + proc = await asyncio.subprocess.create_subprocess_shell( + play_cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await proc.communicate() @quote @@ -122,12 +152,16 @@ def path_checker(sound_path, *args, **kwargs): @path_check -def play(sound_path): +def play(sound_path, loop=False, is_async=True): """ Play sound. :param sound_path: sound path :type sound_path: str + :param loop: play sound on loop (False by default) + :type loop: bool + :param is_async: play synchronously or asynchronously (True by default) + :type is_async: bool :return: None """ try: @@ -137,6 +171,6 @@ def play(sound_path): elif sys_platform == "darwin": __play_mac(sound_path) else: - __play_linux(sound_path) + __play_linux(sound_path, loop, is_async) except Exception: raise NavaBaseError(SOUND_FILE_PLAY_ERROR) diff --git a/test/main.py b/test/main.py index 696ca32..4f2b91c 100644 --- a/test/main.py +++ b/test/main.py @@ -1,3 +1,3 @@ import nava.functions as nvf -nvf.play("test/clear.wav") +nvf.play("test/clear.wav", is_async=False, loop=False) From 2a3c6b326c6ad0d5cb17ec60b026b208592a0b84 Mon Sep 17 00:00:00 2001 From: Nima Samadi Date: Mon, 10 Jul 2023 21:54:36 +0330 Subject: [PATCH 04/16] Fix async bugs - Update documentation and test script * Async bugs: call the play function async from test script * Test script: more intuitive example of test script --- .gitignore | 3 -- dev/playsound.py | 80 -------------------------------------------- dev/test.py | 84 ----------------------------------------------- nava/functions.py | 42 +++++++++++++----------- test/main.py | 47 ++++++++++++++++++++++++-- 5 files changed, 67 insertions(+), 189 deletions(-) delete mode 100644 dev/playsound.py delete mode 100644 dev/test.py diff --git a/.gitignore b/.gitignore index 664f91e..a81c8ee 100644 --- a/.gitignore +++ b/.gitignore @@ -136,6 +136,3 @@ dmypy.json # Cython debug symbols cython_debug/ - -# Dev realated files -dev/ diff --git a/dev/playsound.py b/dev/playsound.py deleted file mode 100644 index 98cba6e..0000000 --- a/dev/playsound.py +++ /dev/null @@ -1,80 +0,0 @@ -# simpleAudio.py -# very basic audio file player without external modules -# for Windows and Mac in Python. Only plays wav files -# on Windows, seems to work with wav, mp3 and other formats on Macs. - -# Provides two functions: -# startSound(filename, async=True, loop=True) -# stopSound() - -import sys, platform, subprocess, time, threading - -def checkForOS(*terms): - platformString = (sys.platform + platform.platform()).lower() - for term in terms: - if (term in platformString): return True - return False -osIsWindows = checkForOS("win32", "win64", "windows") -osIsLinux = checkForOS("linux") - -if (osIsWindows == True): - import winsound - def startSound(filename, async=True, loop=True): - flags = winsound.SND_FILENAME - if (async == True): flags |= winsound.SND_ASYNC - if (loop == True): flags |= winsound.SND_LOOP - winsound.PlaySound(filename, flags) - def stopSound(): - winsound.PlaySound(None, 0) -else: - # For now, just do Mac OS (sorry, Linux...) - soundProcesses = [ ] - soundThreads = [ ] - soundThreadCounter = 0 - def startSound(filename, async=True, loop=True): - if (async == True): startAsyncSound(filename, loop) - else: startSyncSound(filename, loop) - def startSyncSound(filename, loop, thread=None): - while True: - app = "aplay" if osIsLinux else "afplay" - p = subprocess.Popen([app, filename]) - soundProcesses.append(p) - p.wait() - if (p in soundProcesses): soundProcesses.remove(p) - if (loop == False): break - if ((thread != None) and (thread not in soundThreads)): break - def startAsyncSound(filename, loop): - global soundThreadCounter - tc = soundThreadCounter - soundThreadCounter += 1 - soundThreads.append(tc) - thread = threading.Thread(target=startSyncSound, - args=(filename,loop,tc)) - thread.daemon = True - thread.start() - def stopSound(): - global soundThreads - soundThreads = [ ] - while (soundProcesses != [ ]): - try: soundProcesses.pop().terminate() - except: pass - -##################################### -# Simple test -##################################### -""" -def testSoundPlaying(): - print "starting sound!...", - startSound("tetris.wav", async=True, loop=True) - print "done!" - - print "sleeping for 4 seconds...", - time.sleep(4) - print "done!" - - print "stopping sound...", - stopSound() - print "done!" - -testSoundPlaying() -""" \ No newline at end of file diff --git a/dev/test.py b/dev/test.py deleted file mode 100644 index cf44dbf..0000000 --- a/dev/test.py +++ /dev/null @@ -1,84 +0,0 @@ -import asyncio -import subprocess - - -async def count(): - """ - Example function to test async play - - """ - while True: - print("Hi!") - await asyncio.sleep(1) - -async def play_sound_async(file_name, loop = True): - """ - Play sound asynchronously - - :param file_name: input sound file name - :type file_name: str - :param loop: play sound on loop - :type: bool - :return: None - """ - task = asyncio.create_task(count()) - cmd = f"aplay {file_name}" - while True: - proc = await asyncio.subprocess.create_subprocess_shell( - cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - - stdout, stderr = await proc.communicate() - - if not loop: - task.cancel() - break - -def play_sound_sync(file_name, loop = True): - """ - Play sound synchronously - - :param file_name: input sound file name - :type file_name: str - :param loop: play sound on loop - :type: bool - :return: None - """ - while True: - _ = subprocess.check_call(["aplay", - file_name], - shell=False, - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) - if not loop: - break - - -def start_sound(file_name: str, is_async: bool = True, loop: bool = True) -> None: - """ - Start playing given sound - - :file_name: sound file name to play - :type: str - :is_async: play sound synchronously or asynchronously - :type: bool - :loop: play sound on loop - :type: bool - :return: None - """ - if is_async: - asyncio.run(play_sound_async(file_name, loop)) - else: - play_sound_sync(file_name, loop) - -def test_sound_playing(): - """ - Example function testing sound player - """ - start_sound("clear.wav", True, True) - -if __name__ == "__main__": - test_sound_playing() diff --git a/nava/functions.py b/nava/functions.py index f3ea174..f507bb4 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -61,30 +61,29 @@ def __play_win(sound_path): @quote -def __play_linux(sound_path, loop=False, is_async=True): +async def __play_linux(sound_path, is_async=True): """ Play sound in Linux. - :param sound_path: sound path + :param sound_path: sound path to be played :type sound_path: str + :param is_async: play async or not + :type is_async: bool :return: None """ if is_async: - if loop: - while True: - asyncio.run(__play_async_linx(sound_path)) - else: - asyncio.run(__play_async_linx(sound_path)) + task = asyncio.create_task(__play_async_linux(sound_path)) + await task else: - if loop: - while True: - __play_sync_linux(sound_path) - else: - __play_sync_linux(sound_path) + __play_sync_linux(sound_path) + def __play_sync_linux(sound_path): """ - Play sound synch in Linux + Play sound synchronously in Linux + :param sound_path: sound path to be played + :type sound_path: str + :return: None """ _ = subprocess.check_call(["aplay", sound_path], @@ -93,9 +92,13 @@ def __play_sync_linux(sound_path): stdin=subprocess.PIPE, stdout=subprocess.PIPE) -async def __play_async_linx(sound_path): + +async def __play_async_linux(sound_path): """ - Play sound async in Linux + Play sound asynchronously in Linux + :param sound_path: sound path to be played + :type sound_path: str + :return: None """ play_cmd = f"aplay {sound_path}" proc = await asyncio.subprocess.create_subprocess_shell( @@ -152,15 +155,13 @@ def path_checker(sound_path, *args, **kwargs): @path_check -def play(sound_path, loop=False, is_async=True): +async def play(sound_path, is_async=True): """ Play sound. :param sound_path: sound path :type sound_path: str - :param loop: play sound on loop (False by default) - :type loop: bool - :param is_async: play synchronously or asynchronously (True by default) + :param is_async: play synchronously or asynchronously (async by default) :type is_async: bool :return: None """ @@ -171,6 +172,7 @@ def play(sound_path, loop=False, is_async=True): elif sys_platform == "darwin": __play_mac(sound_path) else: - __play_linux(sound_path, loop, is_async) + task = asyncio.create_task(__play_linux(sound_path, is_async)) + await task except Exception: raise NavaBaseError(SOUND_FILE_PLAY_ERROR) diff --git a/test/main.py b/test/main.py index 4f2b91c..571c407 100644 --- a/test/main.py +++ b/test/main.py @@ -1,3 +1,46 @@ -import nava.functions as nvf +import asyncio +import nava.functions as nv -nvf.play("test/clear.wav", is_async=False, loop=False) + +async def test_print(): + """ + Simple function printing hello world async + + :return: None + """ + while True: + print("Hello, World!") + await asyncio.sleep(0.1) + +async def test_play(): + """ + Simple function playing sound async (must provide path to file) + + :return: None + """ + play_task = asyncio.create_task(nv.play("test/clear.wav", is_async=True)) + print_task = asyncio.create_task(test_print()) + + await play_task + +if __name__ == "__main__": + asyncio.run(test_play()) + """ + How to run? Make sure you're in the project root + directory. Then, pass the script to Python interpreter + like below: + > python -m test.main + The result in async mode with 0.1 second delay is as follows: + Hello, World! + Hello, World! + Hello, World! + Hello, World! + Hello, World! + Hello, World! + Hello, World! + Hello, World! + Hello, World! + + In the meantime you should be able to hear the sound. The actual + number of hello worlds depend on the voice length and the delay value. + """ From de92990dbc2c5585c5047b56f3b99c0ebade901f Mon Sep 17 00:00:00 2001 From: Nima Samadi Date: Mon, 10 Jul 2023 22:01:52 +0330 Subject: [PATCH 05/16] Remove clear.wav - Fix documentation spacing issue --- nava/functions.py | 2 ++ test/clear.wav | Bin 15058 -> 0 bytes 2 files changed, 2 insertions(+) delete mode 100644 test/clear.wav diff --git a/nava/functions.py b/nava/functions.py index f507bb4..0883d0e 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -81,6 +81,7 @@ async def __play_linux(sound_path, is_async=True): def __play_sync_linux(sound_path): """ Play sound synchronously in Linux + :param sound_path: sound path to be played :type sound_path: str :return: None @@ -96,6 +97,7 @@ def __play_sync_linux(sound_path): async def __play_async_linux(sound_path): """ Play sound asynchronously in Linux + :param sound_path: sound path to be played :type sound_path: str :return: None diff --git a/test/clear.wav b/test/clear.wav deleted file mode 100644 index f825b257633d6ca6bc97f1e05c3b18eb3f248cc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15058 zcmeHNv5wp}5M6gE^9eydBV7s`NSnfGT$};HV8H+^RH#rvfQ1SbTe$ESgnwyujAl$fm)8ZO(t+Cj`6fY)77pOlZKrLik0eZ{>YC1YtRxmkK$jcgd7jg7Eg zfl^l}jp#yQMANo5Zbs5(AqB%bNx%1uLGxqU9Hwv-4(MX4>^u;B2lQdMC-g61HCalT z>k54bwQ#boBS8R*MM#Qp!$2nh5dXjdP^dN1ZRG1<2x$(8+V>h55lH|O5DpwWEK48< z3qTIJh=6`@AtKOZ{e+ew7y&ES&&IH^05*q&I=;+aXOn^|_FlZ0L_rY&EP7eVDcSax zOSa973L8`k!}8K#+Cc#xvq*Hdj3jfj-h+*7o#N9xQLqgnF;FQT;QoM94wW#nS?(iC z94@ly&FkvxLbqkqPmlArW4038^l9&qCMWOy--0SuSB$A-4VAgf@;H<m^uT_~9?@XHi-4?BRwCC@FYrYYuf0w8^paz;4zh5r zJ5$mK*SUv!xWU~B*SQBnw^pm`?D4zMY@39W&^a$Bov->3e)`M9=InyY4!UEx)p>ON z#VFI}QnrW74Lww*hiZW@5BHL_j2*h*J5rafQMSIyQ0wQ}iRN0Fa7}4-2C`jslPcrC zcOom1b%PVdt63*@n{c~=g{qmI2D^>>Nhc1vaHMQ~R;gC(BO$xr3Q$(Gh_~`zH?DH$ zX8NGZ_53B{*LnSHd#NeeE@@1>p6rCAR;n}Y;YIwZ9bEyF5Z`JOuRX?6lacX zby?nP($zIpl{_p}SEo}S@tIP2rM#y+mUluh{uW{$D-h4z_7BJk;YZQOqidx6r1%>Pka?#zpCqtLI`lA?_HQ`5`^QZ7 zPt$P8IybR*Q-1SkZRX>P1$z(2i?*N9+2cDyOizy>&#!LA2^i~?8~qI$*NR5#CSCi? zHnJELYE3O!`)1f~yf6GSGY$?)`?cgBcvu)qM<9FLN(<_(v(-HlN~eQN&xH?$pu>!d z7YG^F&AWJKd7}dwTmtZ>7tfGG3~$cy|jg-2ep@ zvFW}>G*vcRemO(CBVqt&Pkn34Vqgpzr+WL!54j3MJ{_BFuOWFAj Date: Mon, 10 Jul 2023 22:03:36 +0330 Subject: [PATCH 06/16] Fix period issue in documentations --- nava/functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index 0883d0e..9b7bda4 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -80,7 +80,7 @@ async def __play_linux(sound_path, is_async=True): def __play_sync_linux(sound_path): """ - Play sound synchronously in Linux + Play sound synchronously in Linux. :param sound_path: sound path to be played :type sound_path: str @@ -96,8 +96,8 @@ def __play_sync_linux(sound_path): async def __play_async_linux(sound_path): """ - Play sound asynchronously in Linux - + Play sound asynchronously in Linux. + :param sound_path: sound path to be played :type sound_path: str :return: None From 92ffec15fe353da144afa89a5fc8b96748ad0f39 Mon Sep 17 00:00:00 2001 From: Sadra Sabouri Date: Tue, 11 Jul 2023 12:33:10 +0330 Subject: [PATCH 07/16] remove : extra tests removed. --- test/__init__.py | 0 test/main.py | 46 ---------------------------------------------- 2 files changed, 46 deletions(-) delete mode 100644 test/__init__.py delete mode 100644 test/main.py diff --git a/test/__init__.py b/test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/main.py b/test/main.py deleted file mode 100644 index 571c407..0000000 --- a/test/main.py +++ /dev/null @@ -1,46 +0,0 @@ -import asyncio -import nava.functions as nv - - -async def test_print(): - """ - Simple function printing hello world async - - :return: None - """ - while True: - print("Hello, World!") - await asyncio.sleep(0.1) - -async def test_play(): - """ - Simple function playing sound async (must provide path to file) - - :return: None - """ - play_task = asyncio.create_task(nv.play("test/clear.wav", is_async=True)) - print_task = asyncio.create_task(test_print()) - - await play_task - -if __name__ == "__main__": - asyncio.run(test_play()) - """ - How to run? Make sure you're in the project root - directory. Then, pass the script to Python interpreter - like below: - > python -m test.main - The result in async mode with 0.1 second delay is as follows: - Hello, World! - Hello, World! - Hello, World! - Hello, World! - Hello, World! - Hello, World! - Hello, World! - Hello, World! - Hello, World! - - In the meantime you should be able to hear the sound. The actual - number of hello worlds depend on the voice length and the delay value. - """ From b878be68eac43bd8f8e9609c59411681b9e48882 Mon Sep 17 00:00:00 2001 From: Sadra Sabouri Date: Tue, 11 Jul 2023 17:39:08 +0330 Subject: [PATCH 08/16] add : async functions added. --- nava/functions.py | 101 ++++++++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index 2c640f6..154738d 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -60,55 +60,51 @@ def __play_win(sound_path): winsound.PlaySound(sound_path, winsound.SND_FILENAME) -@quote -async def __play_linux(sound_path, is_async=True): +async def __play_win_async(sound_path): """ - Play sound in Linux. + Play sound in asynchronously Windows. - :param sound_path: sound path to be played + :param sound_path: sound path :type sound_path: str - :param is_async: play async or not - :type is_async: bool :return: None """ - if is_async: - task = asyncio.create_task(__play_async_linux(sound_path)) - await task - else: - __play_sync_linux(sound_path) + import winsound + await winsound.PlaySound(sound_path, winsound.SND_FILENAME | winsound.SND_ASYNC) -def __play_sync_linux(sound_path): +@quote +def __play_linux(sound_path): """ - Play sound synchronously in Linux. + Play sound in Linux. - :param sound_path: sound path to be played + :param sound_path: sound path :type sound_path: str :return: None """ _ = subprocess.check_call(["aplay", - sound_path], - shell=False, - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) + sound_path], + shell=False, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) -async def __play_async_linux(sound_path): +@quote +async def __play_linux_async(sound_path): """ Play sound asynchronously in Linux. - :param sound_path: sound path to be played + :param sound_path: sound path :type sound_path: str :return: None """ - play_cmd = f"aplay {sound_path}" - proc = await asyncio.subprocess.create_subprocess_shell( - play_cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await proc.communicate() + proc = await asyncio.subprocess.create_subprocess_exec("aplay", + sound_path, + shell=False, + stderr=asyncio.subprocess.PIPE, + stdin=asyncio.subprocess.PIPE, + stdout=asyncio.subprocess.PIPE) + await proc.communicate() @quote @@ -128,6 +124,24 @@ def __play_mac(sound_path): stdout=subprocess.PIPE) +@quote +async def __play_mac_async(sound_path): + """ + Play sound asynchronously in macOS. + + :param sound_path: sound path + :type sound_path: str + :return: None + """ + proc = await asyncio.subprocess.create_subprocess_exec("afplay", + sound_path, + shell=False, + stderr=asyncio.subprocess.PIPE, + stdin=asyncio.subprocess.PIPE, + stdout=asyncio.subprocess.PIPE) + await proc.communicate() + + def path_check(func): """ Check the given path to be a string and a valid file directory. @@ -157,14 +171,34 @@ def path_checker(sound_path, *args, **kwargs): @path_check -async def play(sound_path, is_async=True): +async def play_async(sound_path): + """ + Play sound. + + :param sound_path: sound path + :type sound_path: str + :return: None + """ + try: + sys_platform = sys.platform + if sys_platform == "win32": + task = asyncio.create_task(__play_win_async(sound_path)) + elif sys_platform == "darwin": + task = asyncio.create_task(__play_mac_async(sound_path)) + else: + task = asyncio.create_task(__play_linux_async(sound_path)) + await task + except Exception: # pragma: no cover + raise NavaBaseError(SOUND_FILE_PLAY_ERROR) + + +@path_check +def play(sound_path): """ Play sound. :param sound_path: sound path :type sound_path: str - :param is_async: play synchronously or asynchronously (async by default) - :type is_async: bool :return: None """ try: @@ -174,7 +208,6 @@ async def play(sound_path, is_async=True): elif sys_platform == "darwin": __play_mac(sound_path) else: - task = asyncio.create_task(__play_linux(sound_path, is_async)) - await task - except Exception: # pragma: no cover + __play_linux(sound_path) + except Exception: # pragma: no cover raise NavaBaseError(SOUND_FILE_PLAY_ERROR) From bcb5fa84e81fefc4ed68b1650e50aafac2b19ba7 Mon Sep 17 00:00:00 2001 From: Sadra Sabouri Date: Tue, 11 Jul 2023 17:39:31 +0330 Subject: [PATCH 09/16] add : `play_async` added to `__init__.py`. --- nava/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nava/__init__.py b/nava/__init__.py index 3f535d0..3c89119 100644 --- a/nava/__init__.py +++ b/nava/__init__.py @@ -2,6 +2,6 @@ """Nava modules.""" from .params import NAVA_VERSION from .errors import NavaBaseError -from .functions import play +from .functions import play, play_async __version__ = NAVA_VERSION From d6a9e38f27b0de5d38bae805b83205694476c104 Mon Sep 17 00:00:00 2001 From: Sadra Sabouri Date: Tue, 11 Jul 2023 17:39:48 +0330 Subject: [PATCH 10/16] test : tests added. --- test/function_test.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/test/function_test.py b/test/function_test.py index ce58a04..8f3d18b 100644 --- a/test/function_test.py +++ b/test/function_test.py @@ -1,8 +1,24 @@ # -*- coding: utf-8 -*- """ >>> import os ->>> from nava import play ->>> play(os.path.join("others", "test.wav")) +>>> import asyncio +>>> from time import time +>>> from nava import play, play_async +>>> PATH2TEST_WAV = os.path.join("others", "test.wav") +>>> play(PATH2TEST_WAV) +>>> async def test_print(): +... start_time = time() +... while True: +... print("Test voice is in {:.1f}s".format(time() - start_time)) +... await asyncio.sleep(0.1) +>>> async def play_async_test(): +... asyncio.create_task(test_print()) +... await asyncio.create_task(play_async(PATH2TEST_WAV)) +>>> asyncio.run(play_async_test()) +Test voice is in 0.0s +Test voice is in 0.1s +Test voice is in 0.2s +Test voice is in 0.3s >>> from nava.functions import nava_help >>> nava_help() From eb70ea8ede7c97fd32f246c84cc0123b185e17b8 Mon Sep 17 00:00:00 2001 From: Sadra Sabouri Date: Tue, 11 Jul 2023 17:40:00 +0330 Subject: [PATCH 11/16] log : changes logged. --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5cc63a..216e24f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- `play_async` function +- `__play_win_async` function +- `__play_linux_async` function +- `__play_mac_async` function ## [0.2] - 2023-07-10 ### Added - Logo From 89d1550430f246f219984cd3519b7f03d6b9b819 Mon Sep 17 00:00:00 2001 From: Sadra Sabouri Date: Tue, 11 Jul 2023 17:40:16 +0330 Subject: [PATCH 12/16] update : `README.md` async usage section updated. --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index e381355..20796c8 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,22 @@ from nava import play play("alarm.wav") ``` +### Async +```python +import asyncio +from nava import play_async + +async def my_task(): + # the task you wanted to run it asynchronously while sound is playing. + pass + +async def main(): + asyncio.create_task(my_task()) + await asyncio.create_task(play_async("alarm.wav")) + +asyncio.run(main()) +``` + ### Error ```python From 4cfa1616c41f67a233c77f92ba3153384bf4215e Mon Sep 17 00:00:00 2001 From: Sadra Sabouri Date: Tue, 11 Jul 2023 18:04:11 +0330 Subject: [PATCH 13/16] fix : windows async issue fixed. --- nava/functions.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/nava/functions.py b/nava/functions.py index 154738d..9a2c2b6 100644 --- a/nava/functions.py +++ b/nava/functions.py @@ -60,7 +60,7 @@ def __play_win(sound_path): winsound.PlaySound(sound_path, winsound.SND_FILENAME) -async def __play_win_async(sound_path): +def __play_win_async(sound_path): """ Play sound in asynchronously Windows. @@ -69,7 +69,7 @@ async def __play_win_async(sound_path): :return: None """ import winsound - await winsound.PlaySound(sound_path, winsound.SND_FILENAME | winsound.SND_ASYNC) + winsound.PlaySound(sound_path, winsound.SND_FILENAME | winsound.SND_ASYNC) @quote @@ -182,12 +182,11 @@ async def play_async(sound_path): try: sys_platform = sys.platform if sys_platform == "win32": - task = asyncio.create_task(__play_win_async(sound_path)) + __play_win_async(sound_path) elif sys_platform == "darwin": - task = asyncio.create_task(__play_mac_async(sound_path)) + await asyncio.create_task(__play_mac_async(sound_path)) else: - task = asyncio.create_task(__play_linux_async(sound_path)) - await task + await asyncio.create_task(__play_linux_async(sound_path)) except Exception: # pragma: no cover raise NavaBaseError(SOUND_FILE_PLAY_ERROR) From 0a52f58ffe908cdfcbcb6e77c64b86e5b3216165 Mon Sep 17 00:00:00 2001 From: Sadra Sabouri Date: Tue, 11 Jul 2023 18:05:39 +0330 Subject: [PATCH 14/16] update : tests updated. --- test/function_test.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/test/function_test.py b/test/function_test.py index 8f3d18b..3fffe20 100644 --- a/test/function_test.py +++ b/test/function_test.py @@ -6,19 +6,13 @@ >>> from nava import play, play_async >>> PATH2TEST_WAV = os.path.join("others", "test.wav") >>> play(PATH2TEST_WAV) ->>> async def test_print(): -... start_time = time() +>>> async def sleep_test(): ... while True: -... print("Test voice is in {:.1f}s".format(time() - start_time)) ... await asyncio.sleep(0.1) >>> async def play_async_test(): -... asyncio.create_task(test_print()) +... asyncio.create_task(sleep_test()) ... await asyncio.create_task(play_async(PATH2TEST_WAV)) >>> asyncio.run(play_async_test()) -Test voice is in 0.0s -Test voice is in 0.1s -Test voice is in 0.2s -Test voice is in 0.3s >>> from nava.functions import nava_help >>> nava_help() From e0940bb926170b57ce9f34c5684a09c887dd322b Mon Sep 17 00:00:00 2001 From: Sadra Sabouri Date: Tue, 11 Jul 2023 18:11:46 +0330 Subject: [PATCH 15/16] drop : Python 3.6 dropped due to (https://stackoverflow.com/questions/52796630/python3-6-attributeerror-module-asyncio-has-no-attribute-run). --- .github/workflows/linux_test.yml | 2 +- .github/workflows/macOS_test.yml | 2 +- .github/workflows/windows_test.yml | 2 +- others/meta.yaml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/linux_test.yml b/.github/workflows/linux_test.yml index 38a723a..3480145 100644 --- a/.github/workflows/linux_test.yml +++ b/.github/workflows/linux_test.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04] - python-version: [3.6, 3.7, 3.8, 3.9, 3.10.0, 3.11.0] + python-version: [3.7, 3.8, 3.9, 3.10.0, 3.11.0] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/macOS_test.yml b/.github/workflows/macOS_test.yml index d737539..9b4f436 100644 --- a/.github/workflows/macOS_test.yml +++ b/.github/workflows/macOS_test.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [macOS-12, macOS-11] - python-version: [3.6, 3.7, 3.8, 3.9, 3.10.0, 3.11.0] + python-version: [3.7, 3.8, 3.9, 3.10.0, 3.11.0] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/windows_test.yml b/.github/workflows/windows_test.yml index c48ed48..31153db 100644 --- a/.github/workflows/windows_test.yml +++ b/.github/workflows/windows_test.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [windows-2022, windows-2019] - python-version: [3.6, 3.7, 3.8, 3.9, 3.10.0, 3.11.0] + python-version: [3.7, 3.8, 3.9, 3.10.0, 3.11.0] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/others/meta.yaml b/others/meta.yaml index 14bdca8..79b6d34 100644 --- a/others/meta.yaml +++ b/others/meta.yaml @@ -15,10 +15,10 @@ requirements: host: - pip - setuptools - - python >=3.6 + - python >=3.7 run: - art >=1.8 - - python >=3.6 + - python >=3.7 about: home: https://github.com/openscilab/nava license: MIT From d00bd1fd005cb224b81f9381e105a6af0e559241 Mon Sep 17 00:00:00 2001 From: Sadra Sabouri Date: Tue, 11 Jul 2023 18:14:59 +0330 Subject: [PATCH 16/16] fix : Python 3.6 dropeed. --- CHANGELOG.md | 2 ++ setup.py | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 216e24f..3d092dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `__play_win_async` function - `__play_linux_async` function - `__play_mac_async` function +### Changed +- Python 3.6 support dropped. ## [0.2] - 2023-07-10 ### Added - Logo diff --git a/setup.py b/setup.py index a823ceb..5a740b5 100644 --- a/setup.py +++ b/setup.py @@ -47,14 +47,13 @@ def read_description(): 'Discord': 'https://discord.gg/MCbPKCFBs3', }, install_requires=get_requires(), - python_requires='>=3.6', + python_requires='>=3.7', classifiers=[ 'Development Status :: 2 - Pre-Alpha', 'Intended Audience :: Developers', 'Natural Language :: English', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9',