prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  comment
  would be nice to have a comment here mentioning that DummySet contains everything.
  
  https://github.com/pzelasko/lhotse/blob/7555df605def57836c9454ae44aac95c504d86b0/lhotse/audio.py#L77
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `lhotse/utils.py`
  Content:
  ```
  1 from pathlib import Path
  2 from typing import Union
  3 
  4 Pathlike = Union[Path, str]
  5 
  6 INT16MAX = 32768
  7 
  8 
  9 class DummySet:
  10     def __contains__(self, item):
  11         return True
  12 
  13     def intersection(self, iterable):
  14         return True
  15
  ```
  
  Path: `lhotse/audio.py`
  Content:
  ```
  1 from dataclasses import dataclass, asdict
  2 from subprocess import run, PIPE
  3 from typing import List, Optional, Dict, Union
  4 
  5 import librosa
  6 import numpy as np
  7 import yaml
  8 
  9 from lhotse.utils import Pathlike, INT16MAX, DummySet
  10 
  11 Channels = Union[int, List[int]]
  12 
  13 
  14 @dataclass
  15 class AudioSource:
  16     """
  17     AudioSource represents audio data that can be retrieved from somewhere.
  18     Supported sources of audio are currently:
  19     - a file (possibly multi-channel)
  20     - a command/unix pipe (single-channel only)
  21     - a collection of any of the above (see AudioSourceCollection)
  22     """
  23     type: str
  24     channel_ids: List[int]
  25     source: str
  26 
  27     def load_audio(
  28             self,
  29             offset_seconds: float = 0.0,
  30             duration_seconds: Optional[float] = None
  31     ) -> np.ndarray:
  32         assert self.type in ('file', 'command')
  33 
  34         if self.type == 'file':
  35             # TODO(pzelasko): make sure that librosa loads multi-channel audio
  36             #                 in the expected format (n_channels, n_samples)
  37             return librosa.load(
  38                 self.source,
  39                 sr=None,  # 'None' uses the native sampling rate
  40                 offset=offset_seconds,
  41                 duration=duration_seconds
  42             )[0]  # discard returned sampling rate
  43 
  44         # TODO(pzelasko): the following naively assumes we're dealing with raw PCM...
  45         #                 not sure if that's how we should do it
  46         #                 also, how should we support chunking for commands?
  47         raw_audio = run(self.source, shell=True, stdout=PIPE).stdout
  48         int16_audio = np.frombuffer(raw_audio, dtype=np.int16)
  49         return int16_audio / INT16MAX
  50 
  51 
  52 @dataclass
  53 class Recording:
  54     """
  55     Recording represents an AudioSource along with some metadata.
  56     """
  57     id: str
  58     sources: List[AudioSource]
  59     sampling_rate: int
  60     num_samples: int
  61     duration_seconds: float
  62 
  63     def __post_init__(self):
  64         self.sources = [AudioSource(**s) if isinstance(s, dict) else s for s in self.sources]
  65 
  66     @property
  67     def num_channels(self):
  68         return sum(len(source.channel_ids) for source in self.sources)
  69 
  70     def load_audio(
  71             self,
  72             channels: Optional[Channels] = None,
  73             offset_seconds: float = 0.0,
  74             duration_seconds: Optional[float] = None
  75     ) -> np.ndarray:
  76         if channels is None:
  77             channels = DummySet()
  78         elif isinstance(channels, int):
  79             channels = frozenset([channels])
  80         else:
  81             channels = frozenset(channels)
  82 
  83         samples_per_source = []
  84         for source in self.sources:
  85             # Case: source not requested
  86             if not channels.intersection(source.channel_ids):
  87                 continue
  88             samples = source.load_audio(offset_seconds=offset_seconds, duration_seconds=duration_seconds)
  89 
  90             # Case: two-channel audio file but only one channel requested
  91             #       it might not be optimal to load all channels, but IDK if there's anything we can do about it
  92             channels_to_remove = [
  93                 idx for idx, cid in enumerate(source.channel_ids)
  94                 if cid not in channels
  95             ]
  96             if channels_to_remove:
  97                 samples = np.delete(samples, channels_to_remove, axis=0)
  98             samples_per_source.append(samples)
  99 
  100         # shapes: single-channel (n_samples); multi-channel (n_channels, n_samples)
  101         return np.vstack(samples_per_source) if len(samples_per_source) > 1 else samples_per_source[0]
  102 
  103 
  104 @dataclass
  105 class AudioSet:
  106     """
  107     AudioSet represents a dataset of recordings. It does not contain any annotation -
  108     just the information needed to retrieve a recording (possibly multi-channel, from files
  109     or from shell commands and pipes) and some metadata for each of them.
  110 
  111     It also supports (de)serialization to/from YAML and takes care of mapping between
  112     rich Python classes and YAML primitives during conversion.
  113     """
  114     recordings: Dict[str, Recording]
  115 
  116     @staticmethod
  117     def from_yaml(path: Pathlike) -> 'AudioSet':
  118         with open(path) as f:
  119             recordings = (Recording(**raw_rec) for raw_rec in yaml.safe_load(f))
  120         return AudioSet(recordings={r.id: r for r in recordings})
  121 
  122     def to_yaml(self, path: Pathlike):
  123         with open(path, 'w') as f:
  124             yaml.safe_dump([asdict(r) for r in self.recordings.values()], stream=f)
  125 
  126     def load_audio(
  127             self,
  128             recording_id: str,
  129             channels: Optional[Channels] = None,
  130             offset_seconds: float = 0.0,
  131             duration_seconds: Optional[float] = None
  132     ) -> np.ndarray:
  133         return self.recordings[recording_id].load_audio(
  134             channels=channels,
  135             offset_seconds=offset_seconds,
  136             duration_seconds=duration_seconds
  137         )
  138 
  139     def num_channels(self, recording_id: str) -> int:
  140         return self.recordings[recording_id].num_channels
  141 
  142     def sampling_rate(self, recording_id: str) -> int:
  143         return self.recordings[recording_id].sampling_rate
  144 
  145     def num_samples(self, recording_id: str) -> int:
  146         return self.recordings[recording_id].num_samples
  147 
  148     def duration_seconds(self, recording_id: str) -> float:
  149         return self.recordings[recording_id].duration_seconds
  150 
  151     def __iter__(self):
  152         return iter(self.recordings.values())
  153
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/lhotse/audio.py b/lhotse/audio.py
  --- a/lhotse/audio.py
  +++ b/lhotse/audio.py
  @@ -1,4 +1,6 @@
   from dataclasses import dataclass, asdict
  +from dataclasses import dataclass, asdict
  +from io import BytesIO
   from subprocess import run, PIPE
   from typing import List, Optional, Dict, Union
   
  @@ -6,7 +8,7 @@
   import numpy as np
   import yaml
   
  -from lhotse.utils import Pathlike, INT16MAX, DummySet
  +from lhotse.utils import Pathlike, SetContainingAnything
   
   Channels = Union[int, List[int]]
   
  @@ -16,9 +18,8 @@
       """
       AudioSource represents audio data that can be retrieved from somewhere.
       Supported sources of audio are currently:
  -    - a file (possibly multi-channel)
  -    - a command/unix pipe (single-channel only)
  -    - a collection of any of the above (see AudioSourceCollection)
  +    - a file (formats supported by librosa, possibly multi-channel)
  +    - a command/unix pipe (must be WAVE, possibly multi-channel)
       """
       type: str
       channel_ids: List[int]
  @@ -29,24 +30,31 @@
               offset_seconds: float = 0.0,
               duration_seconds: Optional[float] = None
       ) -> np.ndarray:
  +        """
  +        Load the AudioSource (both files and commands) with librosa,
  +        accounting for many audio formats and multi-channel inputs.
  +        Returns numpy array with shapes: (n_samples) for single-channel,
  +        (n_channels, n_samples) for multi-channel.
  +        """
           assert self.type in ('file', 'command')
   
  -        if self.type == 'file':
  -            # TODO(pzelasko): make sure that librosa loads multi-channel audio
  -            #                 in the expected format (n_channels, n_samples)
  -            return librosa.load(
  -                self.source,
  -                sr=None,  # 'None' uses the native sampling rate
  -                offset=offset_seconds,
  -                duration=duration_seconds
  -            )[0]  # discard returned sampling rate
  +        if self.type == 'command':
  +            if offset_seconds != 0.0 or duration_seconds is not None:
  +                # TODO(pzelasko): How should we support chunking for commands?
  +                #                 We risk being very inefficient when reading many chunks from the same file
  +                #                 without some caching scheme, because we'll be re-running commands.
  +                raise ValueError("Reading audio chunks from command AudioSource type is currently not supported.")
  +            source = BytesIO(run(self.source, shell=True, stdout=PIPE).stdout)
  +        else:
  +            source = self.source
   
  -        # TODO(pzelasko): the following naively assumes we're dealing with raw PCM...
  -        #                 not sure if that's how we should do it
  -        #                 also, how should we support chunking for commands?
  -        raw_audio = run(self.source, shell=True, stdout=PIPE).stdout
  -        int16_audio = np.frombuffer(raw_audio, dtype=np.int16)
  -        return int16_audio / INT16MAX
  +        return librosa.load(
  +            source,
  +            sr=None,  # 'None' uses the native sampling rate
  +            mono=False,  # Retain multi-channel if it's there
  +            offset=offset_seconds,
  +            duration=duration_seconds
  +        )[0]  # discard returned sampling rate
   
   
   @dataclass
  @@ -74,7 +82,7 @@
               duration_seconds: Optional[float] = None
       ) -> np.ndarray:
           if channels is None:
  -            channels = DummySet()
  +            channels = SetContainingAnything()
           elif isinstance(channels, int):
               channels = frozenset([channels])
           else:
  diff --git a/lhotse/utils.py b/lhotse/utils.py
  --- a/lhotse/utils.py
  +++ b/lhotse/utils.py
  @@ -6,7 +6,7 @@
   INT16MAX = 32768
   
   
  -class DummySet:
  +class SetContainingAnything:
       def __contains__(self, item):
           return True
  
verification_info: {"golden_diff": "diff --git a/lhotse/audio.py b/lhotse/audio.py\n--- a/lhotse/audio.py\n+++ b/lhotse/audio.py\n@@ -1,4 +1,6 @@\n from dataclasses import dataclass, asdict\n+from dataclasses import dataclass, asdict\n+from io import BytesIO\n from subprocess import run, PIPE\n from typing import List, Optional, Dict, Union\n \n@@ -6,7 +8,7 @@\n import numpy as np\n import yaml\n \n-from lhotse.utils import Pathlike, INT16MAX, DummySet\n+from lhotse.utils import Pathlike, SetContainingAnything\n \n Channels = Union[int, List[int]]\n \n@@ -16,9 +18,8 @@\n     \"\"\"\n     AudioSource represents audio data that can be retrieved from somewhere.\n     Supported sources of audio are currently:\n-    - a file (possibly multi-channel)\n-    - a command/unix pipe (single-channel only)\n-    - a collection of any of the above (see AudioSourceCollection)\n+    - a file (formats supported by librosa, possibly multi-channel)\n+    - a command/unix pipe (must be WAVE, possibly multi-channel)\n     \"\"\"\n     type: str\n     channel_ids: List[int]\n@@ -29,24 +30,31 @@\n             offset_seconds: float = 0.0,\n             duration_seconds: Optional[float] = None\n     ) -> np.ndarray:\n+        \"\"\"\n+        Load the AudioSource (both files and commands) with librosa,\n+        accounting for many audio formats and multi-channel inputs.\n+        Returns numpy array with shapes: (n_samples) for single-channel,\n+        (n_channels, n_samples) for multi-channel.\n+        \"\"\"\n         assert self.type in ('file', 'command')\n \n-        if self.type == 'file':\n-            # TODO(pzelasko): make sure that librosa loads multi-channel audio\n-            #                 in the expected format (n_channels, n_samples)\n-            return librosa.load(\n-                self.source,\n-                sr=None,  # 'None' uses the native sampling rate\n-                offset=offset_seconds,\n-                duration=duration_seconds\n-            )[0]  # discard returned sampling rate\n+        if self.type == 'command':\n+            if offset_seconds != 0.0 or duration_seconds is not None:\n+                # TODO(pzelasko): How should we support chunking for commands?\n+                #                 We risk being very inefficient when reading many chunks from the same file\n+                #                 without some caching scheme, because we'll be re-running commands.\n+                raise ValueError(\"Reading audio chunks from command AudioSource type is currently not supported.\")\n+            source = BytesIO(run(self.source, shell=True, stdout=PIPE).stdout)\n+        else:\n+            source = self.source\n \n-        # TODO(pzelasko): the following naively assumes we're dealing with raw PCM...\n-        #                 not sure if that's how we should do it\n-        #                 also, how should we support chunking for commands?\n-        raw_audio = run(self.source, shell=True, stdout=PIPE).stdout\n-        int16_audio = np.frombuffer(raw_audio, dtype=np.int16)\n-        return int16_audio / INT16MAX\n+        return librosa.load(\n+            source,\n+            sr=None,  # 'None' uses the native sampling rate\n+            mono=False,  # Retain multi-channel if it's there\n+            offset=offset_seconds,\n+            duration=duration_seconds\n+        )[0]  # discard returned sampling rate\n \n \n @dataclass\n@@ -74,7 +82,7 @@\n             duration_seconds: Optional[float] = None\n     ) -> np.ndarray:\n         if channels is None:\n-            channels = DummySet()\n+            channels = SetContainingAnything()\n         elif isinstance(channels, int):\n             channels = frozenset([channels])\n         else:\ndiff --git a/lhotse/utils.py b/lhotse/utils.py\n--- a/lhotse/utils.py\n+++ b/lhotse/utils.py\n@@ -6,7 +6,7 @@\n INT16MAX = 32768\n \n \n-class DummySet:\n+class SetContainingAnything:\n     def __contains__(self, item):\n         return True\n", "issue": "comment\nwould be nice to have a comment here mentioning that DummySet contains everything.\r\n\r\nhttps://github.com/pzelasko/lhotse/blob/7555df605def57836c9454ae44aac95c504d86b0/lhotse/audio.py#L77\n", "before_files": [{"content": "from pathlib import Path\nfrom typing import Union\n\nPathlike = Union[Path, str]\n\nINT16MAX = 32768\n\n\nclass DummySet:\n    def __contains__(self, item):\n        return True\n\n    def intersection(self, iterable):\n        return True\n", "path": "lhotse/utils.py"}, {"content": "from dataclasses import dataclass, asdict\nfrom subprocess import run, PIPE\nfrom typing import List, Optional, Dict, Union\n\nimport librosa\nimport numpy as np\nimport yaml\n\nfrom lhotse.utils import Pathlike, INT16MAX, DummySet\n\nChannels = Union[int, List[int]]\n\n\n@dataclass\nclass AudioSource:\n    \"\"\"\n    AudioSource represents audio data that can be retrieved from somewhere.\n    Supported sources of audio are currently:\n    - a file (possibly multi-channel)\n    - a command/unix pipe (single-channel only)\n    - a collection of any of the above (see AudioSourceCollection)\n    \"\"\"\n    type: str\n    channel_ids: List[int]\n    source: str\n\n    def load_audio(\n            self,\n            offset_seconds: float = 0.0,\n            duration_seconds: Optional[float] = None\n    ) -> np.ndarray:\n        assert self.type in ('file', 'command')\n\n        if self.type == 'file':\n            # TODO(pzelasko): make sure that librosa loads multi-channel audio\n            #                 in the expected format (n_channels, n_samples)\n            return librosa.load(\n                self.source,\n                sr=None,  # 'None' uses the native sampling rate\n                offset=offset_seconds,\n                duration=duration_seconds\n            )[0]  # discard returned sampling rate\n\n        # TODO(pzelasko): the following naively assumes we're dealing with raw PCM...\n        #                 not sure if that's how we should do it\n        #                 also, how should we support chunking for commands?\n        raw_audio = run(self.source, shell=True, stdout=PIPE).stdout\n        int16_audio = np.frombuffer(raw_audio, dtype=np.int16)\n        return int16_audio / INT16MAX\n\n\n@dataclass\nclass Recording:\n    \"\"\"\n    Recording represents an AudioSource along with some metadata.\n    \"\"\"\n    id: str\n    sources: List[AudioSource]\n    sampling_rate: int\n    num_samples: int\n    duration_seconds: float\n\n    def __post_init__(self):\n        self.sources = [AudioSource(**s) if isinstance(s, dict) else s for s in self.sources]\n\n    @property\n    def num_channels(self):\n        return sum(len(source.channel_ids) for source in self.sources)\n\n    def load_audio(\n            self,\n            channels: Optional[Channels] = None,\n            offset_seconds: float = 0.0,\n            duration_seconds: Optional[float] = None\n    ) -> np.ndarray:\n        if channels is None:\n            channels = DummySet()\n        elif isinstance(channels, int):\n            channels = frozenset([channels])\n        else:\n            channels = frozenset(channels)\n\n        samples_per_source = []\n        for source in self.sources:\n            # Case: source not requested\n            if not channels.intersection(source.channel_ids):\n                continue\n            samples = source.load_audio(offset_seconds=offset_seconds, duration_seconds=duration_seconds)\n\n            # Case: two-channel audio file but only one channel requested\n            #       it might not be optimal to load all channels, but IDK if there's anything we can do about it\n            channels_to_remove = [\n                idx for idx, cid in enumerate(source.channel_ids)\n                if cid not in channels\n            ]\n            if channels_to_remove:\n                samples = np.delete(samples, channels_to_remove, axis=0)\n            samples_per_source.append(samples)\n\n        # shapes: single-channel (n_samples); multi-channel (n_channels, n_samples)\n        return np.vstack(samples_per_source) if len(samples_per_source) > 1 else samples_per_source[0]\n\n\n@dataclass\nclass AudioSet:\n    \"\"\"\n    AudioSet represents a dataset of recordings. It does not contain any annotation -\n    just the information needed to retrieve a recording (possibly multi-channel, from files\n    or from shell commands and pipes) and some metadata for each of them.\n\n    It also supports (de)serialization to/from YAML and takes care of mapping between\n    rich Python classes and YAML primitives during conversion.\n    \"\"\"\n    recordings: Dict[str, Recording]\n\n    @staticmethod\n    def from_yaml(path: Pathlike) -> 'AudioSet':\n        with open(path) as f:\n            recordings = (Recording(**raw_rec) for raw_rec in yaml.safe_load(f))\n        return AudioSet(recordings={r.id: r for r in recordings})\n\n    def to_yaml(self, path: Pathlike):\n        with open(path, 'w') as f:\n            yaml.safe_dump([asdict(r) for r in self.recordings.values()], stream=f)\n\n    def load_audio(\n            self,\n            recording_id: str,\n            channels: Optional[Channels] = None,\n            offset_seconds: float = 0.0,\n            duration_seconds: Optional[float] = None\n    ) -> np.ndarray:\n        return self.recordings[recording_id].load_audio(\n            channels=channels,\n            offset_seconds=offset_seconds,\n            duration_seconds=duration_seconds\n        )\n\n    def num_channels(self, recording_id: str) -> int:\n        return self.recordings[recording_id].num_channels\n\n    def sampling_rate(self, recording_id: str) -> int:\n        return self.recordings[recording_id].sampling_rate\n\n    def num_samples(self, recording_id: str) -> int:\n        return self.recordings[recording_id].num_samples\n\n    def duration_seconds(self, recording_id: str) -> float:\n        return self.recordings[recording_id].duration_seconds\n\n    def __iter__(self):\n        return iter(self.recordings.values())\n", "path": "lhotse/audio.py"}], "after_files": [{"content": "from pathlib import Path\nfrom typing import Union\n\nPathlike = Union[Path, str]\n\nINT16MAX = 32768\n\n\nclass SetContainingAnything:\n    def __contains__(self, item):\n        return True\n\n    def intersection(self, iterable):\n        return True\n", "path": "lhotse/utils.py"}, {"content": "from dataclasses import dataclass, asdict\nfrom dataclasses import dataclass, asdict\nfrom io import BytesIO\nfrom subprocess import run, PIPE\nfrom typing import List, Optional, Dict, Union\n\nimport librosa\nimport numpy as np\nimport yaml\n\nfrom lhotse.utils import Pathlike, SetContainingAnything\n\nChannels = Union[int, List[int]]\n\n\n@dataclass\nclass AudioSource:\n    \"\"\"\n    AudioSource represents audio data that can be retrieved from somewhere.\n    Supported sources of audio are currently:\n    - a file (formats supported by librosa, possibly multi-channel)\n    - a command/unix pipe (must be WAVE, possibly multi-channel)\n    \"\"\"\n    type: str\n    channel_ids: List[int]\n    source: str\n\n    def load_audio(\n            self,\n            offset_seconds: float = 0.0,\n            duration_seconds: Optional[float] = None\n    ) -> np.ndarray:\n        \"\"\"\n        Load the AudioSource (both files and commands) with librosa,\n        accounting for many audio formats and multi-channel inputs.\n        Returns numpy array with shapes: (n_samples) for single-channel,\n        (n_channels, n_samples) for multi-channel.\n        \"\"\"\n        assert self.type in ('file', 'command')\n\n        if self.type == 'command':\n            if offset_seconds != 0.0 or duration_seconds is not None:\n                # TODO(pzelasko): How should we support chunking for commands?\n                #                 We risk being very inefficient when reading many chunks from the same file\n                #                 without some caching scheme, because we'll be re-running commands.\n                raise ValueError(\"Reading audio chunks from command AudioSource type is currently not supported.\")\n            source = BytesIO(run(self.source, shell=True, stdout=PIPE).stdout)\n        else:\n            source = self.source\n\n        return librosa.load(\n            source,\n            sr=None,  # 'None' uses the native sampling rate\n            mono=False,  # Retain multi-channel if it's there\n            offset=offset_seconds,\n            duration=duration_seconds\n        )[0]  # discard returned sampling rate\n\n\n@dataclass\nclass Recording:\n    \"\"\"\n    Recording represents an AudioSource along with some metadata.\n    \"\"\"\n    id: str\n    sources: List[AudioSource]\n    sampling_rate: int\n    num_samples: int\n    duration_seconds: float\n\n    def __post_init__(self):\n        self.sources = [AudioSource(**s) if isinstance(s, dict) else s for s in self.sources]\n\n    @property\n    def num_channels(self):\n        return sum(len(source.channel_ids) for source in self.sources)\n\n    def load_audio(\n            self,\n            channels: Optional[Channels] = None,\n            offset_seconds: float = 0.0,\n            duration_seconds: Optional[float] = None\n    ) -> np.ndarray:\n        if channels is None:\n            channels = SetContainingAnything()\n        elif isinstance(channels, int):\n            channels = frozenset([channels])\n        else:\n            channels = frozenset(channels)\n\n        samples_per_source = []\n        for source in self.sources:\n            # Case: source not requested\n            if not channels.intersection(source.channel_ids):\n                continue\n            samples = source.load_audio(offset_seconds=offset_seconds, duration_seconds=duration_seconds)\n\n            # Case: two-channel audio file but only one channel requested\n            #       it might not be optimal to load all channels, but IDK if there's anything we can do about it\n            channels_to_remove = [\n                idx for idx, cid in enumerate(source.channel_ids)\n                if cid not in channels\n            ]\n            if channels_to_remove:\n                samples = np.delete(samples, channels_to_remove, axis=0)\n            samples_per_source.append(samples)\n\n        # shapes: single-channel (n_samples); multi-channel (n_channels, n_samples)\n        return np.vstack(samples_per_source) if len(samples_per_source) > 1 else samples_per_source[0]\n\n\n@dataclass\nclass AudioSet:\n    \"\"\"\n    AudioSet represents a dataset of recordings. It does not contain any annotation -\n    just the information needed to retrieve a recording (possibly multi-channel, from files\n    or from shell commands and pipes) and some metadata for each of them.\n\n    It also supports (de)serialization to/from YAML and takes care of mapping between\n    rich Python classes and YAML primitives during conversion.\n    \"\"\"\n    recordings: Dict[str, Recording]\n\n    @staticmethod\n    def from_yaml(path: Pathlike) -> 'AudioSet':\n        with open(path) as f:\n            recordings = (Recording(**raw_rec) for raw_rec in yaml.safe_load(f))\n        return AudioSet(recordings={r.id: r for r in recordings})\n\n    def to_yaml(self, path: Pathlike):\n        with open(path, 'w') as f:\n            yaml.safe_dump([asdict(r) for r in self.recordings.values()], stream=f)\n\n    def load_audio(\n            self,\n            recording_id: str,\n            channels: Optional[Channels] = None,\n            offset_seconds: float = 0.0,\n            duration_seconds: Optional[float] = None\n    ) -> np.ndarray:\n        return self.recordings[recording_id].load_audio(\n            channels=channels,\n            offset_seconds=offset_seconds,\n            duration_seconds=duration_seconds\n        )\n\n    def num_channels(self, recording_id: str) -> int:\n        return self.recordings[recording_id].num_channels\n\n    def sampling_rate(self, recording_id: str) -> int:\n        return self.recordings[recording_id].sampling_rate\n\n    def num_samples(self, recording_id: str) -> int:\n        return self.recordings[recording_id].num_samples\n\n    def duration_seconds(self, recording_id: str) -> float:\n        return self.recordings[recording_id].duration_seconds\n\n    def __iter__(self):\n        return iter(self.recordings.values())\n", "path": "lhotse/audio.py"}]}