Improved audio playback on Linux
This commit is contained in:
parent
912f86ea8f
commit
050b3aa17a
|
@ -1534,8 +1534,11 @@ class SidebandApp(MDApp):
|
|||
elif audio_field[0] >= LXMF.AM_CODEC2_700C and audio_field[0] <= LXMF.AM_CODEC2_3200:
|
||||
temp_path = self.sideband.rec_cache+"/msg.ogg"
|
||||
from sideband.audioproc import samples_to_ogg, decode_codec2
|
||||
if samples_to_ogg(decode_codec2(audio_field[1], audio_field[0]), temp_path):
|
||||
RNS.log("Wrote wav file to: "+temp_path)
|
||||
samples = decode_codec2(audio_field[1], audio_field[0])
|
||||
if samples_to_ogg(samples, temp_path):
|
||||
RNS.log("Wrote OGG file to: "+temp_path, RNS.LOG_DEBUG)
|
||||
else:
|
||||
RNS.log("OGG write failed", RNS.LOG_DEBUG)
|
||||
|
||||
else:
|
||||
raise NotImplementedError(audio_field[0])
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import time
|
||||
import threading
|
||||
import RNS
|
||||
from sbapp.plyer.facades.audio import Audio
|
||||
from kivy.core.audio import SoundLoader
|
||||
from ffpyplayer.player import MediaPlayer
|
||||
|
||||
class LinuxAudio(Audio):
|
||||
|
||||
|
@ -18,8 +19,13 @@ class LinuxAudio(Audio):
|
|||
self.is_playing = False
|
||||
|
||||
def _check_playback(self):
|
||||
while self.sound != None and self.sound.state == "play":
|
||||
run = True
|
||||
while run and self.sound != None and not self.sound.get_pause():
|
||||
time.sleep(0.25)
|
||||
if self.duration:
|
||||
pts = self.sound.get_pts()
|
||||
if pts > self.duration:
|
||||
run = False
|
||||
|
||||
self.is_playing = False
|
||||
|
||||
|
@ -32,23 +38,30 @@ class LinuxAudio(Audio):
|
|||
pass
|
||||
|
||||
def _stop(self):
|
||||
if self.sound != None and self.sound.state == "play":
|
||||
self.sound.stop()
|
||||
if self.sound != None:
|
||||
self.sound.set_pause(True)
|
||||
self.sound.seek(0, relative=False)
|
||||
self.is_playing = False
|
||||
|
||||
def _play(self):
|
||||
if self.sound == None or self._loaded_path != self._file_path:
|
||||
self.sound = SoundLoader.load(self._file_path)
|
||||
self.sound = MediaPlayer(self._file_path)
|
||||
self.metadata = self.sound.get_metadata()
|
||||
self.duration = self.metadata["duration"]
|
||||
if self.duration == None:
|
||||
time.sleep(0.15)
|
||||
self.metadata = self.sound.get_metadata()
|
||||
self.duration = self.metadata["duration"]
|
||||
|
||||
RNS.log(str(self.metadata))
|
||||
|
||||
self._loaded_path = self._file_path
|
||||
self.is_playing = True
|
||||
self.sound.play()
|
||||
|
||||
self._check_thread = threading.Thread(target=self._check_playback, daemon=True)
|
||||
self._check_thread.start()
|
||||
|
||||
def reload(self):
|
||||
self._loaded_path = None
|
||||
self.sound = None
|
||||
|
||||
def playing(self):
|
||||
return self.is_playing
|
||||
|
|
|
@ -43,28 +43,48 @@ def samples_from_ogg(file_path=None):
|
|||
return audio.get_array_of_samples()
|
||||
|
||||
def samples_to_ogg(samples=None, file_path=None):
|
||||
if file_path != None and samples != None:
|
||||
pcm_data = io.BytesIO(samples)
|
||||
channels = 1; samples_per_second = 8000; bytes_per_sample = 2
|
||||
try:
|
||||
if file_path != None and samples != None:
|
||||
pcm_data = io.BytesIO(samples)
|
||||
|
||||
opus_buffered_encoder = pyogg.OpusBufferedEncoder()
|
||||
opus_buffered_encoder.set_application("audio")
|
||||
opus_buffered_encoder.set_sampling_frequency(samples_per_second)
|
||||
opus_buffered_encoder.set_channels(channels)
|
||||
opus_buffered_encoder.set_frame_size(20) # milliseconds
|
||||
ogg_opus_writer = pyogg.OggOpusWriter(file_path, opus_buffered_encoder)
|
||||
RNS.log(f"Samples: {len(samples)}")
|
||||
RNS.log(f"Type : {type(samples)}")
|
||||
|
||||
frame_duration = 0.1
|
||||
frame_size = int(frame_duration * samples_per_second)
|
||||
bytes_per_frame = frame_size*bytes_per_sample
|
||||
|
||||
while True:
|
||||
pcm = pcm_data.read(bytes_per_frame)
|
||||
if len(pcm) == 0:
|
||||
break
|
||||
ogg_opus_writer.write(memoryview(bytearray(pcm)))
|
||||
channels = 1; samples_per_second = 8000; bytes_per_sample = 2
|
||||
|
||||
ogg_opus_writer.close()
|
||||
opus_buffered_encoder = pyogg.OpusBufferedEncoder()
|
||||
opus_buffered_encoder.set_application("audio")
|
||||
opus_buffered_encoder.set_sampling_frequency(samples_per_second)
|
||||
opus_buffered_encoder.set_channels(channels)
|
||||
opus_buffered_encoder.set_frame_size(20) # milliseconds
|
||||
ogg_opus_writer = pyogg.OggOpusWriter(file_path, opus_buffered_encoder)
|
||||
|
||||
frame_duration = 0.020
|
||||
frame_size = int(frame_duration * samples_per_second)
|
||||
bytes_per_frame = frame_size*bytes_per_sample
|
||||
|
||||
read_bytes = 0
|
||||
written_bytes = 0
|
||||
while True:
|
||||
pcm = pcm_data.read(bytes_per_frame)
|
||||
if len(pcm) == 0:
|
||||
break
|
||||
else:
|
||||
read_bytes += len(pcm)
|
||||
|
||||
ogg_opus_writer.write(memoryview(bytearray(pcm)))
|
||||
written_bytes += len(pcm)
|
||||
|
||||
ogg_opus_writer.close()
|
||||
|
||||
RNS.log(f"Read {read_bytes} bytes")
|
||||
RNS.log(f"Wrote {written_bytes} bytes")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
RNS.trace_exception(e)
|
||||
return False
|
||||
|
||||
def samples_to_wav(samples=None, file_path=None):
|
||||
if samples != None and file_path != None:
|
||||
|
@ -87,7 +107,9 @@ def encode_codec2(samples, mode):
|
|||
SPF = c2.samples_per_frame()
|
||||
PACKET_SIZE = SPF * 2 # 16-bit samples
|
||||
STRUCT_FORMAT = '{}h'.format(SPF)
|
||||
F_FRAMES = len(samples)/SPF
|
||||
N_FRAMES = math.floor(len(samples)/SPF)
|
||||
# TODO: Add padding to align to whole frames
|
||||
frames = np.array(samples[0:N_FRAMES*SPF], dtype=np.int16)
|
||||
|
||||
encoded = b""
|
||||
|
|
Loading…
Reference in New Issue