mirror of
https://github.com/huggingface/lerobot.git
synced 2026-07-24 18:26:11 +00:00
Renamming sampling rate to sample rate for consistency
This commit is contained in:
@@ -73,14 +73,14 @@ def decode_audio_torchaudio(
|
|||||||
audio_path = str(audio_path)
|
audio_path = str(audio_path)
|
||||||
|
|
||||||
reader = torchaudio.io.StreamReader(src=audio_path)
|
reader = torchaudio.io.StreamReader(src=audio_path)
|
||||||
audio_sampling_rate = reader.get_src_stream_info(reader.default_audio_stream).sample_rate
|
audio_sample_rate = reader.get_src_stream_info(reader.default_audio_stream).sample_rate
|
||||||
|
|
||||||
# TODO(CarolinePascal) : sort timestamps ?
|
# TODO(CarolinePascal) : sort timestamps ?
|
||||||
|
|
||||||
reader.add_basic_audio_stream(
|
reader.add_basic_audio_stream(
|
||||||
frames_per_chunk=int(ceil(duration * audio_sampling_rate)), # Too much is better than not enough
|
frames_per_chunk = int(ceil(duration * audio_sample_rate)), #Too much is better than not enough
|
||||||
buffer_chunk_size=-1, # No dropping frames
|
buffer_chunk_size = -1, #No dropping frames
|
||||||
format="fltp", # Format as float32
|
format = "fltp", #Format as float32
|
||||||
)
|
)
|
||||||
|
|
||||||
audio_chunks = []
|
audio_chunks = []
|
||||||
@@ -93,9 +93,7 @@ def decode_audio_torchaudio(
|
|||||||
current_audio_chunk = reader.pop_chunks()[0]
|
current_audio_chunk = reader.pop_chunks()[0]
|
||||||
|
|
||||||
if log_loaded_timestamps:
|
if log_loaded_timestamps:
|
||||||
logging.info(
|
logging.info(f"audio chunk loaded at starting timestamp={current_audio_chunk['pts']:.4f} with duration={len(current_audio_chunk) / audio_sample_rate:.4f}")
|
||||||
f"audio chunk loaded at starting timestamp={current_audio_chunk['pts']:.4f} with duration={len(current_audio_chunk) / audio_sampling_rate:.4f}"
|
|
||||||
)
|
|
||||||
|
|
||||||
audio_chunks.append(current_audio_chunk)
|
audio_chunks.append(current_audio_chunk)
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import draccus
|
|||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class MicrophoneConfig(draccus.ChoiceRegistry, abc.ABC):
|
class MicrophoneConfig(draccus.ChoiceRegistry, abc.ABC):
|
||||||
microphone_index: int
|
microphone_index: int
|
||||||
sampling_rate: int | None = None
|
sample_rate: int | None = None
|
||||||
channels: list[int] | None = None
|
channels: list[int] | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ def record_audio_from_microphones(
|
|||||||
microphone = Microphone(config)
|
microphone = Microphone(config)
|
||||||
microphone.connect()
|
microphone.connect()
|
||||||
print(
|
print(
|
||||||
f"Recording audio from microphone {microphone_id} for {record_time_s} seconds at {microphone.sampling_rate} Hz."
|
f"Recording audio from microphone {microphone_id} for {record_time_s} seconds at {microphone.sample_rate} Hz."
|
||||||
)
|
)
|
||||||
microphones.append(microphone)
|
microphones.append(microphone)
|
||||||
|
|
||||||
@@ -105,13 +105,13 @@ class Microphone:
|
|||||||
"""
|
"""
|
||||||
The Microphone class handles all microphones compatible with sounddevice (and the underlying PortAudio library). Most microphones and sound cards are compatible, across all OS (Linux, Mac, Windows).
|
The Microphone class handles all microphones compatible with sounddevice (and the underlying PortAudio library). Most microphones and sound cards are compatible, across all OS (Linux, Mac, Windows).
|
||||||
|
|
||||||
A Microphone instance requires the sounddevice index of the microphone, which may be obtained using `python -m sounddevice`. It also requires the recording sampling rate as well as the list of recorded channels.
|
A Microphone instance requires the sounddevice index of the microphone, which may be obtained using `python -m sounddevice`. It also requires the recording sample rate as well as the list of recorded channels.
|
||||||
|
|
||||||
Example of usage:
|
Example of usage:
|
||||||
```python
|
```python
|
||||||
from lerobot.common.robot_devices.microphones.configs import MicrophoneConfig
|
from lerobot.common.robot_devices.microphones.configs import MicrophoneConfig
|
||||||
|
|
||||||
config = MicrophoneConfig(microphone_index=0, sampling_rate=16000, channels=[1])
|
config = MicrophoneConfig(microphone_index=0, sample_rate=16000, channels=[1])
|
||||||
microphone = Microphone(config)
|
microphone = Microphone(config)
|
||||||
|
|
||||||
microphone.connect()
|
microphone.connect()
|
||||||
@@ -130,8 +130,8 @@ class Microphone:
|
|||||||
self.config = config
|
self.config = config
|
||||||
self.microphone_index = config.microphone_index
|
self.microphone_index = config.microphone_index
|
||||||
|
|
||||||
# Store the recording sampling rate and channels
|
#Store the recording sample rate and channels
|
||||||
self.sampling_rate = config.sampling_rate
|
self.sample_rate = config.sample_rate
|
||||||
self.channels = config.channels
|
self.channels = config.channels
|
||||||
|
|
||||||
# Input audio stream
|
# Input audio stream
|
||||||
@@ -166,17 +166,15 @@ class Microphone:
|
|||||||
# Check if provided recording parameters are compatible with the microphone
|
# Check if provided recording parameters are compatible with the microphone
|
||||||
actual_microphone = sd.query_devices(self.microphone_index)
|
actual_microphone = sd.query_devices(self.microphone_index)
|
||||||
|
|
||||||
if self.sampling_rate is not None:
|
if self.sample_rate is not None :
|
||||||
if self.sampling_rate > actual_microphone["default_samplerate"]:
|
if self.sample_rate > actual_microphone["default_samplerate"]:
|
||||||
raise OSError(
|
raise OSError(
|
||||||
f"Provided sampling rate {self.sampling_rate} is higher than the sampling rate of the microphone {actual_microphone['default_samplerate']}."
|
f"Provided sample rate {self.sample_rate} is higher than the sample rate of the microphone {actual_microphone['default_samplerate']}."
|
||||||
)
|
|
||||||
elif self.sampling_rate < actual_microphone["default_samplerate"]:
|
|
||||||
logging.warning(
|
|
||||||
"Provided sampling rate is lower than the sampling rate of the microphone. Performance may be impacted."
|
|
||||||
)
|
)
|
||||||
|
elif self.sample_rate < actual_microphone["default_samplerate"]:
|
||||||
|
logging.warning("Provided sample rate is lower than the sample rate of the microphone. Performance may be impacted.")
|
||||||
else:
|
else:
|
||||||
self.sampling_rate = int(actual_microphone["default_samplerate"])
|
self.sample_rate = int(actual_microphone["default_samplerate"])
|
||||||
|
|
||||||
if self.channels is not None:
|
if self.channels is not None:
|
||||||
if any(c > actual_microphone["max_input_channels"] for c in self.channels):
|
if any(c > actual_microphone["max_input_channels"] for c in self.channels):
|
||||||
@@ -192,8 +190,8 @@ class Microphone:
|
|||||||
# Create the audio stream
|
# Create the audio stream
|
||||||
self.stream = sd.InputStream(
|
self.stream = sd.InputStream(
|
||||||
device=self.microphone_index,
|
device=self.microphone_index,
|
||||||
samplerate=self.sampling_rate,
|
samplerate=self.sample_rate,
|
||||||
channels=max(self.channels) + 1,
|
channels=max(self.channels)+1,
|
||||||
dtype="float32",
|
dtype="float32",
|
||||||
callback=self._audio_callback,
|
callback=self._audio_callback,
|
||||||
)
|
)
|
||||||
@@ -211,14 +209,9 @@ class Microphone:
|
|||||||
self.read_queue.put(indata[:, self.channels])
|
self.read_queue.put(indata[:, self.channels])
|
||||||
|
|
||||||
def _record_loop(self, output_file: Path) -> None:
|
def _record_loop(self, output_file: Path) -> None:
|
||||||
# Can only be run on a single process/thread for file writing safety
|
#Can only be run on a single process/thread for file writing safety
|
||||||
with sf.SoundFile(
|
with sf.SoundFile(output_file, mode='x', samplerate=self.sample_rate,
|
||||||
output_file,
|
channels=max(self.channels)+1, subtype=sf.default_subtype(output_file.suffix[1:])) as file:
|
||||||
mode="x",
|
|
||||||
samplerate=self.sampling_rate,
|
|
||||||
channels=max(self.channels) + 1,
|
|
||||||
subtype=sf.default_subtype(output_file.suffix[1:]),
|
|
||||||
) as file:
|
|
||||||
while not self.record_stop_event.is_set():
|
while not self.record_stop_event.is_set():
|
||||||
file.write(self.record_queue.get())
|
file.write(self.record_queue.get())
|
||||||
# self.record_queue.task_done()
|
# self.record_queue.task_done()
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class RobotConfig(draccus.ChoiceRegistry, abc.ABC):
|
|||||||
)
|
)
|
||||||
if hasattr(self, "microphones") and self.microphones:
|
if hasattr(self, "microphones") and self.microphones:
|
||||||
for _, config in self.microphones.items():
|
for _, config in self.microphones.items():
|
||||||
for attr in ["sampling_rate", "channels"]:
|
for attr in ["sample_rate", "channels"]:
|
||||||
if getattr(config, attr) is None:
|
if getattr(config, attr) is None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Specifying '{attr}' is required for the microphone to be used in a robot"
|
f"Specifying '{attr}' is required for the microphone to be used in a robot"
|
||||||
|
|||||||
@@ -77,8 +77,7 @@ class KochFollower(Robot):
|
|||||||
@property
|
@property
|
||||||
def _microphones_ft(self) -> dict[str, tuple]:
|
def _microphones_ft(self) -> dict[str, tuple]:
|
||||||
return {
|
return {
|
||||||
mic: (self.config.microphones[mic].sampling_rate, self.config.microphones[mic].channels)
|
mic: (self.config.microphones[mic].sample_rate, self.config.microphones[mic].channels) for mic in self.microphones
|
||||||
for mic in self.microphones
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|||||||
@@ -102,8 +102,7 @@ class LeKiwi(Robot):
|
|||||||
@property
|
@property
|
||||||
def _microphones_ft(self) -> dict[str, tuple]:
|
def _microphones_ft(self) -> dict[str, tuple]:
|
||||||
return {
|
return {
|
||||||
mic: (self.config.microphones[mic].sampling_rate, self.config.microphones[mic].channels)
|
mic: (self.config.microphones[mic].sample_rate, self.config.microphones[mic].channels) for mic in self.microphones
|
||||||
for mic in self.microphones
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class LeKiwiClient(Robot):
|
|||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def _microphones_ft(self) -> dict[str, tuple]:
|
def _microphones_ft(self) -> dict[str, tuple]:
|
||||||
return {name: (cfg.sampling_rate, cfg.channels) for name, cfg in self.config.microphones.items()}
|
return {name: (cfg.sample_rate, cfg.channels) for name, cfg in self.config.microphones.items()}
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def observation_features(self) -> dict[str, type | tuple]:
|
def observation_features(self) -> dict[str, type | tuple]:
|
||||||
|
|||||||
@@ -78,8 +78,7 @@ class SOFollower(Robot):
|
|||||||
@property
|
@property
|
||||||
def _microphones_ft(self) -> dict[str, tuple]:
|
def _microphones_ft(self) -> dict[str, tuple]:
|
||||||
return {
|
return {
|
||||||
mic: (self.config.microphones[mic].sampling_rate, self.config.microphones[mic].channels)
|
mic: (self.config.microphones[mic].sample_rate, self.config.microphones[mic].channels) for mic in self.microphones
|
||||||
for mic in self.microphones
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|||||||
Reference in New Issue
Block a user