Example: Websocket example, synchronous client with iterator

This example shows:

  1. How to open a websocket connection using a context manager.

  2. Generate a take.

  3. How to iterate over the resulting status and audio messages using iter_request().

  4. How to make a request with and without chunks enabled. (Add argument --chunks.)

Example output
$ python3 -m examples.websocket_example_iter
Found Daisys Speak API version=1 minor=0
[0.002] Take status was changed to: WAITING.
[0.022] Take status was changed to: STARTED.
[0.748] New part being received.
[0.748] Received audio chunk of size 233472.
[1.204] Take status was changed to: PROGRESS_50.
[1.208] New part being received.
[1.208] Received audio chunk of size 116736.
[2.597] Take status was changed to: READY.
Deleting take t01jqrj9j7hyx49enqya9qeas3t: True
Example output (chunks enabled)
$ python3 -m examples.websocket_example_iter --chunks
Found Daisys Speak API version=1 minor=0
[0.002] Take status was changed to: WAITING.
[0.026] Take status was changed to: STARTED.
[0.314] New part being received.
[0.314] Received audio chunk of size 4096.
[0.328] Received audio chunk of size 4096.
[0.341] Received audio chunk of size 4096.
[0.351] Received audio chunk of size 4096.
[0.361] Received audio chunk of size 4096.
[0.371] Received audio chunk of size 4096.
[0.381] Received audio chunk of size 4096.
[0.391] Received audio chunk of size 4096.
[0.401] Received audio chunk of size 4096.
[0.411] Received audio chunk of size 4096.
[0.421] Received audio chunk of size 4096.
[0.431] Received audio chunk of size 4096.
[0.442] Received audio chunk of size 4096.
[0.452] Received audio chunk of size 4096.
[0.462] Received audio chunk of size 4096.
[0.472] Received audio chunk of size 4096.
[0.482] Received audio chunk of size 4096.
[0.492] Received audio chunk of size 4096.
[0.502] Received audio chunk of size 4096.
[0.512] Received audio chunk of size 4096.
[0.521] Received audio chunk of size 4096.
[0.532] Received audio chunk of size 4096.
[0.542] Received audio chunk of size 4096.
[0.551] Received audio chunk of size 4096.
[0.561] Received audio chunk of size 4096.
[0.572] Received audio chunk of size 4096.
[0.582] Received audio chunk of size 4096.
[0.592] Received audio chunk of size 4096.
[0.603] Received audio chunk of size 4096.
[0.613] Received audio chunk of size 1536.
[0.963] Take status was changed to: PROGRESS_50.
[0.966] New part being received.
[0.966] Received audio chunk of size 4096.
[0.976] Received audio chunk of size 4096.
[0.985] Received audio chunk of size 4096.
[0.994] Received audio chunk of size 4096.
[1.004] Received audio chunk of size 4096.
[1.014] Received audio chunk of size 4096.
[1.024] Received audio chunk of size 4096.
[1.034] Received audio chunk of size 4096.
[1.044] Received audio chunk of size 4096.
[1.055] Received audio chunk of size 4096.
[1.065] Received audio chunk of size 4096.
[1.075] Received audio chunk of size 4096.
[1.085] Received audio chunk of size 4096.
[1.095] Received audio chunk of size 3072.
[2.600] Take status was changed to: READY.
Deleting take t01jqrjxc257e1mr0r0z65ak4qb: True
examples/websocket_example_async_iter.py
 1import sys, os, asyncio, time
 2from typing import Optional
 3from daisys import DaisysAPI
 4from daisys.v1.speak import (DaisysWebsocketGenerateError, HTTPStatusError, Status, TakeResponse,
 5                             StreamOptions, StreamMode)
 6
 7# Override DAISYS_EMAIL and DAISYS_PASSWORD with your details!
 8EMAIL = os.environ.get('DAISYS_EMAIL', 'user@example.com')
 9PASSWORD = os.environ.get('DAISYS_PASSWORD', 'pw')
10
11# Please see tokens_example.py for how to use an access token instead of a password.
12
13async def main(chunks):
14    async with DaisysAPI('speak', email=EMAIL, password=PASSWORD) as speak:
15        print('Found Daisys Speak API', await speak.version())
16
17        # A buffer to receive parts; we initialize with a single empty bytes()
18        # because we will use it to accumulate chunks of the current wav file
19        # there.  In total we will end with a list of wav files, one for each
20        # part.  Parts are bits of speech, usually full sentences, that end with
21        # silence.
22        audio_wavs = [bytes()]
23
24        # Assume at least one voice is available
25        voice = (await speak.get_voices())[0]
26
27        async with speak.websocket(voice_id=voice.voice_id) as ws:
28            # Time the latency from when we submit the request until each part
29            # is received.
30            t0 = time.time()
31
32            # Submit a request to generate a take over the websocket connection.
33            generate_request_id = await ws.generate_take(
34                voice_id=voice.voice_id,
35                text='Hello from Daisys websockets! How may I help you?',
36
37                # Optional
38                stream_options=StreamOptions(mode=StreamMode.CHUNKS) if chunks else None,
39            )
40
41            # The use of an interator simplifies streaming, here we show how to
42            # get both status and audio chunks from the same iterator.
43            async for take_id, take, header, audio in ws.iter_request(generate_request_id):
44                now = time.time() - t0
45                if take is not None:
46                    print(f'[{now:.03f}] Take status was changed to: {take.status.name}.')
47                if header is not None:
48                    print(f'[{now:.03f}] New part being received.')
49                if audio is not None:
50                    print(f'[{now:.03f}] Received audio chunk of size {len(audio)}.')
51
52        # Delete the take
53        if take_id:
54            print(f'Deleting take {take_id}:', await speak.delete_take(take_id))
55
56if __name__=='__main__':
57    try:
58        asyncio.run(main('--chunks' in sys.argv[1:]))
59    except HTTPStatusError as e:
60        try:
61            print(f'HTTP error status {e.response.status_code}: {e.response.json()["detail"]}, {e.request.url}')
62        except:
63            print(f'HTTP error status {e.response.status_code}: {e.response.text}, {e.request.url}')