refactor: separate optional audioProcessing, add venv to installer

This commit is contained in:
Nick Pourazima 2024-02-18 11:54:01 -05:00
parent 7218e59331
commit ba2ed75396
4 changed files with 114 additions and 90 deletions

View File

@ -1,39 +1,47 @@
#!/bin/bash
# Dependency installation
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
# Rotary Phone Audio Guestbook Installer
# Use --user flag for pip installations
if ! pip3 install --user pydub pyaudio PyYAML sounddevice; then
echo "Failed to install Python packages."
exit 1
fi
echo "Starting the installation process..."
# Backup and modify PulseAudio configuration
echo "Backing up and modifying PulseAudio configuration..."
# Update and install system dependencies
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
echo "default-fragments = 5" | sudo tee -a /etc/pulse/default.pa
echo "default-fragment-size-msec = 2" | sudo tee -a /etc/pulse/default.pa
echo -e "default-fragments = 5\ndefault-fragment-size-msec = 2" | sudo tee -a /etc/pulse/default.pa
# Restart PulseAudio
# Restart PulseAudio to apply changes
pulseaudio -k
pulseaudio --start
# Display available sound cards and devices
echo "Available sound cards and devices:"
echo "Listing available sound cards and devices:"
aplay -l
# Prompt user for ALSA configuration values
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 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
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."
@ -46,8 +54,10 @@ while ! [[ "$bit_depth" =~ ^(16|24|32)$ ]]; do
read -p "Enter the bit depth (16, 24, 32): " bit_depth
done
# Write ALSA configuration to /etc/asound.conf
sudo tee /etc/asound.conf > /dev/null <<EOF
# Write ALSA configuration
echo "Applying ALSA configuration..."
sudo tee /etc/asound.conf >/dev/null <<EOF
# Custom ALSA configuration for Rotary Phone Audio Guestbook
defaults.pcm.rate_converter "samplerate"
defaults.pcm.dmix.rate $sample_rate
defaults.pcm.dmix.format S$bit_depth
@ -67,6 +77,13 @@ ctl.!default {
}
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
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

View File

@ -11,7 +11,7 @@ import yaml
from gpiozero import Button
from pydub import AudioSegment, playback
import src.audioInterface as audioInterface
import audioInterface as audioInterface
# Set up logging
logging.basicConfig(level=logging.INFO)

View File

@ -5,9 +5,6 @@ import time
import wave
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)
logger = logging.getLogger(__name__)
@ -227,67 +224,3 @@ class AudioInterface:
logger.info(f"Recording saved to {output_file}")
except OSError as 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
View 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)