mirror of
https://github.com/liberatedsystems/openCom-Companion.git
synced 2024-11-25 23:00:36 +01:00
Audio recording on Linux
This commit is contained in:
parent
1a224fd65a
commit
584267dcc5
@ -1534,8 +1534,12 @@ class SidebandApp(MDApp):
|
|||||||
elif audio_field[0] >= LXMF.AM_CODEC2_700C and audio_field[0] <= LXMF.AM_CODEC2_3200:
|
elif audio_field[0] >= LXMF.AM_CODEC2_700C and audio_field[0] <= LXMF.AM_CODEC2_3200:
|
||||||
temp_path = self.sideband.rec_cache+"/msg.ogg"
|
temp_path = self.sideband.rec_cache+"/msg.ogg"
|
||||||
from sideband.audioproc import samples_to_ogg, decode_codec2
|
from sideband.audioproc import samples_to_ogg, decode_codec2
|
||||||
samples = decode_codec2(audio_field[1], audio_field[0])
|
|
||||||
if samples_to_ogg(samples, temp_path):
|
target_rate = 8000
|
||||||
|
if RNS.vendor.platformutils.is_linux():
|
||||||
|
target_rate = 48000
|
||||||
|
|
||||||
|
if samples_to_ogg(decode_codec2(audio_field[1], audio_field[0]), temp_path, input_rate=8000, output_rate=target_rate):
|
||||||
RNS.log("Wrote OGG file to: "+temp_path, RNS.LOG_DEBUG)
|
RNS.log("Wrote OGG file to: "+temp_path, RNS.LOG_DEBUG)
|
||||||
else:
|
else:
|
||||||
RNS.log("OGG write failed", RNS.LOG_DEBUG)
|
RNS.log("OGG write failed", RNS.LOG_DEBUG)
|
||||||
@ -1641,7 +1645,7 @@ class SidebandApp(MDApp):
|
|||||||
audio = audio.set_sample_width(2)
|
audio = audio.set_sample_width(2)
|
||||||
samples = audio.get_array_of_samples()
|
samples = audio.get_array_of_samples()
|
||||||
|
|
||||||
from sideband.audioproc import samples_from_ogg, encode_codec2, decode_codec2
|
from sideband.audioproc import encode_codec2
|
||||||
encoded = encode_codec2(samples, self.audio_msg_mode)
|
encoded = encode_codec2(samples, self.audio_msg_mode)
|
||||||
|
|
||||||
ap_duration = time.time() - ap_start
|
ap_duration = time.time() - ap_start
|
||||||
|
@ -56,7 +56,7 @@ class AndroidAudio(Audio):
|
|||||||
else:
|
else:
|
||||||
self._recorder.setAudioSource(AudioSource.DEFAULT)
|
self._recorder.setAudioSource(AudioSource.DEFAULT)
|
||||||
self._recorder.setAudioSamplingRate(48000)
|
self._recorder.setAudioSamplingRate(48000)
|
||||||
self._recorder.setAudioEncodingBitRate(16000)
|
self._recorder.setAudioEncodingBitRate(32000)
|
||||||
self._recorder.setAudioChannels(1)
|
self._recorder.setAudioChannels(1)
|
||||||
self._recorder.setOutputFormat(OutputFormat.OGG)
|
self._recorder.setOutputFormat(OutputFormat.OGG)
|
||||||
self._recorder.setAudioEncoder(AudioEncoder.OPUS)
|
self._recorder.setAudioEncoder(AudioEncoder.OPUS)
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
import RNS
|
import RNS
|
||||||
|
import io
|
||||||
from sbapp.plyer.facades.audio import Audio
|
from sbapp.plyer.facades.audio import Audio
|
||||||
from ffpyplayer.player import MediaPlayer
|
from ffpyplayer.player import MediaPlayer
|
||||||
|
from sbapp.pyogg import OpusFile, OpusBufferedEncoder, OggOpusWriter
|
||||||
|
import pyaudio
|
||||||
|
|
||||||
class LinuxAudio(Audio):
|
class LinuxAudio(Audio):
|
||||||
|
|
||||||
@ -16,7 +19,10 @@ class LinuxAudio(Audio):
|
|||||||
self._finished_callback = None
|
self._finished_callback = None
|
||||||
self._loaded_path = None
|
self._loaded_path = None
|
||||||
self.sound = None
|
self.sound = None
|
||||||
|
self.pa = None
|
||||||
self.is_playing = False
|
self.is_playing = False
|
||||||
|
self.recorder = None
|
||||||
|
self.should_record = False
|
||||||
|
|
||||||
def _check_playback(self):
|
def _check_playback(self):
|
||||||
run = True
|
run = True
|
||||||
@ -33,12 +39,76 @@ class LinuxAudio(Audio):
|
|||||||
self._check_thread = None
|
self._check_thread = None
|
||||||
self._finished_callback(self)
|
self._finished_callback(self)
|
||||||
|
|
||||||
|
def _record_job(self):
|
||||||
|
samples_per_second = self.default_rate;
|
||||||
|
bytes_per_sample = 2; frame_duration_ms = 20
|
||||||
|
opus_buffered_encoder = OpusBufferedEncoder()
|
||||||
|
opus_buffered_encoder.set_application("voip")
|
||||||
|
opus_buffered_encoder.set_sampling_frequency(samples_per_second)
|
||||||
|
opus_buffered_encoder.set_channels(1)
|
||||||
|
opus_buffered_encoder.set_frame_size(frame_duration_ms)
|
||||||
|
ogg_opus_writer = OggOpusWriter(self._file_path, opus_buffered_encoder)
|
||||||
|
|
||||||
|
frame_duration = frame_duration_ms/1000
|
||||||
|
frame_size = int(frame_duration * samples_per_second)
|
||||||
|
bytes_per_frame = frame_size*bytes_per_sample
|
||||||
|
|
||||||
|
read_bytes = 0
|
||||||
|
pcm_buf = b""
|
||||||
|
should_continue = True
|
||||||
|
while self.should_record and self.recorder:
|
||||||
|
samples_available = self.recorder.get_read_available()
|
||||||
|
bytes_available = samples_available*bytes_per_sample
|
||||||
|
if bytes_available > 0:
|
||||||
|
read_req = bytes_per_frame - len(pcm_buf)
|
||||||
|
read_n = min(bytes_available, read_req)
|
||||||
|
read_s = read_n//bytes_per_sample
|
||||||
|
rb = self.recorder.read(read_s); read_bytes += len(rb)
|
||||||
|
pcm_buf += rb
|
||||||
|
|
||||||
|
if len(pcm_buf) == bytes_per_frame:
|
||||||
|
ogg_opus_writer.write(memoryview(bytearray(pcm_buf)))
|
||||||
|
# RNS.log("Wrote frame of "+str(len(pcm_buf))+", expected size "+str(bytes_per_frame))
|
||||||
|
pcm_buf = b""
|
||||||
|
|
||||||
|
# Finish up anything left in buffer
|
||||||
|
time.sleep(frame_duration)
|
||||||
|
samples_available = self.recorder.get_read_available()
|
||||||
|
bytes_available = samples_available*bytes_per_sample
|
||||||
|
if bytes_available > 0:
|
||||||
|
read_req = bytes_per_frame - len(pcm_buf)
|
||||||
|
read_n = min(bytes_available, read_req)
|
||||||
|
read_s = read_n//bytes_per_sample
|
||||||
|
rb = self.recorder.read(read_s); read_bytes += len(rb)
|
||||||
|
pcm_buf += rb
|
||||||
|
|
||||||
|
if len(pcm_buf) == bytes_per_frame:
|
||||||
|
ogg_opus_writer.write(memoryview(bytearray(pcm_buf)))
|
||||||
|
# RNS.log("Wrote frame of "+str(len(pcm_buf))+", expected size "+str(bytes_per_frame))
|
||||||
|
pcm_buf = b""
|
||||||
|
|
||||||
|
ogg_opus_writer.close()
|
||||||
|
if self.recorder:
|
||||||
|
self.recorder.close()
|
||||||
|
|
||||||
def _start(self):
|
def _start(self):
|
||||||
# TODO: Implement recording
|
self.should_record = True
|
||||||
pass
|
if self.pa == None:
|
||||||
|
self.pa = pyaudio.PyAudio()
|
||||||
|
self.default_input_device = self.pa.get_default_input_device_info()
|
||||||
|
self.default_rate = 48000
|
||||||
|
# self.default_rate = int(self.default_input_device["defaultSampleRate"])
|
||||||
|
if self.recorder:
|
||||||
|
self.recorder.close()
|
||||||
|
self.recorder = None
|
||||||
|
self.recorder = self.pa.open(self.default_rate, 1, pyaudio.paInt16, input=True)
|
||||||
|
threading.Thread(target=self._record_job, daemon=True).start()
|
||||||
|
|
||||||
def _stop(self):
|
def _stop(self):
|
||||||
if self.sound != None:
|
if self.should_record == True:
|
||||||
|
self.should_record = False
|
||||||
|
|
||||||
|
elif self.sound != None:
|
||||||
self.sound.set_pause(True)
|
self.sound.set_pause(True)
|
||||||
self.sound.seek(0, relative=False)
|
self.sound.seek(0, relative=False)
|
||||||
self.is_playing = False
|
self.is_playing = False
|
||||||
|
@ -8,7 +8,7 @@ import RNS
|
|||||||
import LXMF
|
import LXMF
|
||||||
|
|
||||||
if RNS.vendor.platformutils.is_android():
|
if RNS.vendor.platformutils.is_android():
|
||||||
import pyogg
|
from pyogg import OpusFile, OpusBufferedEncoder, OggOpusWriter
|
||||||
from pydub import AudioSegment
|
from pydub import AudioSegment
|
||||||
else:
|
else:
|
||||||
if RNS.vendor.platformutils.is_linux():
|
if RNS.vendor.platformutils.is_linux():
|
||||||
@ -30,7 +30,7 @@ codec2_modes = {
|
|||||||
LXMF.AM_CODEC2_3200: 3200,
|
LXMF.AM_CODEC2_3200: 3200,
|
||||||
}
|
}
|
||||||
|
|
||||||
def samples_from_ogg(file_path=None):
|
def samples_from_ogg(file_path=None, output_rate=8000):
|
||||||
if file_path != None and os.path.isfile(file_path):
|
if file_path != None and os.path.isfile(file_path):
|
||||||
opus_file = OpusFile(file_path)
|
opus_file = OpusFile(file_path)
|
||||||
audio = AudioSegment(
|
audio = AudioSegment(
|
||||||
@ -41,49 +41,48 @@ def samples_from_ogg(file_path=None):
|
|||||||
|
|
||||||
audio = audio.split_to_mono()[0]
|
audio = audio.split_to_mono()[0]
|
||||||
audio = audio.apply_gain(-audio.max_dBFS)
|
audio = audio.apply_gain(-audio.max_dBFS)
|
||||||
audio = audio.set_frame_rate(8000)
|
audio = audio.set_frame_rate(output_rate)
|
||||||
audio = audio.set_sample_width(2)
|
audio = audio.set_sample_width(2)
|
||||||
|
|
||||||
return audio.get_array_of_samples()
|
return audio.get_array_of_samples()
|
||||||
|
|
||||||
def samples_to_ogg(samples=None, file_path=None):
|
def resample(samples, width, channels, input_rate, output_rate, normalize):
|
||||||
|
audio = AudioSegment(
|
||||||
|
samples,
|
||||||
|
frame_rate=input_rate,
|
||||||
|
sample_width=width,
|
||||||
|
channels=channels)
|
||||||
|
|
||||||
|
if normalize:
|
||||||
|
audio = audio.apply_gain(-audio.max_dBFS)
|
||||||
|
|
||||||
|
resampled_audio = audio.set_frame_rate(output_rate)
|
||||||
|
return resampled_audio.get_array_of_samples().tobytes()
|
||||||
|
|
||||||
|
def samples_to_ogg(samples=None, file_path=None, normalize=False, input_channels=1, input_sample_width=2, input_rate=8000, output_rate=12000, profile="audio"):
|
||||||
try:
|
try:
|
||||||
if file_path != None and samples != None:
|
if file_path != None and samples != None:
|
||||||
|
if input_rate != output_rate or normalize:
|
||||||
|
samples = resample(samples, input_sample_width, input_channels, input_rate, output_rate, normalize)
|
||||||
|
|
||||||
pcm_data = io.BytesIO(samples)
|
pcm_data = io.BytesIO(samples)
|
||||||
|
channels = input_channels; samples_per_second = output_rate; bytes_per_sample = 2
|
||||||
RNS.log(f"Samples: {len(samples)}")
|
frame_duration_ms = 60
|
||||||
RNS.log(f"Type : {type(samples)}")
|
|
||||||
|
|
||||||
channels = 1; samples_per_second = 8000; bytes_per_sample = 2
|
|
||||||
|
|
||||||
opus_buffered_encoder = OpusBufferedEncoder()
|
opus_buffered_encoder = OpusBufferedEncoder()
|
||||||
opus_buffered_encoder.set_application("audio")
|
opus_buffered_encoder.set_application(profile)
|
||||||
opus_buffered_encoder.set_sampling_frequency(samples_per_second)
|
opus_buffered_encoder.set_sampling_frequency(samples_per_second)
|
||||||
opus_buffered_encoder.set_channels(channels)
|
opus_buffered_encoder.set_channels(channels)
|
||||||
opus_buffered_encoder.set_frame_size(20) # milliseconds
|
opus_buffered_encoder.set_frame_size(frame_duration_ms)
|
||||||
ogg_opus_writer = OggOpusWriter(file_path, opus_buffered_encoder)
|
ogg_opus_writer = OggOpusWriter(file_path, opus_buffered_encoder)
|
||||||
|
|
||||||
frame_duration = 0.020
|
frame_duration = frame_duration_ms/1000.0
|
||||||
frame_size = int(frame_duration * samples_per_second)
|
frame_size = int(frame_duration * samples_per_second)
|
||||||
bytes_per_frame = frame_size*bytes_per_sample
|
bytes_per_frame = frame_size*bytes_per_sample
|
||||||
|
|
||||||
read_bytes = 0
|
ogg_opus_writer.write(memoryview(bytearray(samples)))
|
||||||
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()
|
ogg_opus_writer.close()
|
||||||
|
|
||||||
RNS.log(f"Read {read_bytes} bytes")
|
|
||||||
RNS.log(f"Wrote {written_bytes} bytes")
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
Loading…
Reference in New Issue
Block a user