first commit
This commit is contained in:
142
audio2face_streaming_utils.py
Normal file
142
audio2face_streaming_utils.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""
|
||||
This demo script shows how to send audio data to Audio2Face Streaming Audio Player via gRPC requests.
|
||||
There are two options:
|
||||
* Send the whole track at once using PushAudioRequest()
|
||||
* Send the audio chunks seuqntially in a stream using PushAudioStreamRequest()
|
||||
For the second option this script emulates the stream of chunks, generated by splitting an input WAV audio file.
|
||||
But in a real application such stream of chunks may be aquired from some other streaming source:
|
||||
* streaming audio via internet, streaming Text-To-Speech, etc
|
||||
gRPC protocol details could be find in audio2face.proto
|
||||
"""
|
||||
|
||||
import sys
|
||||
import grpc
|
||||
import time
|
||||
import numpy as np
|
||||
import soundfile
|
||||
|
||||
import audio2face_pb2
|
||||
import audio2face_pb2_grpc
|
||||
|
||||
|
||||
def push_audio_track(url, audio_data, samplerate, instance_name):
|
||||
"""
|
||||
This function pushes the whole audio track at once via PushAudioRequest()
|
||||
PushAudioRequest parameters:
|
||||
* audio_data: bytes, containing audio data for the whole track, where each sample is encoded as 4 bytes (float32)
|
||||
* samplerate: sampling rate for the audio data
|
||||
* instance_name: prim path of the Audio2Face Streaming Audio Player on the stage, were to push the audio data
|
||||
* block_until_playback_is_finished: if True, the gRPC request will be blocked until the playback of the pushed track is finished
|
||||
The request is passed to PushAudio()
|
||||
"""
|
||||
|
||||
block_until_playback_is_finished = True # ADJUST
|
||||
with grpc.insecure_channel(url) as channel:
|
||||
stub = audio2face_pb2_grpc.Audio2FaceStub(channel)
|
||||
request = audio2face_pb2.PushAudioRequest()
|
||||
request.audio_data = audio_data.astype(np.float32).tobytes()
|
||||
request.samplerate = samplerate
|
||||
request.instance_name = instance_name
|
||||
request.block_until_playback_is_finished = block_until_playback_is_finished
|
||||
print("Sending audio data...")
|
||||
response = stub.PushAudio(request)
|
||||
if response.success:
|
||||
print("SUCCESS")
|
||||
else:
|
||||
print(f"ERROR: {response.message}")
|
||||
print("Closed channel")
|
||||
|
||||
|
||||
def push_audio_track_stream(url, audio_data, samplerate, instance_name):
|
||||
"""
|
||||
This function pushes audio chunks sequentially via PushAudioStreamRequest()
|
||||
The function emulates the stream of chunks, generated by splitting input audio track.
|
||||
But in a real application such stream of chunks may be aquired from some other streaming source.
|
||||
The first message must contain start_marker field, containing only meta information (without audio data):
|
||||
* samplerate: sampling rate for the audio data
|
||||
* instance_name: prim path of the Audio2Face Streaming Audio Player on the stage, were to push the audio data
|
||||
* block_until_playback_is_finished: if True, the gRPC request will be blocked until the playback of the pushed track is finished (after the last message)
|
||||
Second and other messages must contain audio_data field:
|
||||
* audio_data: bytes, containing audio data for an audio chunk, where each sample is encoded as 4 bytes (float32)
|
||||
All messages are packed into a Python generator and passed to PushAudioStream()
|
||||
"""
|
||||
|
||||
chunk_size = samplerate // 10 # ADJUST
|
||||
sleep_between_chunks = 0.04 # ADJUST
|
||||
block_until_playback_is_finished = True # ADJUST
|
||||
|
||||
with grpc.insecure_channel(url) as channel:
|
||||
print("Channel creadted")
|
||||
stub = audio2face_pb2_grpc.Audio2FaceStub(channel)
|
||||
|
||||
def make_generator():
|
||||
start_marker = audio2face_pb2.PushAudioRequestStart(
|
||||
samplerate=samplerate,
|
||||
instance_name=instance_name,
|
||||
block_until_playback_is_finished=block_until_playback_is_finished,
|
||||
)
|
||||
# At first, we send a message with start_marker
|
||||
yield audio2face_pb2.PushAudioStreamRequest(start_marker=start_marker)
|
||||
# Then we send messages with audio_data
|
||||
for i in range(len(audio_data) // chunk_size + 1):
|
||||
time.sleep(sleep_between_chunks)
|
||||
chunk = audio_data[i * chunk_size : i * chunk_size + chunk_size]
|
||||
yield audio2face_pb2.PushAudioStreamRequest(audio_data=chunk.astype(np.float32).tobytes())
|
||||
|
||||
request_generator = make_generator()
|
||||
print("Sending audio data...")
|
||||
response = stub.PushAudioStream(request_generator)
|
||||
if response.success:
|
||||
print("SUCCESS")
|
||||
else:
|
||||
print(f"ERROR: {response.message}")
|
||||
print("Channel closed")
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
This demo script shows how to send audio data to Audio2Face Streaming Audio Player via gRPC requests.
|
||||
There two options:
|
||||
* Send the whole track at once using PushAudioRequest()
|
||||
* Send the audio chunks seuqntially in a stream using PushAudioStreamRequest()
|
||||
For the second option this script emulates the stream of chunks, generated by splitting an input WAV audio file.
|
||||
But in a real application such stream of chunks may be aquired from some other streaming source:
|
||||
* streaming audio via internet, streaming Text-To-Speech, etc
|
||||
gRPC protocol details could be find in audio2face.proto
|
||||
"""
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("Format: python test_client.py PATH_TO_WAV INSTANCE_NAME")
|
||||
return
|
||||
|
||||
# Sleep time emulates long latency of the request
|
||||
sleep_time = 2.0 # ADJUST
|
||||
|
||||
# URL of the Audio2Face Streaming Audio Player server (where A2F App is running)
|
||||
url = "localhost:50051" # ADJUST
|
||||
|
||||
# Local input WAV file path
|
||||
audio_fpath = sys.argv[1]
|
||||
|
||||
# Prim path of the Audio2Face Streaming Audio Player on the stage (were to push the audio data)
|
||||
instance_name = sys.argv[2]
|
||||
|
||||
data, samplerate = soundfile.read(audio_fpath, dtype="float32")
|
||||
|
||||
# Only Mono audio is supported
|
||||
if len(data.shape) > 1:
|
||||
data = np.average(data, axis=1)
|
||||
|
||||
print(f"Sleeping for {sleep_time} seconds")
|
||||
time.sleep(sleep_time)
|
||||
|
||||
if 0: # ADJUST
|
||||
# Push the whole audio track at once
|
||||
push_audio_track(url, data, samplerate, instance_name)
|
||||
else:
|
||||
# Emulate audio stream and push audio chunks sequentially
|
||||
push_audio_track_stream(url, data, samplerate, instance_name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user