From 30bb0d91f7edaf5574f5578c762db3a025c5c0d9 Mon Sep 17 00:00:00 2001 From: Nick Pourazima <12181252+nickpourazima@users.noreply.github.com> Date: Mon, 9 Oct 2023 21:27:53 -0600 Subject: [PATCH] feat: add installer script prompts (#15) add time expired sound update README --- README.md | 90 +++++++++++++++++++++--------------- audioInterface.py | 10 ++-- config.yaml | 2 +- installer.sh | 97 +++++++++++++++++++++++++++++++++++++++ sounds/time_exceeded.wav | Bin 0 -> 43610 bytes 5 files changed, 157 insertions(+), 42 deletions(-) create mode 100644 sounds/time_exceeded.wav diff --git a/README.md b/README.md index c91b3bb..b4d3ae1 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,20 @@ # Rotary Phone Audio Guestbook +This project transforms a rotary phone into a voice recorder for use at special events (i.e. wedding audio guestbook, etc.). + +![image](images/final_result_2.jpg) + - [Rotary Phone Audio Guestbook](#rotary-phone-audio-guestbook) - [Background](#background) - - [Post-Event](#post-event) - - [Future Work (Action Items)](#future-work-action-items) + - [Post-Event Reflection](#post-event-reflection) + - [Future Enhancements](#future-enhancements) + - [Quick-Start](#quick-start) - [Materials](#materials) - [Hardware](#hardware) - [Wiring](#wiring) - [Hook](#hook) - [Phone Cord](#phone-cord) - - [Microphone Replacement (Optional)](#microphone-replacement-optional) + - [Optional: Microphone Replacement](#optional-microphone-replacement) - [Software](#software) - [Dev Environment](#dev-environment) - [Installation](#installation) @@ -26,29 +31,34 @@ - [Restart ALSA](#restart-alsa) - [Support](#support) -This project transforms a rotary phone into a voice recorder for use at special events (i.e. wedding audio guestbook, etc.). - -![image](images/final_result_2.jpg) - ## Background -I was inspired by my own upcoming wedding to put together a DIY solution for an audio guestbook using a rotary phone. Most online rentals were charging $600 for an experience that didn't even offer the ability to add a custom voice mail and took about 4-6 weeks of turn around time to process the audio after the event. I tried to use as many parts that I had laying around to keep costs down. It worked out quite well and we were able to gather some very special voice messages. +Inspired by my own upcoming wedding, I created a DIY solution for an audio guestbook using a rotary phone. With most online rentals charging exorbitant fees without offering custom voicemail options, I sought a more affordable and customizable solution. Here, I've detailed a guide on creating your own audio guestbook. If you have questions, don't hesitate to reach out. -Below you will find a parts list and detailed setup guide. Please feel free to reach out to me with any questions. +## Post-Event Reflection -## Post-Event +The real event provided insights into areas of improvement for the setup. For instance, introducing a recording time limit became essential after some younger attendees left lengthy messages, draining the battery. Depending on the situation, you might also consider connecting the setup directly to a 5V power supply. -Since this was a trial by fire type of scenario there ended up being a few gotchas at the real event which I've since accounted for. Namely setting a time limit on the recording length as we had some youngsters leaving 5+ minute messages repeatedly and this ended up draining the battery. Alternatively, depending on your scenario, it might be preferable to attach directly to a 5V power supply. +## Future Enhancements -### Future Work (Action Items) +In anticipation of my wedding, I had code in place to detect dialed numbers from the rotary encoder, allowing us to play special messages for specific guests based on their dialed combination. However, this required users to dial zero before leaving a voice message, introducing an extra step. We opted for simplicity, but if you're interested in expanding on this, you're welcome to explore further. The details of this operation mode are described in [Mode 2](#operation-mode-2-audioguestbookwithrotarydialer) -A few weeks before the wedding I had the code registering dialed numbers from the rotary encoder with the goal of playing back special messages for certain guests who dialed a certain combination (i.e. dial an area code to hear a special message to my old roomates). The details of this operation mode are described in [Mode 2](#operation-mode-2-audioguestbookwithrotarydialer) below. In order to activate this mode I had to wait for input when the phone was off the hook. This required an extra step of dialing zero before leaving a normal voice message. In the end we decided to keep it simple and I've thus migrated this code to the dev branch along with the code to run through post-processing the audio in a separate process. -If anyone is interested in expanding this please feel free. +Additionally, threading the audio playback would be beneficial, allowing for a watchdog service to terminate the thread upon a hook callback. This would stop the message playback when a user hangs up. -I would also like to thread the audio playback so I can have a monitor/watchdog service terminate the thread upon hook callback so that the message doesn't continue playing once the user hangs up. +## Quick-Start + +To get started: + +```bash +chmod +x installer.sh +./installer.sh +``` ## Materials +
+ Parts List + | Part | Notes | Quantity | Cost | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------ | | [rotary phone](https://www.ebay.com/b/Rotary-Dial-Telephone/38038/bn_55192308) | Estate/garage/yard sales are probably the best places to find once of these. Ideally one with a phone jack since we will be using these four wires extensively. | 1 | $0.00-$60.00 | @@ -66,6 +76,8 @@ I would also like to thread the audio playback so I can have a monitor/watchdog | --- | **--- If replacing the built-it microphone ---** | --- | --- | | [LavMic](https://www.amazon.com/dp/B01N6P80OQ?ref=nb_sb_ss_w_as-reorder-t1_ypp_rep_k3_1_9&=&crid=15WZEWMZ17EM9&=&sprefix=saramonic) | Optional: if you'd like to replace the carbon microphone. This is an omnidirectional lavalier mic and outputs via a 3.5mm TRS | 1 | $24.95 | +
+ ## Hardware ### Wiring @@ -90,7 +102,7 @@ To accommodate either type, you'll need to update the `config.yaml` with the app ![image](images/pi_block_terminal_wiring.jpg) -- _Note: the green wire was used for the experimental rotary encoder feature identified in the [future work](#future-work-action-items) section._ +- _Note: the green wire was used for the experimental rotary encoder feature identified in the [future work](#future-enhancements) section._ | Rotary Phone Block Terminal | Top-down view | | ----------------------------------- | -------------------------------------------- | @@ -109,9 +121,11 @@ To accommodate either type, you'll need to update the `config.yaml` with the app - [Here's](https://superuser.com/questions/53957/what-do-alsa-devices-like-hw0-0-mean-how-do-i-figure-out-which-to-use) a good reference to device selection. - You can also check [this](https://stackoverflow.com/questions/32838279/getting-list-of-audio-input-devices-in-python) from Python. -### Microphone Replacement (Optional) +### Optional: Microphone Replacement -I found the sound quality of the built-in [carbon microphone](https://en.wikipedia.org/wiki/Carbon_microphone) on the rotary phone to be quite lacking in terms of amplitude, dynamic range and overall vocal quality. I tried boosting the gain from the digital (ALSA driver) side but this introduced an incredible amount of noise as expected. I then approached this from the analog domain and tried alternative circuitry to boost the sound quality based off this [carbon-to-dynamic converter](https://www.circuits-diy.com/mic-converter-circuit/). +For improved sound quality, consider replacing the built-in [carbon microphone](https://en.wikipedia.org/wiki/Carbon_microphone). + +I found the sound quality of the built-in mic on the rotary phone to be quite lacking in terms of amplitude, dynamic range and overall vocal quality. I tried boosting the gain from the digital (ALSA driver) side but this introduced an incredible amount of noise as expected. I then approached this from the analog domain and tried alternative circuitry to boost the sound quality based off this [carbon-to-dynamic converter](https://www.circuits-diy.com/mic-converter-circuit/). Might be worth a further investigation in the future since it retains the integrity of the original rotary phone. @@ -140,40 +154,41 @@ To replace: - rpi image: [Rasbian](https://www.raspberrypi.com/documentation/computers/getting-started.html) w/ SSH enabled - rpi on same network as development machine -- Desktop IDE: vscode w/ [SSH FS extension](https://marketplace.visualstudio.com/items?itemName=Kelvin.vscode-sshfs) +- _Optional: vscode w/ [SSH FS extension](https://marketplace.visualstudio.com/items?itemName=Kelvin.vscode-sshfs)_ [Here's](https://jayproulx.medium.com/headless-raspberry-pi-zero-w-setup-with-ssh-and-wi-fi-8ddd8c4d2742) a great guide to get the rpi setup headless w/ SSH & WiFi dialed in. ### Installation -After cloning the repository, there's an installer script available to ease the setup process. This script takes care of several tasks: +- On the networked rpi - clone the repository: -1. Install required dependencies. -2. Replace placeholders in the service file to adapt to your project directory. -3. Move the modified service file to the systemd directory. -4. Create necessary directories (recordings and sounds). -5. Grant execution permissions to the Python scripts. -6. Reload systemd, enable, and start the service. +```bash +git clone git@github.com:nickpourazima/rotary-phone-audio-guestbook.git +cd rotary-phone-audio-guestbook +``` -To run the installer, navigate to the project directory and execute: +- Next, use the installer script for a hassle-free setup.: ```bash chmod +x installer.sh ./installer.sh ``` +- Note, this script takes care of several tasks: + + 1. Install required dependencies. + 2. Populate config.yaml based on user input + 3. Replace placeholders in the service file to adapt to your project directory. + 4. Move the modified service file to the systemd directory. + 5. Create necessary directories (recordings and sounds). + 6. Grant execution permissions to the Python scripts. + 7. Reload systemd, enable, and start the service. + ### [audioGuestBook systemctl service](audioGuestBook.service) -The provided service ensures the Python script starts on boot. The installer script will place this service file in the `/etc/systemd/system`` directory and modify paths according to your project directory. +This service ensures smooth operation without manual intervention every time your Raspberry Pi boots up. The installer script will place this service file in the `/etc/systemd/system` directory and modify paths according to your project directory. -To manually control the service: - -```sh -sudo systemctl enable audioGuestBook.service -sudo systemctl start audioGuestBook.service -``` - -This service ensures smooth operation without manual intervention every time your Raspberry Pi boots up. +Manual control of the service is possible as it operates as any other [`.service` entity](https://www.freedesktop.org/software/systemd/man/systemd.service.html) ### [Config](config.yaml) @@ -197,9 +212,10 @@ This service ensures smooth operation without manual intervention every time you - On hook (depressed) - Nothing happens - Off hook (released) - - Plays back your own welcome message located in `/sounds/voicemail.wav` followed by the beep indicating ready to record. + - Plays back your own added welcome message located in `/sounds/voicemail.wav` followed by the [beep](/sounds/beep.wav) indicating the start of recording. - Begins recording the guests voice message. - Guest hangs up, recording is stopped and stored to the `/recordings/` directory. + - If the guest exceeds the **recording_limit** specified in the [config.yaml](/config.yaml), play the warning [time_exceeded.wav](/sounds/time_exceeded.wav) sound and stop recording. ### Operation Mode 2: [audioGuestBookwithRotaryDialer](./todo/audioGuestBookwithRotaryDialer.py) diff --git a/audioInterface.py b/audioInterface.py index bf0ae83..0155f8b 100644 --- a/audioInterface.py +++ b/audioInterface.py @@ -66,6 +66,8 @@ class AudioInterface: data = self.stream.read(self.chunk, exception_on_overflow=True) self.frames.append(data) else: + # Notify the user that their recording time is up + self.play("time_exceeded.wav") break except KeyboardInterrupt: logger.info("Done recording") @@ -110,14 +112,14 @@ class AudioInterface: except OSError as e: logger.error(f"Error writing to file {output_file}. Error: {e}") - def postProcess(self, outputFile): - source = AudioSegment.from_wav(outputFile + ".wav") + def post_process(self, output_file): + source = AudioSegment.from_wav(output_file + ".wav") filtered = self.filter_audio(source) normalized = self.normalize_audio(filtered) compressed = self.compress_audio(normalized) - normalized.export(outputFile + "normalized.wav", format="wav") - compressed.export(outputFile + "compressed.mp3", format="mp3") + normalized.export(output_file + "normalized.wav", format="wav") + compressed.export(output_file + "compressed.mp3", format="mp3") def filter_audio(self, audio): logger.info("Filtering...") diff --git a/config.yaml b/config.yaml index 6877949..493bcd1 100644 --- a/config.yaml +++ b/config.yaml @@ -4,7 +4,7 @@ buffer_size: 4096 channels: 2 hook_gpio: 22 playback_reduction: 16 -recording_limit: 60 +recording_limit: 300 rotary_gpio: 23 rotary_hold_repeat: true rotary_hold_time: 0.25 diff --git a/installer.sh b/installer.sh index 4b2d3da..9b4fef0 100644 --- a/installer.sh +++ b/installer.sh @@ -29,6 +29,103 @@ if ! sudo chown -R $USER:$USER "$DIR"; then exit 1 fi +# Prompt user for config values +echo "Please provide your configuration values:" + +while true; do + read -p "alsa_hw_mapping (default 1): " alsa_hw_mapping + alsa_hw_mapping=${alsa_hw_mapping:-1} + if [[ "$alsa_hw_mapping" =~ ^[0-9]+$ ]]; then + break + else + echo "Invalid number. Please enter a non-negative integer for alsa_hw_mapping." + fi +done + +while true; do + read -p "number of microphone channels (default 2): " channels + channels=${channels:-2} + if [[ "$channels" =~ ^[1-9][0-9]*$ ]]; then + break + else + echo "Invalid number. Please enter a positive integer for channels." + fi +done + +while true; do + read -p "recording sample rate (default 44100): " sample_rate + sample_rate=${sample_rate:-44100} + if [[ "$sample_rate" =~ ^[89][0-9]{3}$|^[1-9][0-9]{4}$|^[1][0-8][0-9]{4}$|192000$ ]]; then + break + else + echo "Invalid sample rate. Please enter a value between 8000 and 192000." + fi +done + +while true; do + read -p "recording format - INT16, INT32, FLOAT32 (default INT16): " format + format=${format:-INT16} + if [[ "$format" =~ ^(INT16|INT32|FLOAT32)$ ]]; then + break + else + echo "Invalid format. Please choose from INT16, INT32, or FLOAT32." + fi +done + +while true; do + read -p "hook type - normally open (NO) or normally closed (default NC): " hook_type + hook_type=${hook_type:-NC} + if [[ "$hook_type" =~ ^(NO|NC)$ ]]; then + break + else + echo "Invalid hook type. Please enter either NO or NC." + fi +done + +while true; do + read -p "hook_gpio (default 22): " hook_gpio + hook_gpio=${hook_gpio:-22} + if [[ "$hook_gpio" =~ ^[0-9]+$ ]]; then + break + else + echo "Invalid GPIO pin number. Please enter a non-negative integer." + fi +done + +while true; do + read -p "recording_limit - in seconds (default 300): " recording_limit + recording_limit=${recording_limit:-300} + if [[ "$recording_limit" =~ ^[0-9]+$ ]]; then + break + else + echo "Invalid recording limit. Please enter a non-negative integer for the number of seconds." + fi +done + +# Confirm before overwriting +read -p "This will overwrite the current config.yaml. Are you sure? (Y/N): " overwrite +if [[ "$overwrite" != "Y" ]]; then + echo "Config update aborted." + exit 1 +fi + +cat <"$DIR/config.yaml" +alsa_hw_mapping: $alsa_hw_mapping +beep_reduction: 24 +buffer_size: 4096 +channels: $channels +hook_gpio: $hook_gpio +playback_reduction: 16 +recording_limit: $recording_limit +rotary_gpio: 23 +rotary_hold_repeat: true +rotary_hold_time: 0.25 +sample_rate: $sample_rate +source_file: audioGuestBook.py +format: $format +hook_type: $hook_type +EOF + # Replace placeholders in the service file and save to temporary location if ! sed "s||$DIR|g" "$DIR/audioGuestBook.service.template" >/tmp/audioGuestBook.service; then echo "sed command failed." diff --git a/sounds/time_exceeded.wav b/sounds/time_exceeded.wav new file mode 100644 index 0000000000000000000000000000000000000000..16b76f20dbd1f9041311a4ca9d8998236db37b8b GIT binary patch literal 43610 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 literal 0 HcmV?d00001