refactor: separate optional audioProcessing, add venv to installer
This commit is contained in:
parent
7218e59331
commit
ba2ed75396
59
installer.sh
59
installer.sh
@ -1,39 +1,47 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Dependency installation
|
# Rotary Phone Audio Guestbook Installer
|
||||||
echo "Installing dependencies..."
|
|
||||||
sudo apt-get update
|
|
||||||
if ! sudo apt-get install -y python3-pip python3-gpiozero; then
|
|
||||||
echo "Failed to install system packages."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Use --user flag for pip installations
|
echo "Starting the installation process..."
|
||||||
if ! pip3 install --user pydub pyaudio PyYAML sounddevice; then
|
|
||||||
echo "Failed to install Python packages."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Backup and modify PulseAudio configuration
|
# Update and install system dependencies
|
||||||
echo "Backing up and modifying PulseAudio configuration..."
|
echo "Installing additional dependencies..."
|
||||||
|
sudo apt-get install -y python3-pip python3-venv python3-gpiozero ffmpeg || {
|
||||||
|
echo "Failed to install required system packages."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set up Python virtual environment for project dependencies
|
||||||
|
echo "Setting up Python virtual environment..."
|
||||||
|
python3 -m venv ~/rotary-phone-venv || {
|
||||||
|
echo "Failed to create Python virtual environment."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
source ~/rotary-phone-venv/bin/activate
|
||||||
|
|
||||||
|
# Install Python dependencies in the virtual environment
|
||||||
|
pip install pydub pyaudio PyYAML sounddevice || {
|
||||||
|
echo "Failed to install Python dependencies."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Modify PulseAudio configuration for improved audio handling
|
||||||
|
echo "Configuring PulseAudio..."
|
||||||
sudo cp /etc/pulse/default.pa /etc/pulse/default.pa.backup
|
sudo cp /etc/pulse/default.pa /etc/pulse/default.pa.backup
|
||||||
echo "default-fragments = 5" | sudo tee -a /etc/pulse/default.pa
|
echo -e "default-fragments = 5\ndefault-fragment-size-msec = 2" | sudo tee -a /etc/pulse/default.pa
|
||||||
echo "default-fragment-size-msec = 2" | sudo tee -a /etc/pulse/default.pa
|
|
||||||
|
|
||||||
# Restart PulseAudio
|
# Restart PulseAudio to apply changes
|
||||||
pulseaudio -k
|
pulseaudio -k
|
||||||
pulseaudio --start
|
pulseaudio --start
|
||||||
|
|
||||||
# Display available sound cards and devices
|
# Display available sound cards and devices
|
||||||
echo "Available sound cards and devices:"
|
echo "Listing available sound cards and devices:"
|
||||||
aplay -l
|
aplay -l
|
||||||
|
|
||||||
# Prompt user for ALSA configuration values
|
# Prompt user for ALSA configuration values
|
||||||
echo "Configuring ALSA..."
|
echo "Configuring ALSA..."
|
||||||
read -p "Enter the card number for the default playback card (e.g., 0, 1): " playback_card
|
read -p "Enter the card number for the default playback card (e.g., 0, 1): " playback_card
|
||||||
read -p "Enter the card number for the default capture card (e.g., 0, 1): " capture_card
|
read -p "Enter the card number for the default capture card (e.g., 0, 1): " capture_card
|
||||||
|
|
||||||
# Use a consolidated prompt for sample rate to avoid duplication
|
|
||||||
read -p "Enter the default sample rate (e.g., 44100): " sample_rate
|
read -p "Enter the default sample rate (e.g., 44100): " sample_rate
|
||||||
while ! [[ "$sample_rate" =~ ^[89][0-9]{3}$|^[1-9][0-9]{4}$|^[1][0-8][0-9]{4}$|192000$ ]]; do
|
while ! [[ "$sample_rate" =~ ^[89][0-9]{3}$|^[1-9][0-9]{4}$|^[1][0-8][0-9]{4}$|192000$ ]]; do
|
||||||
echo "Invalid sample rate. Please enter a value between 8000 and 192000."
|
echo "Invalid sample rate. Please enter a value between 8000 and 192000."
|
||||||
@ -46,8 +54,10 @@ while ! [[ "$bit_depth" =~ ^(16|24|32)$ ]]; do
|
|||||||
read -p "Enter the bit depth (16, 24, 32): " bit_depth
|
read -p "Enter the bit depth (16, 24, 32): " bit_depth
|
||||||
done
|
done
|
||||||
|
|
||||||
# Write ALSA configuration to /etc/asound.conf
|
# Write ALSA configuration
|
||||||
|
echo "Applying ALSA configuration..."
|
||||||
sudo tee /etc/asound.conf >/dev/null <<EOF
|
sudo tee /etc/asound.conf >/dev/null <<EOF
|
||||||
|
# Custom ALSA configuration for Rotary Phone Audio Guestbook
|
||||||
defaults.pcm.rate_converter "samplerate"
|
defaults.pcm.rate_converter "samplerate"
|
||||||
defaults.pcm.dmix.rate $sample_rate
|
defaults.pcm.dmix.rate $sample_rate
|
||||||
defaults.pcm.dmix.format S$bit_depth
|
defaults.pcm.dmix.format S$bit_depth
|
||||||
@ -67,6 +77,13 @@ ctl.!default {
|
|||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# Test recording and playback functionality
|
||||||
|
echo "Testing recording and playback..."
|
||||||
|
arecord -D hw:$capture_card,0 -d 5 -f cd test-mic.wav && aplay test-mic.wav || {
|
||||||
|
echo "Test failed. Check your microphone and speaker setup."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
# Get the directory of the currently executing script
|
# Get the directory of the currently executing script
|
||||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import yaml
|
|||||||
from gpiozero import Button
|
from gpiozero import Button
|
||||||
from pydub import AudioSegment, playback
|
from pydub import AudioSegment, playback
|
||||||
|
|
||||||
import src.audioInterface as audioInterface
|
import audioInterface as audioInterface
|
||||||
|
|
||||||
# Set up logging
|
# Set up logging
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
@ -5,9 +5,6 @@ import time
|
|||||||
import wave
|
import wave
|
||||||
|
|
||||||
import pyaudio
|
import pyaudio
|
||||||
from pydub import AudioSegment
|
|
||||||
from pydub.effects import compress_dynamic_range, normalize
|
|
||||||
from pydub.scipy_effects import band_pass_filter
|
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -227,67 +224,3 @@ class AudioInterface:
|
|||||||
logger.info(f"Recording saved to {output_file}")
|
logger.info(f"Recording saved to {output_file}")
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
logger.error(f"Error writing to file {output_file}. Error: {e}")
|
logger.error(f"Error writing to file {output_file}. Error: {e}")
|
||||||
|
|
||||||
def post_process(self, output_file):
|
|
||||||
"""
|
|
||||||
Applies post-processing to the recorded audio and saves the processed files.
|
|
||||||
|
|
||||||
The post-processing includes filtering, normalization, and dynamic range compression.
|
|
||||||
The processed audio is saved in both WAV and MP3 formats.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
output_file (str): The base path for the output files.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
Exception: If there is an error during post-processing.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
source = AudioSegment.from_wav(output_file + ".wav")
|
|
||||||
filtered = self.filter_audio(source)
|
|
||||||
normalized = self.normalize_audio(filtered)
|
|
||||||
compressed = self.compress_audio(normalized)
|
|
||||||
|
|
||||||
normalized.export(output_file + "normalized.wav", format="wav")
|
|
||||||
compressed.export(output_file + "compressed.mp3", format="mp3")
|
|
||||||
logger.info("Post-processing completed successfully.")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Post-processing error: {e}")
|
|
||||||
|
|
||||||
def filter_audio(self, audio):
|
|
||||||
"""
|
|
||||||
Applies a band-pass filter to the given audio.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
audio (AudioSegment): The audio segment to be filtered.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
AudioSegment: The filtered audio segment.
|
|
||||||
"""
|
|
||||||
logger.info("Filtering audio.")
|
|
||||||
return band_pass_filter(audio, self.filter_low_freq, self.filter_high_freq)
|
|
||||||
|
|
||||||
def normalize_audio(self, audio):
|
|
||||||
"""
|
|
||||||
Normalizes the given audio segment.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
audio (AudioSegment): The audio segment to be normalized.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
AudioSegment: The normalized audio segment.
|
|
||||||
"""
|
|
||||||
logger.info("Normalizing audio.")
|
|
||||||
return normalize(audio)
|
|
||||||
|
|
||||||
def compress_audio(self, audio):
|
|
||||||
"""
|
|
||||||
Compresses the dynamic range of the given audio segment.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
audio (AudioSegment): The audio segment to be compressed.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
AudioSegment: The audio segment with compressed dynamic range.
|
|
||||||
"""
|
|
||||||
logger.info("Compressing dynamic range of audio.")
|
|
||||||
return compress_dynamic_range(audio)
|
|
||||||
|
74
src/audioProcessing.py
Normal file
74
src/audioProcessing.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import logging
|
||||||
|
from pydub import AudioSegment
|
||||||
|
from pydub.effects import compress_dynamic_range, normalize
|
||||||
|
from pydub.scipy_effects import band_pass_filter
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AudioProcessing:
|
||||||
|
|
||||||
|
def post_process(self, output_file):
|
||||||
|
"""
|
||||||
|
Applies post-processing to the recorded audio and saves the processed files.
|
||||||
|
|
||||||
|
The post-processing includes filtering, normalization, and dynamic range compression.
|
||||||
|
The processed audio is saved in both WAV and MP3 formats.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output_file (str): The base path for the output files.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: If there is an error during post-processing.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
source = AudioSegment.from_wav(output_file + ".wav")
|
||||||
|
filtered = self.filter_audio(source)
|
||||||
|
normalized = self.normalize_audio(filtered)
|
||||||
|
compressed = self.compress_audio(normalized)
|
||||||
|
|
||||||
|
normalized.export(output_file + "normalized.wav", format="wav")
|
||||||
|
compressed.export(output_file + "compressed.mp3", format="mp3")
|
||||||
|
logger.info("Post-processing completed successfully.")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Post-processing error: {e}")
|
||||||
|
|
||||||
|
def filter_audio(self, audio):
|
||||||
|
"""
|
||||||
|
Applies a band-pass filter to the given audio.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
audio (AudioSegment): The audio segment to be filtered.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
AudioSegment: The filtered audio segment.
|
||||||
|
"""
|
||||||
|
logger.info("Filtering audio.")
|
||||||
|
return band_pass_filter(audio, self.filter_low_freq, self.filter_high_freq)
|
||||||
|
|
||||||
|
def normalize_audio(self, audio):
|
||||||
|
"""
|
||||||
|
Normalizes the given audio segment.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
audio (AudioSegment): The audio segment to be normalized.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
AudioSegment: The normalized audio segment.
|
||||||
|
"""
|
||||||
|
logger.info("Normalizing audio.")
|
||||||
|
return normalize(audio)
|
||||||
|
|
||||||
|
def compress_audio(self, audio):
|
||||||
|
"""
|
||||||
|
Compresses the dynamic range of the given audio segment.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
audio (AudioSegment): The audio segment to be compressed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
AudioSegment: The audio segment with compressed dynamic range.
|
||||||
|
"""
|
||||||
|
logger.info("Compressing dynamic range of audio.")
|
||||||
|
return compress_dynamic_range(audio)
|
Loading…
Reference in New Issue
Block a user