M2-PT-DRP/source/behaviors/roles/MasterRole.py
2025-01-04 14:07:49 +01:00

63 lines
2.5 KiB
Python

import os
from datetime import datetime, timedelta
import pause
import pydub
from pydub.utils import make_chunks
from source.behaviors.roles import base
from source.managers import Manager
from source.packets import AudioPacket
from source.utils.crypto.type import CipherType
class MasterRole(base.BaseRole):
"""
Role used when the machine is declared as the master.
It will be the machine responsible for emitting data that the others peers should play together.
"""
TARGET_SIZE: int = 60 * 1024 # set an upper bound because of the IPv6 maximum packet size.
def __init__(self, manager: "Manager"):
super().__init__(manager)
# generate a random secret key for AES communication
self.manager.communication.secret_key = os.urandom(32)
# prepare the audio file that will be streamed
# TODO(Faraphel): use another audio source
self.audio = pydub.AudioSegment.from_file("../assets/Caravan Palace - Wonderland.mp3")
self.play_time = datetime.now()
# calculate the number of bytes per milliseconds in the audio
bytes_per_ms = self.audio.frame_rate * self.audio.sample_width * self.audio.channels / 1000
# calculate the required chunk duration to reach that size
self.chunk_duration = timedelta(milliseconds=self.TARGET_SIZE / bytes_per_ms)
# split the audio into chunks
self.chunks = make_chunks(self.audio, self.chunk_duration.total_seconds() * 1000)
def handle(self) -> None:
# TODO(Faraphel): communicate with chrony to add peers and enable server mode ?
# TODO(Faraphel): share the secret key generated with the other *allowed* peers ! How to select them ? A file ?
# TODO(Faraphel): check if another server is emitting sound in the network. Return to undefined if yes
# get the next chunk
chunk = self.chunks.pop(0)
# broadcast it in the network
audio_packet = AudioPacket(
# TODO(Faraphel): adjust time depending on the network average latency ?
datetime.now() + timedelta(seconds=5), # play it in some seconds to let all the machines get the sample
chunk.channels,
chunk.frame_rate,
chunk.sample_width,
chunk.raw_data,
)
self.manager.communication.broadcast(audio_packet, CipherType.AES_CBC)
# wait for the audio to play
# TODO(Faraphel): should adapt to the compute time above
pause.until(datetime.now() + self.chunk_duration)