reachy-mini 1.2.5rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reachy_mini/__init__.py +4 -0
- reachy_mini/apps/__init__.py +25 -0
- reachy_mini/apps/app.py +258 -0
- reachy_mini/apps/assistant.py +787 -0
- reachy_mini/apps/manager.py +312 -0
- reachy_mini/apps/sources/__init__.py +4 -0
- reachy_mini/apps/sources/hf_space.py +106 -0
- reachy_mini/apps/sources/local_common_venv.py +567 -0
- reachy_mini/apps/templates/README.md.j2 +12 -0
- reachy_mini/apps/templates/gitignore.j2 +3 -0
- reachy_mini/apps/templates/index.html.j2 +40 -0
- reachy_mini/apps/templates/main.py.j2 +74 -0
- reachy_mini/apps/templates/pyproject.toml.j2 +28 -0
- reachy_mini/apps/templates/static/index.html.j2 +27 -0
- reachy_mini/apps/templates/static/main.js.j2 +47 -0
- reachy_mini/apps/templates/static/style.css.j2 +25 -0
- reachy_mini/apps/templates/style.css.j2 +411 -0
- reachy_mini/apps/utils.py +30 -0
- reachy_mini/assets/config/hardware_config.yaml +164 -0
- reachy_mini/assets/confused1.wav +0 -0
- reachy_mini/assets/count.wav +0 -0
- reachy_mini/assets/dance1.wav +0 -0
- reachy_mini/assets/firmware/CHANGELOG.md +15 -0
- reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.2.bin +0 -0
- reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.3.bin +0 -0
- reachy_mini/assets/firmware/update.sh +9 -0
- reachy_mini/assets/go_sleep.wav +0 -0
- reachy_mini/assets/impatient1.wav +0 -0
- reachy_mini/assets/kinematics_data.json +253 -0
- reachy_mini/assets/models/fknetwork.dynamic.onnx +0 -0
- reachy_mini/assets/models/fknetwork.onnx +0 -0
- reachy_mini/assets/models/fknetwork_int8.onnx +0 -0
- reachy_mini/assets/models/iknetwork.onnx +0 -0
- reachy_mini/assets/wake_up.wav +0 -0
- reachy_mini/daemon/__init__.py +1 -0
- reachy_mini/daemon/app/__init__.py +1 -0
- reachy_mini/daemon/app/bg_job_register.py +142 -0
- reachy_mini/daemon/app/dashboard/static/assets/KO-cartoon-static.svg +40 -0
- reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon-static.svg +42 -0
- reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/go-to-sleep-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon-static.svg +50 -0
- reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-awake.svg +18 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-connection-lost-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-go-to-sleep-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko.svg +22 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping-static.svg +41 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping.svg +18 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-wake-up-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/css/app.css +1433 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Archivo-Italic-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Archivo-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Asap-Italic-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Asap-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/js/3rdparty/gstwebrtc-api-2.0.0.min.js +5 -0
- reachy_mini/daemon/app/dashboard/static/js/apps.js +323 -0
- reachy_mini/daemon/app/dashboard/static/js/appstore.js +167 -0
- reachy_mini/daemon/app/dashboard/static/js/daemon.js +169 -0
- reachy_mini/daemon/app/dashboard/static/js/health_check.js +9 -0
- reachy_mini/daemon/app/dashboard/static/js/move_player.js +132 -0
- reachy_mini/daemon/app/dashboard/static/js/notification.js +41 -0
- reachy_mini/daemon/app/dashboard/static/js/update.js +132 -0
- reachy_mini/daemon/app/dashboard/static/js/volume_control.js +252 -0
- reachy_mini/daemon/app/dashboard/static/js/wifi.js +176 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/README.md +15 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/package-lock.json +1127 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/package.json +6 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/pnpm-lock.yaml +608 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/postcss.config.js +6 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/styles/app.css +185 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/tailwind.config.js +11 -0
- reachy_mini/daemon/app/dashboard/templates/base.html +21 -0
- reachy_mini/daemon/app/dashboard/templates/index.html +20 -0
- reachy_mini/daemon/app/dashboard/templates/sections/apps.html +8 -0
- reachy_mini/daemon/app/dashboard/templates/sections/appstore.html +35 -0
- reachy_mini/daemon/app/dashboard/templates/sections/daemon.html +45 -0
- reachy_mini/daemon/app/dashboard/templates/sections/move_player.html +121 -0
- reachy_mini/daemon/app/dashboard/templates/sections/notification.html +19 -0
- reachy_mini/daemon/app/dashboard/templates/sections/update.html +54 -0
- reachy_mini/daemon/app/dashboard/templates/sections/wifi.html +46 -0
- reachy_mini/daemon/app/dashboard/templates/settings.html +14 -0
- reachy_mini/daemon/app/dependencies.py +41 -0
- reachy_mini/daemon/app/main.py +469 -0
- reachy_mini/daemon/app/models.py +149 -0
- reachy_mini/daemon/app/routers/apps.py +114 -0
- reachy_mini/daemon/app/routers/daemon.py +85 -0
- reachy_mini/daemon/app/routers/kinematics.py +57 -0
- reachy_mini/daemon/app/routers/motors.py +41 -0
- reachy_mini/daemon/app/routers/move.py +265 -0
- reachy_mini/daemon/app/routers/state.py +146 -0
- reachy_mini/daemon/app/routers/update.py +86 -0
- reachy_mini/daemon/app/routers/volume.py +423 -0
- reachy_mini/daemon/app/routers/wifi_config.py +219 -0
- reachy_mini/daemon/app/services/__init__.py +1 -0
- reachy_mini/daemon/app/services/bluetooth/bluetooth_service.py +796 -0
- reachy_mini/daemon/app/services/bluetooth/commands/HOTSPOT.sh +7 -0
- reachy_mini/daemon/app/services/bluetooth/commands/RESTART_DAEMON.sh +4 -0
- reachy_mini/daemon/app/services/bluetooth/commands/SOFTWARE_RESET.sh +7 -0
- reachy_mini/daemon/app/services/bluetooth/install_service_bluetooth.sh +39 -0
- reachy_mini/daemon/app/services/gpio_shutdown/__init__.py +1 -0
- reachy_mini/daemon/app/services/gpio_shutdown/install_service.sh +33 -0
- reachy_mini/daemon/app/services/gpio_shutdown/launcher.sh +3 -0
- reachy_mini/daemon/app/services/gpio_shutdown/shutdown_monitor.py +28 -0
- reachy_mini/daemon/app/services/wireless/generate_asoundrc.sh +70 -0
- reachy_mini/daemon/app/services/wireless/install_service.sh +33 -0
- reachy_mini/daemon/app/services/wireless/launcher.sh +5 -0
- reachy_mini/daemon/backend/__init__.py +1 -0
- reachy_mini/daemon/backend/abstract.py +815 -0
- reachy_mini/daemon/backend/mujoco/__init__.py +36 -0
- reachy_mini/daemon/backend/mujoco/backend.py +395 -0
- reachy_mini/daemon/backend/mujoco/utils.py +59 -0
- reachy_mini/daemon/backend/mujoco/video_udp.py +57 -0
- reachy_mini/daemon/backend/robot/__init__.py +8 -0
- reachy_mini/daemon/backend/robot/backend.py +597 -0
- reachy_mini/daemon/daemon.py +651 -0
- reachy_mini/daemon/utils.py +116 -0
- reachy_mini/descriptions/reachy_mini/mjcf/additional.xml +6 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/5w_speaker.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/5w_speaker.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_body_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_body_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_l_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_l_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_r_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_r_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_interface_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_interface_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/big_lens_d40.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/big_lens_d40.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_down_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_down_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_foot_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_foot_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_top_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_top_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_turning_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_turning_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bts2_m2_6x8.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bts2_m2_6x8.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/README.m +1 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/croissant_1k.obj +5021 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/textures/croissant_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_b_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_b_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_f_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_f_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_m_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_m_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_horn_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_horn_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_led_cap2_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_led_cap2_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/food_apple_01_1k.obj +18045 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/textures/food_apple_01_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/glasses_dolder_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/glasses_dolder_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_back_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_back_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_front_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_front_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_mic_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_mic_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d30_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d30_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d40_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d40_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/m12_fisheye_lens_1_8mm.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/m12_fisheye_lens_1_8mm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/mp01062_stewart_arm_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/mp01062_stewart_arm_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/neck_reference_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/neck_reference_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_2.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/pp01102_arducam_carter.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/pp01102_arducam_carter.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/rubber_duck_toy_1k.obj +8940 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/textures/rubber_duck_toy_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/small_lens_d30.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/small_lens_d30.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball__2.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball__2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_main_plate_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_main_plate_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_tricap_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_tricap_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/textures/wooden_table_02_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/wooden_table_02_1k.obj +485 -0
- reachy_mini/descriptions/reachy_mini/mjcf/config.json +21 -0
- reachy_mini/descriptions/reachy_mini/mjcf/joints_properties.xml +29 -0
- reachy_mini/descriptions/reachy_mini/mjcf/reachy_mini.xml +642 -0
- reachy_mini/descriptions/reachy_mini/mjcf/reachy_mini.xml.bak +442 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scene.xml +24 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scenes/empty.xml +27 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scenes/minimal.xml +132 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/README.m +1 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/foot.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/foot.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/shape.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/shape.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/config.json +18 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot.urdf +3281 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot.urdf.bak +3282 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot_no_collision.urdf +2316 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot_simple_collision.urdf +2399 -0
- reachy_mini/io/__init__.py +15 -0
- reachy_mini/io/abstract.py +70 -0
- reachy_mini/io/audio_ws.py +242 -0
- reachy_mini/io/protocol.py +44 -0
- reachy_mini/io/video_ws.py +135 -0
- reachy_mini/io/ws_controller.py +120 -0
- reachy_mini/io/zenoh_client.py +273 -0
- reachy_mini/io/zenoh_server.py +230 -0
- reachy_mini/kinematics/__init__.py +68 -0
- reachy_mini/kinematics/analytical_kinematics.py +151 -0
- reachy_mini/kinematics/nn_kinematics.py +109 -0
- reachy_mini/kinematics/placo_kinematics.py +656 -0
- reachy_mini/media/__init__.py +1 -0
- reachy_mini/media/audio_base.py +111 -0
- reachy_mini/media/audio_control_utils.py +455 -0
- reachy_mini/media/audio_gstreamer.py +272 -0
- reachy_mini/media/audio_sounddevice.py +321 -0
- reachy_mini/media/audio_utils.py +114 -0
- reachy_mini/media/camera_base.py +100 -0
- reachy_mini/media/camera_constants.py +152 -0
- reachy_mini/media/camera_gstreamer.py +231 -0
- reachy_mini/media/camera_opencv.py +89 -0
- reachy_mini/media/camera_utils.py +111 -0
- reachy_mini/media/media_manager.py +281 -0
- reachy_mini/media/webrtc_client_gstreamer.py +288 -0
- reachy_mini/media/webrtc_daemon.py +334 -0
- reachy_mini/motion/__init__.py +4 -0
- reachy_mini/motion/goto.py +71 -0
- reachy_mini/motion/move.py +43 -0
- reachy_mini/motion/recorded_move.py +146 -0
- reachy_mini/reachy_mini.py +753 -0
- reachy_mini/tools/reflash_motors.py +116 -0
- reachy_mini/tools/setup_motor.py +422 -0
- reachy_mini/tools/setup_motor_rpi.py +108 -0
- reachy_mini/utils/__init__.py +46 -0
- reachy_mini/utils/constants.py +9 -0
- reachy_mini/utils/hardware_config/__init__.py +1 -0
- reachy_mini/utils/hardware_config/parser.py +65 -0
- reachy_mini/utils/interpolation.py +227 -0
- reachy_mini/utils/parse_urdf_for_kinematics.py +110 -0
- reachy_mini/utils/rerun.py +320 -0
- reachy_mini/utils/wireless_version/__init__.py +1 -0
- reachy_mini/utils/wireless_version/startup_check.py +144 -0
- reachy_mini/utils/wireless_version/update.py +20 -0
- reachy_mini/utils/wireless_version/update_available.py +61 -0
- reachy_mini/utils/wireless_version/utils.py +39 -0
- reachy_mini-1.2.5rc1.dist-info/METADATA +161 -0
- reachy_mini-1.2.5rc1.dist-info/RECORD +479 -0
- reachy_mini-1.2.5rc1.dist-info/WHEEL +5 -0
- reachy_mini-1.2.5rc1.dist-info/entry_points.txt +4 -0
- reachy_mini-1.2.5rc1.dist-info/licenses/LICENSE +201 -0
- reachy_mini-1.2.5rc1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"""GStreamer camera backend.
|
|
2
|
+
|
|
3
|
+
This module provides an implementation of the CameraBase class using GStreamer.
|
|
4
|
+
By default the module directly returns JPEG images as output by the camera.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from threading import Thread
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import numpy.typing as npt
|
|
13
|
+
|
|
14
|
+
from reachy_mini.media.audio_utils import (
|
|
15
|
+
get_respeaker_card_number,
|
|
16
|
+
has_reachymini_asoundrc,
|
|
17
|
+
)
|
|
18
|
+
from reachy_mini.utils.constants import ASSETS_ROOT_PATH
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
import gi
|
|
22
|
+
except ImportError as e:
|
|
23
|
+
raise ImportError(
|
|
24
|
+
"The 'gi' module is required for GStreamerCamera but could not be imported. \
|
|
25
|
+
Please install the GStreamer backend: pip install .[gstreamer]."
|
|
26
|
+
) from e
|
|
27
|
+
|
|
28
|
+
gi.require_version("Gst", "1.0")
|
|
29
|
+
gi.require_version("GstApp", "1.0")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
from gi.repository import GLib, Gst, GstApp # noqa: E402
|
|
33
|
+
|
|
34
|
+
from .audio_base import AudioBase # noqa: E402
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class GStreamerAudio(AudioBase):
|
|
38
|
+
"""Audio implementation using GStreamer."""
|
|
39
|
+
|
|
40
|
+
def __init__(self, log_level: str = "INFO") -> None:
|
|
41
|
+
"""Initialize the GStreamer audio."""
|
|
42
|
+
super().__init__(log_level=log_level)
|
|
43
|
+
Gst.init(None)
|
|
44
|
+
self._loop = GLib.MainLoop()
|
|
45
|
+
self._thread_bus_calls = Thread(target=lambda: self._loop.run(), daemon=True)
|
|
46
|
+
self._thread_bus_calls.start()
|
|
47
|
+
|
|
48
|
+
self._id_audio_card = get_respeaker_card_number()
|
|
49
|
+
|
|
50
|
+
self._pipeline_record = Gst.Pipeline.new("audio_recorder")
|
|
51
|
+
self._appsink_audio: Optional[GstApp] = None
|
|
52
|
+
self._init_pipeline_record(self._pipeline_record)
|
|
53
|
+
self._bus_record = self._pipeline_record.get_bus()
|
|
54
|
+
self._bus_record.add_watch(
|
|
55
|
+
GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
self._pipeline_playback = Gst.Pipeline.new("audio_player")
|
|
59
|
+
self._appsrc: Optional[GstApp] = None
|
|
60
|
+
self._init_pipeline_playback(self._pipeline_playback)
|
|
61
|
+
self._bus_playback = self._pipeline_playback.get_bus()
|
|
62
|
+
self._bus_playback.add_watch(
|
|
63
|
+
GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def _init_pipeline_record(self, pipeline: Gst.Pipeline) -> None:
|
|
67
|
+
self._appsink_audio = Gst.ElementFactory.make("appsink")
|
|
68
|
+
caps = Gst.Caps.from_string(
|
|
69
|
+
f"audio/x-raw,rate={self.SAMPLE_RATE},channels={self.CHANNELS},format=F32LE,layout=interleaved"
|
|
70
|
+
)
|
|
71
|
+
self._appsink_audio.set_property("caps", caps)
|
|
72
|
+
self._appsink_audio.set_property("drop", True) # avoid overflow
|
|
73
|
+
self._appsink_audio.set_property("max-buffers", 200)
|
|
74
|
+
|
|
75
|
+
audiosrc: Optional[Gst.Element] = None
|
|
76
|
+
if self._id_audio_card == -1:
|
|
77
|
+
audiosrc = Gst.ElementFactory.make("autoaudiosrc") # use default mic
|
|
78
|
+
elif has_reachymini_asoundrc():
|
|
79
|
+
# reachy mini wireless has a preconfigured asoundrc
|
|
80
|
+
audiosrc = Gst.ElementFactory.make("alsasrc")
|
|
81
|
+
audiosrc.set_property("device", "reachymini_audio_src")
|
|
82
|
+
else:
|
|
83
|
+
audiosrc = Gst.ElementFactory.make("alsasrc")
|
|
84
|
+
audiosrc.set_property("device", f"hw:{self._id_audio_card},0")
|
|
85
|
+
|
|
86
|
+
queue = Gst.ElementFactory.make("queue")
|
|
87
|
+
audioconvert = Gst.ElementFactory.make("audioconvert")
|
|
88
|
+
audioresample = Gst.ElementFactory.make("audioresample")
|
|
89
|
+
|
|
90
|
+
if not all([audiosrc, queue, audioconvert, audioresample, self._appsink_audio]):
|
|
91
|
+
raise RuntimeError("Failed to create GStreamer elements")
|
|
92
|
+
|
|
93
|
+
pipeline.add(audiosrc)
|
|
94
|
+
pipeline.add(queue)
|
|
95
|
+
pipeline.add(audioconvert)
|
|
96
|
+
pipeline.add(audioresample)
|
|
97
|
+
pipeline.add(self._appsink_audio)
|
|
98
|
+
|
|
99
|
+
audiosrc.link(queue)
|
|
100
|
+
queue.link(audioconvert)
|
|
101
|
+
audioconvert.link(audioresample)
|
|
102
|
+
audioresample.link(self._appsink_audio)
|
|
103
|
+
|
|
104
|
+
def __del__(self) -> None:
|
|
105
|
+
"""Destructor to ensure gstreamer resources are released."""
|
|
106
|
+
super().__del__()
|
|
107
|
+
self._loop.quit()
|
|
108
|
+
self._bus_record.remove_watch()
|
|
109
|
+
self._bus_playback.remove_watch()
|
|
110
|
+
|
|
111
|
+
def _init_pipeline_playback(self, pipeline: Gst.Pipeline) -> None:
|
|
112
|
+
self._appsrc = Gst.ElementFactory.make("appsrc")
|
|
113
|
+
self._appsrc.set_property("format", Gst.Format.TIME)
|
|
114
|
+
self._appsrc.set_property("is-live", True)
|
|
115
|
+
caps = Gst.Caps.from_string(
|
|
116
|
+
f"audio/x-raw,format=F32LE,channels={self.CHANNELS},rate={self.SAMPLE_RATE},layout=interleaved"
|
|
117
|
+
)
|
|
118
|
+
self._appsrc.set_property("caps", caps)
|
|
119
|
+
|
|
120
|
+
audioconvert = Gst.ElementFactory.make("audioconvert")
|
|
121
|
+
audioresample = Gst.ElementFactory.make("audioresample")
|
|
122
|
+
|
|
123
|
+
queue = Gst.ElementFactory.make("queue")
|
|
124
|
+
audiosink: Optional[Gst.Element] = None
|
|
125
|
+
if self._id_audio_card == -1:
|
|
126
|
+
audiosink = Gst.ElementFactory.make("autoaudiosink") # use default speaker
|
|
127
|
+
elif has_reachymini_asoundrc():
|
|
128
|
+
# reachy mini wireless has a preconfigured asoundrc
|
|
129
|
+
audiosink = Gst.ElementFactory.make("alsasink")
|
|
130
|
+
audiosink.set_property("device", "reachymini_audio_sink")
|
|
131
|
+
else:
|
|
132
|
+
audiosink = Gst.ElementFactory.make("alsasink")
|
|
133
|
+
audiosink.set_property("device", f"hw:{self._id_audio_card},0")
|
|
134
|
+
|
|
135
|
+
pipeline.add(queue)
|
|
136
|
+
pipeline.add(audiosink)
|
|
137
|
+
pipeline.add(self._appsrc)
|
|
138
|
+
pipeline.add(audioconvert)
|
|
139
|
+
pipeline.add(audioresample)
|
|
140
|
+
|
|
141
|
+
self._appsrc.link(queue)
|
|
142
|
+
queue.link(audioconvert)
|
|
143
|
+
audioconvert.link(audioresample)
|
|
144
|
+
audioresample.link(audiosink)
|
|
145
|
+
|
|
146
|
+
def _on_bus_message(self, bus: Gst.Bus, msg: Gst.Message, loop) -> bool: # type: ignore[no-untyped-def]
|
|
147
|
+
t = msg.type
|
|
148
|
+
if t == Gst.MessageType.EOS:
|
|
149
|
+
self.logger.warning("End-of-stream")
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
elif t == Gst.MessageType.ERROR:
|
|
153
|
+
err, debug = msg.parse_error()
|
|
154
|
+
self.logger.error(f"Error: {err} {debug}")
|
|
155
|
+
return False
|
|
156
|
+
|
|
157
|
+
return True
|
|
158
|
+
|
|
159
|
+
def start_recording(self) -> None:
|
|
160
|
+
"""Open the audio card using GStreamer."""
|
|
161
|
+
self._pipeline_record.set_state(Gst.State.PLAYING)
|
|
162
|
+
|
|
163
|
+
def _get_sample(self, appsink: GstApp.AppSink) -> Optional[bytes]:
|
|
164
|
+
sample = appsink.try_pull_sample(20_000_000)
|
|
165
|
+
if sample is None:
|
|
166
|
+
return None
|
|
167
|
+
data = None
|
|
168
|
+
if isinstance(sample, Gst.Sample):
|
|
169
|
+
buf = sample.get_buffer()
|
|
170
|
+
if buf is None:
|
|
171
|
+
self.logger.warning("Buffer is None")
|
|
172
|
+
|
|
173
|
+
data = buf.extract_dup(0, buf.get_size())
|
|
174
|
+
return data
|
|
175
|
+
|
|
176
|
+
def get_audio_sample(self) -> Optional[npt.NDArray[np.float32]]:
|
|
177
|
+
"""Read a sample from the audio card. Returns the sample or None if error.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Optional[npt.NDArray[np.float32]]: The captured sample in raw format, or None if error.
|
|
181
|
+
|
|
182
|
+
"""
|
|
183
|
+
sample = self._get_sample(self._appsink_audio)
|
|
184
|
+
if sample is None:
|
|
185
|
+
return None
|
|
186
|
+
return np.frombuffer(sample, dtype=np.float32).reshape(-1, 2)
|
|
187
|
+
|
|
188
|
+
def get_input_audio_samplerate(self) -> int:
|
|
189
|
+
"""Get the input samplerate of the audio device."""
|
|
190
|
+
return self.SAMPLE_RATE
|
|
191
|
+
|
|
192
|
+
def get_output_audio_samplerate(self) -> int:
|
|
193
|
+
"""Get the output samplerate of the audio device."""
|
|
194
|
+
return self.SAMPLE_RATE
|
|
195
|
+
|
|
196
|
+
def get_input_channels(self) -> int:
|
|
197
|
+
"""Get the number of input channels of the audio device."""
|
|
198
|
+
return self.CHANNELS
|
|
199
|
+
|
|
200
|
+
def get_output_channels(self) -> int:
|
|
201
|
+
"""Get the number of output channels of the audio device."""
|
|
202
|
+
return self.CHANNELS
|
|
203
|
+
|
|
204
|
+
def stop_recording(self) -> None:
|
|
205
|
+
"""Release the camera resource."""
|
|
206
|
+
self._pipeline_record.set_state(Gst.State.NULL)
|
|
207
|
+
|
|
208
|
+
def start_playing(self) -> None:
|
|
209
|
+
"""Open the audio output using GStreamer."""
|
|
210
|
+
self._pipeline_playback.set_state(Gst.State.PLAYING)
|
|
211
|
+
|
|
212
|
+
def stop_playing(self) -> None:
|
|
213
|
+
"""Stop playing audio and release resources."""
|
|
214
|
+
self._pipeline_playback.set_state(Gst.State.NULL)
|
|
215
|
+
|
|
216
|
+
def push_audio_sample(self, data: npt.NDArray[np.float32]) -> None:
|
|
217
|
+
"""Push audio data to the output device."""
|
|
218
|
+
if self._appsrc is not None:
|
|
219
|
+
buf = Gst.Buffer.new_wrapped(data.tobytes())
|
|
220
|
+
self._appsrc.push_buffer(buf)
|
|
221
|
+
else:
|
|
222
|
+
self.logger.warning(
|
|
223
|
+
"AppSrc is not initialized. Call start_playing() first."
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def play_sound(self, sound_file: str) -> None:
|
|
227
|
+
"""Play a sound file.
|
|
228
|
+
|
|
229
|
+
Todo: for now this function is mean to be used on the wireless version.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
sound_file (str): Path to the sound file to play.
|
|
233
|
+
|
|
234
|
+
"""
|
|
235
|
+
if not os.path.exists(sound_file):
|
|
236
|
+
file_path = f"{ASSETS_ROOT_PATH}/{sound_file}"
|
|
237
|
+
if not os.path.exists(file_path):
|
|
238
|
+
raise FileNotFoundError(
|
|
239
|
+
f"Sound file {sound_file} not found in assets directory or given path."
|
|
240
|
+
)
|
|
241
|
+
else:
|
|
242
|
+
file_path = sound_file
|
|
243
|
+
|
|
244
|
+
audiosink: Optional[Gst.Element] = None
|
|
245
|
+
|
|
246
|
+
if has_reachymini_asoundrc():
|
|
247
|
+
# reachy mini wireless has a preconfigured asoundrc
|
|
248
|
+
audiosink = Gst.ElementFactory.make("alsasink")
|
|
249
|
+
audiosink.set_property("device", "reachymini_audio_sink")
|
|
250
|
+
|
|
251
|
+
playbin = Gst.ElementFactory.make("playbin", "player")
|
|
252
|
+
if not playbin:
|
|
253
|
+
self.logger.error("Failed to create playbin element")
|
|
254
|
+
return
|
|
255
|
+
playbin.set_property("uri", f"file://{file_path}")
|
|
256
|
+
if audiosink is not None:
|
|
257
|
+
playbin.set_property("audio-sink", audiosink)
|
|
258
|
+
|
|
259
|
+
playbin.set_state(Gst.State.PLAYING)
|
|
260
|
+
|
|
261
|
+
def clear_player(self) -> None:
|
|
262
|
+
"""Flush the player's appsrc to drop any queued audio immediately."""
|
|
263
|
+
if self._appsrc is not None:
|
|
264
|
+
self._pipeline_playback.set_state(Gst.State.PAUSED)
|
|
265
|
+
self._appsrc.send_event(Gst.Event.new_flush_start())
|
|
266
|
+
self._appsrc.send_event(Gst.Event.new_flush_stop(reset_time=True))
|
|
267
|
+
self._pipeline_playback.set_state(Gst.State.PLAYING)
|
|
268
|
+
self.logger.info("Cleared player queue")
|
|
269
|
+
else:
|
|
270
|
+
self.logger.warning(
|
|
271
|
+
"AppSrc is not initialized. Call start_playing() first."
|
|
272
|
+
)
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"""Audio implementation using sounddevice backend."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import threading
|
|
5
|
+
from collections import deque
|
|
6
|
+
from typing import Deque, List, Optional
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import numpy.typing as npt
|
|
10
|
+
import scipy
|
|
11
|
+
import sounddevice as sd
|
|
12
|
+
import soundfile as sf
|
|
13
|
+
|
|
14
|
+
from reachy_mini.utils.constants import ASSETS_ROOT_PATH
|
|
15
|
+
|
|
16
|
+
from .audio_base import AudioBase
|
|
17
|
+
|
|
18
|
+
MAX_INPUT_CHANNELS = 4
|
|
19
|
+
MAX_INPUT_QUEUE_SECONDS = 60.0
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SoundDeviceAudio(AudioBase):
|
|
23
|
+
"""Audio device implementation using sounddevice."""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
log_level: str = "INFO",
|
|
28
|
+
) -> None:
|
|
29
|
+
"""Initialize the SoundDevice audio device."""
|
|
30
|
+
super().__init__(log_level=log_level)
|
|
31
|
+
self._input_stream = None
|
|
32
|
+
self._output_stream = None
|
|
33
|
+
self._input_lock = threading.Lock()
|
|
34
|
+
self._output_lock = threading.Lock()
|
|
35
|
+
self._input_buffer: Deque[npt.NDArray[np.float32]] = deque()
|
|
36
|
+
self._output_buffer: List[npt.NDArray[np.float32]] = []
|
|
37
|
+
self._input_max_queue_seconds: float = MAX_INPUT_QUEUE_SECONDS
|
|
38
|
+
self._input_queued_samples: int = 0
|
|
39
|
+
|
|
40
|
+
self._output_device_id = self._get_device_id(
|
|
41
|
+
["Reachy Mini Audio", "respeaker"], device_io_type="output"
|
|
42
|
+
)
|
|
43
|
+
self._input_device_id = self._get_device_id(
|
|
44
|
+
["Reachy Mini Audio", "respeaker"], device_io_type="input"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
self._logs = {
|
|
48
|
+
"input_underflows": 0,
|
|
49
|
+
"input_overflows": 0,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def _input_max_queue_samples(self) -> int:
|
|
54
|
+
return int(self._input_max_queue_seconds * self.get_input_audio_samplerate())
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def _is_recording(self) -> bool:
|
|
58
|
+
return self._input_stream is not None and self._input_stream.active
|
|
59
|
+
|
|
60
|
+
def start_recording(self) -> None:
|
|
61
|
+
"""Open the audio input stream, using ReSpeaker card if available."""
|
|
62
|
+
if self._is_recording:
|
|
63
|
+
self.stop_recording()
|
|
64
|
+
|
|
65
|
+
self._input_stream = sd.InputStream(
|
|
66
|
+
device=self._input_device_id,
|
|
67
|
+
samplerate=self.get_input_audio_samplerate(),
|
|
68
|
+
callback=self._input_callback,
|
|
69
|
+
)
|
|
70
|
+
if self._input_stream is None:
|
|
71
|
+
raise RuntimeError("Failed to open SoundDevice audio input stream.")
|
|
72
|
+
|
|
73
|
+
self._input_buffer.clear()
|
|
74
|
+
self._input_queued_samples = 0
|
|
75
|
+
self._input_stream.start()
|
|
76
|
+
self.logger.info("SoundDevice audio input stream opened.")
|
|
77
|
+
|
|
78
|
+
def _input_callback(
|
|
79
|
+
self,
|
|
80
|
+
indata: npt.NDArray[np.float32],
|
|
81
|
+
frames: int,
|
|
82
|
+
time: int,
|
|
83
|
+
status: sd.CallbackFlags,
|
|
84
|
+
) -> None:
|
|
85
|
+
if status and status.input_underflow:
|
|
86
|
+
self._logs["input_underflows"] += 1
|
|
87
|
+
if self._logs["input_underflows"] % 10 == 1:
|
|
88
|
+
self.logger.debug(
|
|
89
|
+
f"Audio input underflow count: {self._logs['input_underflows']}"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
with self._input_lock:
|
|
93
|
+
if (
|
|
94
|
+
self._input_queued_samples + indata.shape[0]
|
|
95
|
+
> self._input_max_queue_samples
|
|
96
|
+
):
|
|
97
|
+
while (
|
|
98
|
+
self._input_queued_samples + indata.shape[0]
|
|
99
|
+
> self._input_max_queue_samples
|
|
100
|
+
and len(self._input_buffer) > 0
|
|
101
|
+
):
|
|
102
|
+
dropped = self._input_buffer.popleft()
|
|
103
|
+
self._input_queued_samples -= dropped.shape[0]
|
|
104
|
+
self._logs["input_overflows"] += 1
|
|
105
|
+
self.logger.warning(
|
|
106
|
+
"Audio input buffer overflowed, dropped old chunks !"
|
|
107
|
+
)
|
|
108
|
+
self._input_buffer.append(indata[:, :MAX_INPUT_CHANNELS].copy())
|
|
109
|
+
self._input_queued_samples += indata.shape[0]
|
|
110
|
+
|
|
111
|
+
def get_audio_sample(self) -> Optional[npt.NDArray[np.float32]]:
|
|
112
|
+
"""Read audio data from the buffer. Returns numpy array or None if empty."""
|
|
113
|
+
with self._input_lock:
|
|
114
|
+
if self._input_buffer and len(self._input_buffer) > 0:
|
|
115
|
+
data: npt.NDArray[np.float32] = np.concatenate(
|
|
116
|
+
self._input_buffer, axis=0
|
|
117
|
+
)
|
|
118
|
+
self._input_buffer.clear()
|
|
119
|
+
self._input_queued_samples = 0
|
|
120
|
+
return data
|
|
121
|
+
self.logger.debug("No audio data available in buffer.")
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
def get_input_audio_samplerate(self) -> int:
|
|
125
|
+
"""Get the input samplerate of the audio device."""
|
|
126
|
+
return int(
|
|
127
|
+
sd.query_devices(self._input_device_id, "input")["default_samplerate"]
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def get_output_audio_samplerate(self) -> int:
|
|
131
|
+
"""Get the output samplerate of the audio device."""
|
|
132
|
+
return int(
|
|
133
|
+
sd.query_devices(self._output_device_id, "output")["default_samplerate"]
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def get_input_channels(self) -> int:
|
|
137
|
+
"""Get the number of input channels of the audio device."""
|
|
138
|
+
return min(
|
|
139
|
+
int(sd.query_devices(self._input_device_id, "input")["max_input_channels"]),
|
|
140
|
+
MAX_INPUT_CHANNELS,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def get_output_channels(self) -> int:
|
|
144
|
+
"""Get the number of output channels of the audio device."""
|
|
145
|
+
return int(
|
|
146
|
+
sd.query_devices(self._output_device_id, "output")["max_output_channels"]
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def stop_recording(self) -> None:
|
|
150
|
+
"""Close the audio stream and release resources."""
|
|
151
|
+
if self._is_recording:
|
|
152
|
+
self._input_stream.stop() # type: ignore[attr-defined]
|
|
153
|
+
self._input_stream.close() # type: ignore[attr-defined]
|
|
154
|
+
self._input_stream = None
|
|
155
|
+
self.logger.info("SoundDevice audio stream closed.")
|
|
156
|
+
|
|
157
|
+
def push_audio_sample(self, data: npt.NDArray[np.float32]) -> None:
|
|
158
|
+
"""Push audio data to the output device."""
|
|
159
|
+
if self._output_stream is not None:
|
|
160
|
+
with self._output_lock:
|
|
161
|
+
self._output_buffer.append(data.copy())
|
|
162
|
+
else:
|
|
163
|
+
self.logger.warning(
|
|
164
|
+
"Output stream is not open. Call start_playing() first."
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def clear_output_buffer(self) -> None:
|
|
168
|
+
"""Clear the output buffer."""
|
|
169
|
+
with self._output_lock:
|
|
170
|
+
self._output_buffer.clear()
|
|
171
|
+
|
|
172
|
+
def start_playing(self) -> None:
|
|
173
|
+
"""Open the audio output stream."""
|
|
174
|
+
self.clear_output_buffer()
|
|
175
|
+
|
|
176
|
+
if self._output_stream is not None:
|
|
177
|
+
self.stop_playing()
|
|
178
|
+
self._output_stream = sd.OutputStream(
|
|
179
|
+
samplerate=self.get_output_audio_samplerate(),
|
|
180
|
+
device=self._output_device_id,
|
|
181
|
+
callback=self._output_callback,
|
|
182
|
+
)
|
|
183
|
+
if self._output_stream is None:
|
|
184
|
+
raise RuntimeError("Failed to open SoundDevice audio output stream.")
|
|
185
|
+
self._output_stream.start()
|
|
186
|
+
self.logger.info("SoundDevice audio output stream opened.")
|
|
187
|
+
|
|
188
|
+
def _output_callback(
|
|
189
|
+
self,
|
|
190
|
+
outdata: npt.NDArray[np.float32],
|
|
191
|
+
frames: int,
|
|
192
|
+
time: int,
|
|
193
|
+
status: sd.CallbackFlags,
|
|
194
|
+
) -> None:
|
|
195
|
+
"""Handle audio output stream callback."""
|
|
196
|
+
if status:
|
|
197
|
+
self.logger.warning(f"SoundDevice output status: {status}")
|
|
198
|
+
|
|
199
|
+
with self._output_lock:
|
|
200
|
+
filled = 0
|
|
201
|
+
while filled < frames and self._output_buffer:
|
|
202
|
+
chunk = self._output_buffer[0]
|
|
203
|
+
|
|
204
|
+
needed = frames - filled
|
|
205
|
+
available = len(chunk)
|
|
206
|
+
take = min(needed, available)
|
|
207
|
+
|
|
208
|
+
outdata[filled:filled + take] = chunk[:take]
|
|
209
|
+
filled += take
|
|
210
|
+
|
|
211
|
+
if take < available:
|
|
212
|
+
# Partial consumption, keep remainder
|
|
213
|
+
self._output_buffer[0] = chunk[take:]
|
|
214
|
+
else:
|
|
215
|
+
# Fully consumed this chunk
|
|
216
|
+
self._output_buffer.pop(0)
|
|
217
|
+
|
|
218
|
+
# Only pad with zeros if buffer is truly empty
|
|
219
|
+
if filled < frames:
|
|
220
|
+
outdata[filled:] = 0
|
|
221
|
+
|
|
222
|
+
def ensure_chunk_shape(
|
|
223
|
+
self, chunk: npt.NDArray[np.float32], target_shape: tuple[int, ...]
|
|
224
|
+
) -> npt.NDArray[np.float32]:
|
|
225
|
+
"""Ensure chunk has the shape (frames, num_channels) as required by outdata.
|
|
226
|
+
|
|
227
|
+
- If chunk is 1D, tile to required num_channels.
|
|
228
|
+
- If chunk is 2D with mismatched channels, use column 0.
|
|
229
|
+
- If chunk is already correct, return as-is.
|
|
230
|
+
"""
|
|
231
|
+
num_channels = target_shape[1] if len(target_shape) > 1 else 1
|
|
232
|
+
if chunk.ndim == 1:
|
|
233
|
+
return np.tile(chunk[:, None], (1, num_channels))
|
|
234
|
+
elif chunk.shape[1] != num_channels:
|
|
235
|
+
# Broadcast first channel only
|
|
236
|
+
return np.tile(chunk[:, [0]], (1, num_channels))
|
|
237
|
+
return chunk
|
|
238
|
+
|
|
239
|
+
def stop_playing(self) -> None:
|
|
240
|
+
"""Close the audio output stream."""
|
|
241
|
+
if self._output_stream is not None:
|
|
242
|
+
self._output_stream.stop()
|
|
243
|
+
self._output_stream.close()
|
|
244
|
+
self._output_stream = None
|
|
245
|
+
self.clear_output_buffer()
|
|
246
|
+
self.logger.info("SoundDevice audio output stream closed.")
|
|
247
|
+
|
|
248
|
+
def play_sound(self, sound_file: str) -> None:
|
|
249
|
+
"""Play a sound file.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
sound_file (str): Path to the sound file to play. May be given relative to the assets directory or as an absolute path.
|
|
253
|
+
|
|
254
|
+
"""
|
|
255
|
+
if not os.path.exists(sound_file):
|
|
256
|
+
file_path = f"{ASSETS_ROOT_PATH}/{sound_file}"
|
|
257
|
+
if not os.path.exists(file_path):
|
|
258
|
+
raise FileNotFoundError(
|
|
259
|
+
f"Sound file {sound_file} not found in assets directory or given path."
|
|
260
|
+
)
|
|
261
|
+
else:
|
|
262
|
+
file_path = sound_file
|
|
263
|
+
|
|
264
|
+
data, samplerate_in = sf.read(file_path, dtype="float32")
|
|
265
|
+
samplerate_out = self.get_output_audio_samplerate()
|
|
266
|
+
|
|
267
|
+
if samplerate_in != samplerate_out:
|
|
268
|
+
data = scipy.signal.resample(
|
|
269
|
+
data, int(len(data) * (samplerate_out / samplerate_in))
|
|
270
|
+
)
|
|
271
|
+
data = self.ensure_chunk_shape(data, (-1, self.get_output_channels()))
|
|
272
|
+
|
|
273
|
+
self.logger.debug(f"Playing sound '{file_path}' at {samplerate_in} Hz")
|
|
274
|
+
|
|
275
|
+
if self._output_stream is not None:
|
|
276
|
+
self.push_audio_sample(data)
|
|
277
|
+
else:
|
|
278
|
+
self.logger.warning(
|
|
279
|
+
"Output stream wasn't open. We are opening it and leaving it open."
|
|
280
|
+
)
|
|
281
|
+
self.start_playing()
|
|
282
|
+
self.push_audio_sample(data)
|
|
283
|
+
|
|
284
|
+
def _get_device_id(
|
|
285
|
+
self, names_contains: List[str], device_io_type: str = "output"
|
|
286
|
+
) -> int:
|
|
287
|
+
"""Return the output device id whose name contains the given strings (case-insensitive).
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
names_contains (List[str]): List of strings that should be contained in the device name.
|
|
291
|
+
device_io_type (str): 'input' or 'output' to specify device type.
|
|
292
|
+
|
|
293
|
+
If not found, return the default output device id.
|
|
294
|
+
|
|
295
|
+
"""
|
|
296
|
+
devices = sd.query_devices()
|
|
297
|
+
|
|
298
|
+
for idx, dev in enumerate(devices):
|
|
299
|
+
for name_contains in names_contains:
|
|
300
|
+
if (
|
|
301
|
+
name_contains.lower() in dev["name"].lower()
|
|
302
|
+
and dev[f"max_{device_io_type}_channels"] > 0
|
|
303
|
+
):
|
|
304
|
+
return idx
|
|
305
|
+
# Return default output device if not found
|
|
306
|
+
self.logger.warning(
|
|
307
|
+
f"No {device_io_type} device found containing '{names_contains}', using default."
|
|
308
|
+
)
|
|
309
|
+
return self._safe_query_device(device_io_type)
|
|
310
|
+
|
|
311
|
+
def _safe_query_device(self, kind: str) -> int:
|
|
312
|
+
try:
|
|
313
|
+
return int(sd.query_devices(None, kind)["index"])
|
|
314
|
+
except sd.PortAudioError:
|
|
315
|
+
return (
|
|
316
|
+
int(sd.default.device[1])
|
|
317
|
+
if kind == "input"
|
|
318
|
+
else int(sd.default.device[0])
|
|
319
|
+
)
|
|
320
|
+
except IndexError:
|
|
321
|
+
return 0
|