diff --git a/src/lerobot/datasets/audio_utils.py b/src/lerobot/datasets/audio_utils.py index 5cc0b83f8..d073109a0 100644 --- a/src/lerobot/datasets/audio_utils.py +++ b/src/lerobot/datasets/audio_utils.py @@ -73,14 +73,14 @@ def decode_audio_torchaudio( audio_path = str(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 ? reader.add_basic_audio_stream( - frames_per_chunk=int(ceil(duration * audio_sampling_rate)), # Too much is better than not enough - buffer_chunk_size=-1, # No dropping frames - format="fltp", # Format as float32 + frames_per_chunk = int(ceil(duration * audio_sample_rate)), #Too much is better than not enough + buffer_chunk_size = -1, #No dropping frames + format = "fltp", #Format as float32 ) audio_chunks = [] @@ -93,9 +93,7 @@ def decode_audio_torchaudio( current_audio_chunk = reader.pop_chunks()[0] if log_loaded_timestamps: - logging.info( - f"audio chunk loaded at starting timestamp={current_audio_chunk['pts']:.4f} with duration={len(current_audio_chunk) / audio_sampling_rate:.4f}" - ) + logging.info(f"audio chunk loaded at starting timestamp={current_audio_chunk['pts']:.4f} with duration={len(current_audio_chunk) / audio_sample_rate:.4f}") audio_chunks.append(current_audio_chunk) diff --git a/src/lerobot/microphones/configs.py b/src/lerobot/microphones/configs.py index 8239506a1..b1858865a 100644 --- a/src/lerobot/microphones/configs.py +++ b/src/lerobot/microphones/configs.py @@ -21,7 +21,7 @@ import draccus @dataclass(kw_only=True) class MicrophoneConfig(draccus.ChoiceRegistry, abc.ABC): microphone_index: int - sampling_rate: int | None = None + sample_rate: int | None = None channels: list[int] | None = None @property diff --git a/src/lerobot/microphones/microphone.py b/src/lerobot/microphones/microphone.py index e1d151ce1..092ab95c9 100644 --- a/src/lerobot/microphones/microphone.py +++ b/src/lerobot/microphones/microphone.py @@ -73,7 +73,7 @@ def record_audio_from_microphones( microphone = Microphone(config) microphone.connect() 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) @@ -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). - 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: ```python 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.connect() @@ -130,8 +130,8 @@ class Microphone: self.config = config self.microphone_index = config.microphone_index - # Store the recording sampling rate and channels - self.sampling_rate = config.sampling_rate + #Store the recording sample rate and channels + self.sample_rate = config.sample_rate self.channels = config.channels # Input audio stream @@ -166,17 +166,15 @@ class Microphone: # Check if provided recording parameters are compatible with the microphone actual_microphone = sd.query_devices(self.microphone_index) - if self.sampling_rate is not None: - if self.sampling_rate > actual_microphone["default_samplerate"]: + if self.sample_rate is not None : + if self.sample_rate > actual_microphone["default_samplerate"]: raise OSError( - f"Provided sampling rate {self.sampling_rate} is higher than the sampling 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." + f"Provided sample rate {self.sample_rate} is higher than the sample rate of the microphone {actual_microphone['default_samplerate']}." ) + 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: - self.sampling_rate = int(actual_microphone["default_samplerate"]) + self.sample_rate = int(actual_microphone["default_samplerate"]) if self.channels is not None: if any(c > actual_microphone["max_input_channels"] for c in self.channels): @@ -192,8 +190,8 @@ class Microphone: # Create the audio stream self.stream = sd.InputStream( device=self.microphone_index, - samplerate=self.sampling_rate, - channels=max(self.channels) + 1, + samplerate=self.sample_rate, + channels=max(self.channels)+1, dtype="float32", callback=self._audio_callback, ) @@ -211,14 +209,9 @@ class Microphone: self.read_queue.put(indata[:, self.channels]) def _record_loop(self, output_file: Path) -> None: - # Can only be run on a single process/thread for file writing safety - with sf.SoundFile( - output_file, - mode="x", - samplerate=self.sampling_rate, - channels=max(self.channels) + 1, - subtype=sf.default_subtype(output_file.suffix[1:]), - ) as file: + #Can only be run on a single process/thread for file writing safety + with sf.SoundFile(output_file, mode='x', samplerate=self.sample_rate, + channels=max(self.channels)+1, subtype=sf.default_subtype(output_file.suffix[1:])) as file: while not self.record_stop_event.is_set(): file.write(self.record_queue.get()) # self.record_queue.task_done() diff --git a/src/lerobot/robots/config.py b/src/lerobot/robots/config.py index 39799a45a..e142fe34e 100644 --- a/src/lerobot/robots/config.py +++ b/src/lerobot/robots/config.py @@ -36,7 +36,7 @@ class RobotConfig(draccus.ChoiceRegistry, abc.ABC): ) if hasattr(self, "microphones") and self.microphones: for _, config in self.microphones.items(): - for attr in ["sampling_rate", "channels"]: + for attr in ["sample_rate", "channels"]: if getattr(config, attr) is None: raise ValueError( f"Specifying '{attr}' is required for the microphone to be used in a robot" diff --git a/src/lerobot/robots/koch_follower/koch_follower.py b/src/lerobot/robots/koch_follower/koch_follower.py index 5333222e6..9921d416b 100644 --- a/src/lerobot/robots/koch_follower/koch_follower.py +++ b/src/lerobot/robots/koch_follower/koch_follower.py @@ -77,8 +77,7 @@ class KochFollower(Robot): @property def _microphones_ft(self) -> dict[str, tuple]: return { - mic: (self.config.microphones[mic].sampling_rate, self.config.microphones[mic].channels) - for mic in self.microphones + mic: (self.config.microphones[mic].sample_rate, self.config.microphones[mic].channels) for mic in self.microphones } @cached_property diff --git a/src/lerobot/robots/lekiwi/lekiwi.py b/src/lerobot/robots/lekiwi/lekiwi.py index 0e8341713..5bf3ca41b 100644 --- a/src/lerobot/robots/lekiwi/lekiwi.py +++ b/src/lerobot/robots/lekiwi/lekiwi.py @@ -102,8 +102,7 @@ class LeKiwi(Robot): @property def _microphones_ft(self) -> dict[str, tuple]: return { - mic: (self.config.microphones[mic].sampling_rate, self.config.microphones[mic].channels) - for mic in self.microphones + mic: (self.config.microphones[mic].sample_rate, self.config.microphones[mic].channels) for mic in self.microphones } @cached_property diff --git a/src/lerobot/robots/lekiwi/lekiwi_client.py b/src/lerobot/robots/lekiwi/lekiwi_client.py index 43b925bcf..889a0e405 100644 --- a/src/lerobot/robots/lekiwi/lekiwi_client.py +++ b/src/lerobot/robots/lekiwi/lekiwi_client.py @@ -99,7 +99,7 @@ class LeKiwiClient(Robot): @cached_property 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 def observation_features(self) -> dict[str, type | tuple]: diff --git a/src/lerobot/robots/so_follower/so_follower.py b/src/lerobot/robots/so_follower/so_follower.py index f4e0197e0..17ed4c2c2 100644 --- a/src/lerobot/robots/so_follower/so_follower.py +++ b/src/lerobot/robots/so_follower/so_follower.py @@ -78,8 +78,7 @@ class SOFollower(Robot): @property def _microphones_ft(self) -> dict[str, tuple]: return { - mic: (self.config.microphones[mic].sampling_rate, self.config.microphones[mic].channels) - for mic in self.microphones + mic: (self.config.microphones[mic].sample_rate, self.config.microphones[mic].channels) for mic in self.microphones } @cached_property