Skip to content

Commit 3adf959

Browse files
committed
release 1.41, api doc corrections for the IceCastClient
1 parent a66c6a1 commit 3adf959

File tree

2 files changed

+41
-10
lines changed

2 files changed

+41
-10
lines changed

README.md

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ stream_file() instead.
347347

348348
*class* ``CaptureDevice``
349349

350-
``CaptureDevice (self, input_format: miniaudio.SampleFormat = <SampleFormat.SIGNED16: 2>, nchannels: int = 2, sample_rate: int = 44100, buffersize_msec: int = 200, device_id: Union[_cffi_backend._CDataBase, NoneType] = None, callback_periods: int = 0, backends: Union[List[miniaudio.Backend], NoneType] = None, thread_prio: miniaudio.ThreadPriority = <ThreadPriority.HIGHEST: 0>, app_name: str = '') ``
350+
``CaptureDevice (self, input_format: miniaudio.SampleFormat = <SampleFormat.SIGNED16: 2>, nchannels: int = 2, sample_rate: int = 44100, buffersize_msec: int = 200, device_id: Union[_cffi_backend.CData, NoneType] = None, callback_periods: int = 0, backends: Union[List[miniaudio.Backend], NoneType] = None, thread_prio: miniaudio.ThreadPriority = <ThreadPriority.HIGHEST: 0>, app_name: str = '') ``
351351
> An audio device provided by miniaudio, for audio capture (recording).
352352
353353
> *method* ``close (self) ``
@@ -388,7 +388,7 @@ callback generator as raw bytes. (it should already be started before)
388388

389389
*class* ``DuplexStream``
390390

391-
``DuplexStream (self, playback_format: miniaudio.SampleFormat = <SampleFormat.SIGNED16: 2>, playback_channels: int = 2, capture_format: miniaudio.SampleFormat = <SampleFormat.SIGNED16: 2>, capture_channels: int = 2, sample_rate: int = 44100, buffersize_msec: int = 200, playback_device_id: Union[_cffi_backend._CDataBase, NoneType] = None, capture_device_id: Union[_cffi_backend._CDataBase, NoneType] = None, callback_periods: int = 0, backends: Union[List[miniaudio.Backend], NoneType] = None, thread_prio: miniaudio.ThreadPriority = <ThreadPriority.HIGHEST: 0>, app_name: str = '') ``
391+
``DuplexStream (self, playback_format: miniaudio.SampleFormat = <SampleFormat.SIGNED16: 2>, playback_channels: int = 2, capture_format: miniaudio.SampleFormat = <SampleFormat.SIGNED16: 2>, capture_channels: int = 2, sample_rate: int = 44100, buffersize_msec: int = 200, playback_device_id: Union[_cffi_backend.CData, NoneType] = None, capture_device_id: Union[_cffi_backend.CData, NoneType] = None, callback_periods: int = 0, backends: Union[List[miniaudio.Backend], NoneType] = None, thread_prio: miniaudio.ThreadPriority = <ThreadPriority.HIGHEST: 0>, app_name: str = '') ``
392392
> Joins a capture device and a playback device.
393393
394394
> *method* ``close (self) ``
@@ -409,7 +409,19 @@ already be started before passing it in)
409409
``IceCastClient (self, url: str, update_stream_title: Callable[[ForwardRef('IceCastClient'), str], NoneType] = None) ``
410410
> A simple client for IceCast audio streams as miniaudio streamable source. If the stream has Icy
411411
Meta Data, the stream_title attribute will be updated with the actual title taken from the meta
412-
data. You can also provide a callback to be called when a new stream title is available.
412+
data. You can also provide a callback to be called when a new stream title is available. The
413+
downloading of the data from the internet is done in a background thread and it tries to keep a
414+
(small) buffer filled with available data to read.
415+
416+
> *method* ``close (self) ``
417+
> > Stop the stream, aborting the background downloading.
418+
419+
> *method* ``read (self, num_bytes: int) -> bytes``
420+
> > Read a chunk of data from the stream.
421+
422+
> *method* ``seek (self, offset: int, origin: miniaudio.SeekOrigin) -> bool``
423+
> > Override this if the stream supports seeking. Note: seek support is sometimes not needed if you
424+
give the file type to a decoder upfront. You can ignore this method then.
413425

414426

415427
*class* ``MiniaudioError``
@@ -420,7 +432,7 @@ data. You can also provide a callback to be called when a new stream title is av
420432

421433
*class* ``PlaybackDevice``
422434

423-
``PlaybackDevice (self, output_format: miniaudio.SampleFormat = <SampleFormat.SIGNED16: 2>, nchannels: int = 2, sample_rate: int = 44100, buffersize_msec: int = 200, device_id: Union[_cffi_backend._CDataBase, NoneType] = None, callback_periods: int = 0, backends: Union[List[miniaudio.Backend], NoneType] = None, thread_prio: miniaudio.ThreadPriority = <ThreadPriority.HIGHEST: 0>, app_name: str = '') ``
435+
``PlaybackDevice (self, output_format: miniaudio.SampleFormat = <SampleFormat.SIGNED16: 2>, nchannels: int = 2, sample_rate: int = 44100, buffersize_msec: int = 200, device_id: Union[_cffi_backend.CData, NoneType] = None, callback_periods: int = 0, backends: Union[List[miniaudio.Backend], NoneType] = None, thread_prio: miniaudio.ThreadPriority = <ThreadPriority.HIGHEST: 0>, app_name: str = '') ``
424436
> An audio device provided by miniaudio, for audio playback.
425437
426438
> *method* ``close (self) ``
@@ -449,6 +461,16 @@ The generator should already be started before passing it in.
449461
> Base class for streams of audio data bytes. Can be used as a contextmanager, to properly call
450462
close().
451463

464+
> *method* ``close (self) ``
465+
> > Override this to properly close the stream and free resources.
466+
467+
> *method* ``read (self, num_bytes: int) -> Union[bytes, memoryview]``
468+
> > override this to provide data bytes to the consumer of the stream
469+
470+
> *method* ``seek (self, offset: int, origin: miniaudio.SeekOrigin) -> bool``
471+
> > Override this if the stream supports seeking. Note: seek support is sometimes not needed if you
472+
give the file type to a decoder upfront. You can ignore this method then.
473+
452474

453475
*class* ``WavFileReadStream``
454476

miniaudio.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Software license: "MIT software license". See http://opensource.org/licenses/MIT
66
"""
77

8-
__version__ = "1.40"
8+
__version__ = "1.41"
99

1010

1111
import abc
@@ -1042,14 +1042,19 @@ class StreamableSource(abc.ABC):
10421042

10431043
@abc.abstractmethod
10441044
def read(self, num_bytes: int) -> Union[bytes, memoryview]:
1045+
"""override this to provide data bytes to the consumer of the stream"""
10451046
pass
10461047

10471048
def seek(self, offset: int, origin: SeekOrigin) -> bool:
1048-
# Note: seek support is usually not needed if you give the file type
1049-
# to the decoder upfront. You can ignore this method then.
1049+
"""
1050+
Override this if the stream supports seeking.
1051+
Note: seek support is sometimes not needed if you give the file type
1052+
to a decoder upfront. You can ignore this method then.
1053+
"""
10501054
return False
10511055

10521056
def close(self) -> None:
1057+
"""Override this to properly close the stream and free resources."""
10531058
pass
10541059

10551060
def __enter__(self) -> "StreamableSource":
@@ -1065,6 +1070,8 @@ class IceCastClient(StreamableSource):
10651070
If the stream has Icy Meta Data, the stream_title attribute will be updated
10661071
with the actual title taken from the meta data.
10671072
You can also provide a callback to be called when a new stream title is available.
1073+
The downloading of the data from the internet is done in a background thread
1074+
and it tries to keep a (small) buffer filled with available data to read.
10681075
"""
10691076

10701077
BLOCK_SIZE = 8*1024
@@ -1087,7 +1094,7 @@ def __init__(self, url: str, update_stream_title: Callable[['IceCastClient', str
10871094
self.station_name = result.headers["icy-name"]
10881095
stream_format = result.headers["Content-Type"]
10891096
self.audio_format = self.determine_audio_format(stream_format)
1090-
self._download_thread = threading.Thread(target=self.download_stream, daemon=True)
1097+
self._download_thread = threading.Thread(target=self._download_stream, daemon=True)
10911098
self._download_thread.start()
10921099

10931100
def determine_audio_format(self, stream_format: str) -> FileFormat:
@@ -1101,6 +1108,7 @@ def determine_audio_format(self, stream_format: str) -> FileFormat:
11011108
return FileFormat.UNKNOWN
11021109

11031110
def read(self, num_bytes: int) -> bytes:
1111+
"""Read a chunk of data from the stream."""
11041112
while len(self._buffer) < num_bytes:
11051113
time.sleep(0.1)
11061114
with self._buffer_lock:
@@ -1109,6 +1117,7 @@ def read(self, num_bytes: int) -> bytes:
11091117
return chunk
11101118

11111119
def close(self) -> None:
1120+
"""Stop the stream, aborting the background downloading."""
11121121
self._stop_stream = True
11131122
self._download_thread.join()
11141123

@@ -1118,7 +1127,7 @@ def _readall(self, fileobject, size: int) -> bytes:
11181127
b += fileobject.read(size)
11191128
return b
11201129

1121-
def download_stream(self) -> None:
1130+
def _download_stream(self) -> None:
11221131
req = urllib.request.Request(self.url, headers={"icy-metadata": "1"})
11231132
with urllib.request.urlopen(req) as result:
11241133
self.station_genre = result.headers["icy-genre"]
@@ -1144,7 +1153,7 @@ def download_stream(self) -> None:
11441153
meta_size = 16 * self._readall(result, 1)[0]
11451154
metadata = str(self._readall(result, meta_size).strip(b"\0"), "utf-8", errors="replace")
11461155
if metadata:
1147-
search = re.search("StreamTItle='(.*?)'", metadata, re.IGNORECASE)
1156+
search = re.search("StreamTitle='(.*?)'", metadata, re.IGNORECASE)
11481157
if search:
11491158
self.stream_title = search.group(1)
11501159
if self._update_title:

0 commit comments

Comments
 (0)