Skip to content

Commit

Permalink
Merge pull request #11 from RoBorregos/main
Browse files Browse the repository at this point in the history
precommit to branch
  • Loading branch information
Ale-Coeto authored Jan 3, 2025
2 parents 9284f15 + c68b168 commit 55a0272
Show file tree
Hide file tree
Showing 12 changed files with 219 additions and 86 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: pre-commit

on:
pull_request:
push:
branches: ["**"]

jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: pre-commit/[email protected]
12 changes: 12 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.8.4
hooks:
# Run the linter.
- id: ruff
types_or: [python, pyi]
# args: [--fix]
# Run the formatter.
- id: ruff-format
types_or: [python, pyi]
55 changes: 55 additions & 0 deletions Setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Project setup

## Pre-commit

[Pre-commit](https://pre-commit.com/) is a framework for managing and maintaining multi-language pre-commit hooks. It is a useful tool to ensure that code is formatted and linted before committing. The following hooks are used in this project:

- [ruff](https://github.com/astral-sh/ruff-pre-commit/tree/main) (linter and formatter for python)

To install pre-commit and the repo's hooks, run the following commands:

```bash
pip install pre-commit
pre-commit install
```

To run against all files (useful when adding new hooks), use:

```bash
pre-commit run --all-files
```

## Ruff

Ruff is a linter and formatter for python. It is used to mantain consistency and readability in the codebase. Ruff in installed as a pre-commit hook, but can also be run manually. To install ruff, run the following command:

```bash
pip install ruff
```

To run ruff, use the following command:

```bash
ruff check
```

### Vscode integration

If you are using vscode, you can add the following in `.vscode/settings.json` to enable ruff formatting on save. For this to work, you must also install the [Ruff extension for vscode](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff).

```json
{
"[python]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
},
"editor.defaultFormatter": "charliermarsh.ruff"
}
}
```

### Other integrations

For integration with other editors, check the [ruff documentation](https://docs.astral.sh/ruff/editors/setup/)
70 changes: 48 additions & 22 deletions hri/packages/speech/debug/microphone.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
import pyaudio
import wave

import numpy as np
import pyaudio

# List available input devices and test audio recording
# To check if the audio was recorded correctly, use 'TestSpeaker.py' or see "ws/src/speech/Readme.md"


def record_audio(output_file, input_device_index, duration=10, channels=1, sample_rate=48000, chunk_size=480, format=pyaudio.paInt16):
def record_audio(
output_file,
input_device_index,
duration=10,
channels=1,
sample_rate=48000,
chunk_size=480,
format=pyaudio.paInt16,
):
audio = pyaudio.PyAudio()

# Open the audio stream
# See available devices with list_audio_devices()
stream = audio.open(input_device_index=input_device_index,
format=format,
channels=channels,
rate=sample_rate,
input=True,
frames_per_buffer=chunk_size)
stream = audio.open(
input_device_index=input_device_index,
format=format,
channels=channels,
rate=sample_rate,
input=True,
frames_per_buffer=chunk_size,
)

print("Recording...")

Expand All @@ -35,26 +46,37 @@ def record_audio(output_file, input_device_index, duration=10, channels=1, sampl
audio.terminate()

# Write audio data to a WAV file
with wave.open(output_file, 'wb') as wf:
with wave.open(output_file, "wb") as wf:
wf.setnchannels(channels)
wf.setsampwidth(audio.get_sample_size(format))
wf.setframerate(sample_rate)
wf.writeframes(b''.join(frames))
wf.writeframes(b"".join(frames))

print(f"Audio recorded and saved to {output_file}")


def record_audio_respeaker(output_file, input_device_index, duration=10, channels=6, sample_rate=16000, chunk_size=512, format=pyaudio.paInt16, extract_channel=0):
def record_audio_respeaker(
output_file,
input_device_index,
duration=10,
channels=6,
sample_rate=16000,
chunk_size=512,
format=pyaudio.paInt16,
extract_channel=0,
):
audio = pyaudio.PyAudio()

# Open the audio stream
# See available devices with list_audio_devices()
stream = audio.open(input_device_index=input_device_index,
format=format,
channels=channels,
rate=sample_rate,
input=True,
frames_per_buffer=chunk_size)
stream = audio.open(
input_device_index=input_device_index,
format=format,
channels=channels,
rate=sample_rate,
input=True,
frames_per_buffer=chunk_size,
)

print("Recording...")

Expand All @@ -76,20 +98,21 @@ def record_audio_respeaker(output_file, input_device_index, duration=10, channel
audio.terminate()

# Write audio data to a WAV file
with wave.open("1_" + output_file, 'wb') as wf:
with wave.open("1_" + output_file, "wb") as wf:
wf.setnchannels(1)
wf.setsampwidth(audio.get_sample_size(format))
wf.setframerate(sample_rate)
wf.writeframes(b''.join(frames))
wf.writeframes(b"".join(frames))

with wave.open("6_" + output_file, 'wb') as wf:
with wave.open("6_" + output_file, "wb") as wf:
wf.setnchannels(6)
wf.setsampwidth(audio.get_sample_size(format))
wf.setframerate(sample_rate)
wf.writeframes(b''.join(frames_6))
wf.writeframes(b"".join(frames_6))

print(f"Audio recorded and saved to 1_{output_file} and 6_{output_file}")


# Get available devices


Expand All @@ -99,7 +122,9 @@ def list_audio_devices():
print("Available audio devices:")
for i in range(num_devices):
device_info = p.get_device_info_by_index(i)
print(f"Device {i}: [{device_info['name']}], [{device_info['maxInputChannels']}] input channels, [{device_info['maxOutputChannels']}] output channels")
print(
f"Device {i}: [{device_info['name']}], [{device_info['maxInputChannels']}] input channels, [{device_info['maxOutputChannels']}] output channels"
)
p.terminate()


Expand All @@ -109,3 +134,4 @@ def list_audio_devices():
# output_file = "../recorded_audio.wav"
# record_audio(output_file, input_device_index=12, duration=5)
# record_audio_respeaker(output_file, input_device_index=10, duration=10, channels=6, sample_rate=16000, chunk_size=512, format=pyaudio.paInt16, extract_channel=0)
# record_audio_respeaker(output_file, input_device_index=10, duration=10, channels=6, sample_rate=16000, chunk_size=512, format=pyaudio.paInt16, extract_channel=0)
11 changes: 5 additions & 6 deletions hri/packages/speech/debug/speaker.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# python3 debug/speaker.py

from speech.speech_api_utils import SpeechApiUtils

from speech.wav_utils import WavUtils

import os
# from speech.speech_api_utils import SpeechApiUtils
# from speech.wav_utils import WavUtils
# import os

import sounddevice as sd
from pygame import mixer
Expand All @@ -15,7 +13,8 @@ def get_devices():
num_dev = 0
for device_info in devices:
print(
f"Device [{num_dev}]: [{device_info['name']}], [{device_info['max_input_channels']}] input channels, [{device_info['max_output_channels']}] output channels")
f"Device [{num_dev}]: [{device_info['name']}], [{device_info['max_input_channels']}] input channels, [{device_info['max_output_channels']}] output channels"
)
num_dev = num_dev + 1

# print("Original order:")
Expand Down
30 changes: 16 additions & 14 deletions hri/packages/speech/scripts/audio_capturer.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@
MIC_OUT_CHANNELS = int(os.getenv("MIC_OUT_CHANNELS", default=0))

INPUT_DEVICE_INDEX = SpeechApiUtils.getIndexByNameAndChannels(
MIC_DEVICE_NAME, MIC_INPUT_CHANNELS, MIC_OUT_CHANNELS)
MIC_DEVICE_NAME, MIC_INPUT_CHANNELS, MIC_OUT_CHANNELS
)

if INPUT_DEVICE_INDEX is None:
print("Warning: input device index not found, using system default.")


class AudioCapturer(Node):
def __init__(self):
super().__init__('audio_capturer')
self.publisher_ = self.create_publisher(AudioData, 'rawAudioChunk', 20)
self.get_logger().info('AudioCapturer node initialized.')
super().__init__("audio_capturer")
self.publisher_ = self.create_publisher(AudioData, "rawAudioChunk", 20)
self.get_logger().info("AudioCapturer node initialized.")

def record(self):
self.get_logger().info('AudioCapturer node recording.')
self.get_logger().info("AudioCapturer node recording.")

# Format for the recorded audio, constants set from the Porcupine demo.py
CHUNK_SIZE = 512
Expand All @@ -43,19 +44,20 @@ def record(self):
EXTRACT_CHANNEL = 0 # Use channel 0. Tested with TestMic.py. See channel meaning: https://wiki.seeedstudio.com/ReSpeaker-USB-Mic-Array/#update-firmware

p = pyaudio.PyAudio()
stream = p.open(input_device_index=INPUT_DEVICE_INDEX, # See list_audio_devices() or set it to None for default
format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK_SIZE)
stream = p.open(
input_device_index=INPUT_DEVICE_INDEX, # See list_audio_devices() or set it to None for default
format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK_SIZE,
)

while stream.is_active() and rclpy.ok():
try:
in_data = stream.read(CHUNK_SIZE, exception_on_overflow=False)
if USE_RESPEAKER:
in_data = np.frombuffer(in_data, dtype=np.int16)[
EXTRACT_CHANNEL:: 6]
in_data = np.frombuffer(in_data, dtype=np.int16)[EXTRACT_CHANNEL::6]
in_data = in_data.tobytes()
msg = in_data
self.publisher_.publish(AudioData(data=msg))
Expand All @@ -81,5 +83,5 @@ def main(args=None):
rclpy.try_shutdown()


if __name__ == '__main__':
if __name__ == "__main__":
main()
19 changes: 10 additions & 9 deletions hri/packages/speech/scripts/say.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,18 @@


class Say(Node):

def __init__(self):
super().__init__('say')
self.publisher_ = self.create_publisher(Bool, 'saying', 10)
self.get_logger().info('Say node has started.')
super().__init__("say")
self.publisher_ = self.create_publisher(Bool, "saying", 10)
self.get_logger().info("Say node has started.")

self.connected = False
if not OFFLINE:
self.connected = SpeechApiUtils.is_connected()

self.create_service(Speak, SPEAK_SERVICE_TOPIC, self.speak_service)

self.create_subscription(String,
SPEAK_NOW_TOPIC, self.speak_topic, 10)
self.create_subscription(String, SPEAK_NOW_TOPIC, self.speak_topic, 10)

def speak_service(self, req):
"""When say is called as a service. Caller awaits for the response."""
Expand Down Expand Up @@ -138,8 +136,11 @@ def split_text(text: str, max_len, split_sentences=False):
def synthesize_voice_offline(self, output_path, text):
"""Synthesize text using the offline voice model."""

executable = "/workspace/piper/install/piper" if os.path.exists(
"/workspace/piper/install/piper") else "python3.10 -m piper"
executable = (
"/workspace/piper/install/piper"
if os.path.exists("/workspace/piper/install/piper")
else "python3.10 -m piper"
)

model_path = os.path.join(VOICE_DIRECTORY, MODEL + ".onnx")

Expand Down Expand Up @@ -176,5 +177,5 @@ def main(args=None):
rclpy.try_shutdown()


if __name__ == '__main__':
if __name__ == "__main__":
main()
Loading

0 comments on commit 55a0272

Please sign in to comment.