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:
|
||||
temp_path = self.sideband.rec_cache+"/msg.ogg"
|
||||
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)
|
||||
else:
|
||||
RNS.log("OGG write failed", RNS.LOG_DEBUG)
|
||||
|
@ -1641,7 +1645,7 @@ class SidebandApp(MDApp):
|
|||
audio = audio.set_sample_width(2)
|
||||
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)
|
||||
|
||||
ap_duration = time.time() - ap_start
|
||||
|
|
|
@ -56,7 +56,7 @@ class AndroidAudio(Audio):
|
|||
else:
|
||||
self._recorder.setAudioSource(AudioSource.DEFAULT)
|
||||
self._recorder.setAudioSamplingRate(48000)
|
||||
self._recorder.setAudioEncodingBitRate(16000)
|
||||
self._recorder.setAudioEncodingBitRate(32000)
|
||||
self._recorder.setAudioChannels(1)
|
||||
self._recorder.setOutputFormat(OutputFormat.OGG)
|
||||
self._recorder.setAudioEncoder(AudioEncoder.OPUS)
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import time
|
||||
import threading
|
||||
import RNS
|
||||
import io
|
||||
from sbapp.plyer.facades.audio import Audio
|
||||
from ffpyplayer.player import MediaPlayer
|
||||
from sbapp.pyogg import OpusFile, OpusBufferedEncoder, OggOpusWriter
|
||||
import pyaudio
|
||||
|
||||
class LinuxAudio(Audio):
|
||||
|
||||
|
@ -16,7 +19,10 @@ class LinuxAudio(Audio):
|
|||
self._finished_callback = None
|
||||
self._loaded_path = None
|
||||
self.sound = None
|
||||
self.pa = None
|
||||
self.is_playing = False
|
||||
self.recorder = None
|
||||
self.should_record = False
|
||||
|
||||
def _check_playback(self):
|
||||
run = True
|
||||
|
@ -33,12 +39,76 @@ class LinuxAudio(Audio):
|
|||
self._check_thread = None
|
||||
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):
|
||||
# TODO: Implement recording
|
||||
pass
|
||||
self.should_record = True
|
||||
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):
|
||||
if self.sound != None:
|
||||
if self.should_record == True:
|
||||
self.should_record = False
|
||||
|
||||
elif self.sound != None:
|
||||
self.sound.set_pause(True)
|
||||
self.sound.seek(0, relative=False)
|
||||
self.is_playing = False
|
||||
|
|
|
@ -8,7 +8,7 @@ import RNS
|
|||
import LXMF
|
||||
|
||||
if RNS.vendor.platformutils.is_android():
|
||||
import pyogg
|
||||
from pyogg import OpusFile, OpusBufferedEncoder, OggOpusWriter
|
||||
from pydub import AudioSegment
|
||||
else:
|
||||
if RNS.vendor.platformutils.is_linux():
|
||||
|
@ -30,7 +30,7 @@ codec2_modes = {
|
|||
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):
|
||||
opus_file = OpusFile(file_path)
|
||||
audio = AudioSegment(
|
||||
|
@ -41,49 +41,48 @@ def samples_from_ogg(file_path=None):
|
|||
|
||||
audio = audio.split_to_mono()[0]
|
||||
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)
|
||||
|
||||
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:
|
||||
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)
|
||||
|
||||
RNS.log(f"Samples: {len(samples)}")
|
||||
RNS.log(f"Type : {type(samples)}")
|
||||
|
||||
channels = 1; samples_per_second = 8000; bytes_per_sample = 2
|
||||
channels = input_channels; samples_per_second = output_rate; bytes_per_sample = 2
|
||||
frame_duration_ms = 60
|
||||
|
||||
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_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)
|
||||
|
||||
frame_duration = 0.020
|
||||
frame_duration = frame_duration_ms/1000.0
|
||||
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.write(memoryview(bytearray(samples)))
|
||||
ogg_opus_writer.close()
|
||||
|
||||
RNS.log(f"Read {read_bytes} bytes")
|
||||
RNS.log(f"Wrote {written_bytes} bytes")
|
||||
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
|
|
Loading…
Reference in New Issue