diff --git a/.gitignore b/.gitignore index 0b1c5d4..1090132 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -*.wav -*.mp3 +*.code* \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..912f46f --- /dev/null +++ b/README.md @@ -0,0 +1,112 @@ +# 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.). + +- [Rotary Phone Audio Guestbook](#rotary-phone-audio-guestbook) + - [Background](#background) + - [Post-Event](#post-event) + - [Future Potential](#future-potential) + - [Materials](#materials) + - [Setup](#setup) + - [Hardware](#hardware) + - [Wiring](#wiring) + - [Microphone Replacement (Optional)](#microphone-replacement-optional) + - [Software](#software) + - [Dev Environment](#dev-environment) + - [Dependencies](#dependencies) + - [AudioInterface Class](#audiointerface-class) + - [rotaryGuestBook.service](#rotaryguestbookservice) + - [Operation Mode 1: rotaryGuestBook](#operation-mode-1-rotaryguestbook) + - [Operation Mode 2: rotaryGuestBookwithRotaryDialer](#operation-mode-2-rotaryguestbookwithrotarydialer) + +## 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. + +Below you will find a parts list and detailed setup guide. Please feel free to reach out to me with any questions. + +## Post-Event + +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. + +### Future Potential + +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 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-porcessing the audio in a separate process. +If any one is interested in expanding this please feel free. + +## Materials + +| 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 | +| [raspberry pi zero](https://www.raspberrypi.com/products/raspberry-pi-zero/) | I didn't realize how hard these are to find these days. You can use any rpi or arduino style single-board computer but be aware of size constraints (i.e. must fit inside the rotary phone enclosure) | 1 | $9.99 | +| [raspberry pi zero case](https://www.adafruit.com/product/3252) | Optional: added for protection. One of the cases on Amazon has a heat-sink cutout which might be nice for better heat dissapation since it will all be enclosed in the end. | 1 | $4.95 | +| [micro SD card](https://a.co/d/1gb2zhC) | Any high capacity/throughput micro SD card that is rpi compatible | 1 | $8.99 | +| [USB Audio Adapter](https://www.adafruit.com/product/1475) | Note: I removed the external plastic shell and directly soldered the wires instead of using the female 3.5mm receptacle. | 1 | $4.95 | +| [USB OTG Host Cable - MicroB OTG male to A female](https://www.adafruit.com/product/1099) | | 1 | $2.50 | +| --- | **--- If running off a battery ---** | --- | --- | +| [LiPo Battery](https://www.adafruit.com/product/2011)| Optional: maximize capacity based on what will fit within your rotary enclosure. |1| $12.50 | +| [LiPo Shim](https://www.adafruit.com/product/3196)| Optional: if you plan to run this off a LiPo I would recommend something like this to interface with the rpi zero. |1| $9.95 | +| [LiPo Charger](https://www.adafruit.com/product/1904) | Optional: for re-charging the LiPo. |1| $6.95 | +| --- | **--- 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 | + +## Setup + +### Hardware + +#### Wiring + +#### Microphone Replacement (Optional) + +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/). + +Might be worth a further investigation in the future since it retains the integrity of the original rotary phone. + +My final attempt involved the introduction of some post-proceesing (see dev branch) to bandpass some of the freqs outside the speech domain and add some normalization. The processing was costly in terms of processing and power consumption/rendering time and I ultimately decided it was worth acquiring something that yielded a better capture right out the gate. Crap in, crap out - as they say in the sound recording industry. + +To replace: + +- Unscrew mouthpiece and remove the carbon mic +- Pop out the plastic terminal housing with the two metal leads +- Unscrew red and black wires from terminal + +![image](images/phone2.jpg) + +### Software + +#### Dev Environment + +- 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) + +[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. + +#### Dependencies + +- `pip3 install -r requirements.txt` or pip install each manually: + - [GPIOZero](https://gpiozero.readthedocs.io) + - [Pydub](http://pydub.com/) + - [PyAudio](https://people.csail.mit.edu/hubert/pyaudio/) + - [PyYAML](https://pyyaml.org/) + +#### [AudioInterface Class](audioInterface.py) + +#### [rotaryGuestBook.service](rotaryGuestBook.service) + +This service starts the python script on boot. Place it in the `/etc/systemd/system` directory. + +`systemctl enable audioGuestBook.service` +`systemctl start audioGuestBook.service` + + +#### Operation Mode 1: [rotaryGuestBook](rotaryGuestBook.py) + +#### Operation Mode 2: [rotaryGuestBookwithRotaryDialer](rotaryGuestBookwithRotaryDialer.py) + +***Note*:** Untested - decided not to go this route for my own wedding + +- This mode is a special modification of the normal operation and requires a slightly different wiring connection since it accepts input from the rotary dialer. +- The idea was to playback special messages when particular users dial a certain number combination (i.e. 909 would play back a message for certain guests who lived with the groom in that area code). +- In this mode of operation the users will need to dial 0 on the rotary dialer in order to initiate the voicemail. diff --git a/audioGuestBook.py b/audioGuestBook.py new file mode 100644 index 0000000..b4c9bca --- /dev/null +++ b/audioGuestBook.py @@ -0,0 +1,73 @@ +#! /usr/bin/env python3 + +import audioInterface +import os +import yaml +import sys + +from datetime import datetime +from gpiozero import Button +from signal import pause +from pydub import AudioSegment +from pydub.playback import play + +try: + with open("config.yaml") as f: + config = yaml.load(f, Loader=yaml.FullLoader) +except FileNotFoundError as e: + print( + f"Could not find the config.yaml file. FileNotFoundError: {e}. Check config location and retry." + ) + sys.exit(1) + +hook = Button(config["hook_gpio"]) + + +def off_hook() -> None: + print("Phone off hook, ready to begin!") + audio_interface = audioInterface.AudioInterface(config, hook) + + # playback voice message through speaker + print("Playing voicemail message...") + play( + AudioSegment.from_wav( + os.path.dirname(os.path.abspath(config["source_file"])) + + "/sounds/voicemail.wav" + ) + - config["playback_reduction"] + ) + + # start recording beep + print("Playing beep...") + play( + AudioSegment.from_wav( + os.path.dirname(os.path.abspath(config["source_file"])) + "/sounds/beep.wav" + ) + - config["beep_reduction"] + ) + + # now, while phone is off the hook, record audio from the microphone + print("recording") + audio_interface.record() + audio_interface.stop() + output_file = ( + os.path.dirname(os.path.abspath(config["source_file"])) + + "/recordings/" + + f"{datetime.now().isoformat()}" + ) + audio_interface.close(output_file + ".wav") + print("Finished recording!") + + +def on_hook() -> None: + print("Phone on hook.\nSleeping...") + + +def main(): + hook.when_pressed = off_hook + hook.when_released = on_hook + pause() + + +if __name__ == "__main__": + main() diff --git a/audioGuestBook.service b/audioGuestBook.service new file mode 100644 index 0000000..d728ffc --- /dev/null +++ b/audioGuestBook.service @@ -0,0 +1,11 @@ +[Unit] +Description=Rotary Phone Guest Book Project +After=multi-user.target +[Service] +WorkingDirectory=/home//Desktop/rotaryGuestBook +User= +Type=simple +Restart=always +ExecStart=/usr/bin/env python3 /home//Desktop/rotaryGuestBook/rotaryGuestBook.py +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/audioInterface.py b/audioInterface.py new file mode 100644 index 0000000..61afa7d --- /dev/null +++ b/audioInterface.py @@ -0,0 +1,98 @@ +#! /usr/bin/env python3 + +import pyaudio +import time +import wave +from pydub import AudioSegment +from pydub.effects import normalize, compress_dynamic_range +from pydub.scipy_effects import band_pass_filter + + + +class AudioInterface: + def __init__(self, config, hook) -> None: + self.audio = pyaudio.PyAudio() + self.chunk = config["buffer_size"] + self.chans = config["channels"] + self.format = pyaudio.paInt16 # 16-bit resolution + self.frames = [] # raw data frames recorded from mic + self.hook = hook + self.samp_rate = config["sample_rate"] + self.recording_limit = config["recording_limit"] + self.dev_index = config["alsa_hw_mapping"] # device index found by p.get_device_info_by_index(ii) + + def record(self): + # create pyaudio stream + self.stream = self.audio.open( + format=self.format, + rate=self.samp_rate, + channels=self.chans, + input_device_index=self.dev_index, + input=True, + frames_per_buffer=self.chunk, + ) + # loop through stream and append audio chunks to frame array + try: + start = time.time() + while self.hook.is_pressed: + if time.time() - start < self.recording_limit: + data = self.stream.read(self.chunk, exception_on_overflow=True) + self.frames.append(data) + else: + break + except KeyboardInterrupt: + print("Done recording") + except Exception as e: + print(str(e)) + + def play(self, file): + self.wf = wave.open(file, "rb") + self.stream = self.audio.open( + format=self.audio.get_format_from_width(self.wf.getsampwidth()), + channels=self.wf.getnchannels(), + rate=self.wf.getframerate(), + output=True, + ) + """ Play entire file """ + data = self.wf.readframes(self.chunk) + while len(data): + self.stream.write(data) + data = self.wf.readframes(self.chunk) + + def stop(self): + # stop the stream + self.stream.stop_stream() + # close it + self.stream.close() + # terminate the pyaudio instantiation + self.audio.terminate() + + def close(self, output_file): + # save the audio frames as .wav file + with wave.open(output_file, "wb") as wavefile: + wavefile.setnchannels(self.chans) + wavefile.setsampwidth(self.audio.get_sample_size(self.format)) + wavefile.setframerate(self.samp_rate) + wavefile.writeframes(b"".join(self.frames)) + + def postProcess(self, outputFile): + """ + TODO: Evaluate whether this is worthwhile... + """ + source = AudioSegment.from_wav(outputFile + ".wav") + + print("Filtering...") + filtered = band_pass_filter(source, 300, 10000) + print("Normalizing...") + normalized = normalize(filtered) + + print("Compress Dynamic Range") + compressed = compress_dynamic_range(normalized) + + print("Exporting normalized") + normalized.export(outputFile + "normalized.wav", format="wav") + + print("Exporting compressed") + compressed.export(outputFile + "compressed.mp3", format="mp3") + + print("Finished...") \ No newline at end of file diff --git a/config.yaml b/config.yaml index 6c58642..778c85e 100644 --- a/config.yaml +++ b/config.yaml @@ -1,9 +1,12 @@ -hook_gpio: 22 -rotary_gpio: 3 -rotary_hold_time: 0.25 -rotary_hold_repeat: true -playback_reduction: 16 +alsa_hw_mapping: 1 beep_reduction: 24 -sample_rate: 44100 buffer_size: 4096 -channels: 2 \ No newline at end of file +channels: 2 +hook_gpio: 22 +playback_reduction: 16 +recording_limit: 60 +rotary_gpio: 23 +rotary_hold_repeat: true +rotary_hold_time: 0.25 +sample_rate: 44100 +source_file: "audioGuestBook.py" \ No newline at end of file diff --git a/images/PXL_20221127_191801539.jpg b/images/PXL_20221127_191801539.jpg new file mode 100644 index 0000000..9eb44a4 Binary files /dev/null and b/images/PXL_20221127_191801539.jpg differ diff --git a/images/PXL_20221127_191808218.jpg b/images/PXL_20221127_191808218.jpg new file mode 100644 index 0000000..ef4d8c0 Binary files /dev/null and b/images/PXL_20221127_191808218.jpg differ diff --git a/images/PXL_20221127_191822432.jpg b/images/PXL_20221127_191822432.jpg new file mode 100644 index 0000000..11011b5 Binary files /dev/null and b/images/PXL_20221127_191822432.jpg differ diff --git a/images/PXL_20221127_191828405.jpg b/images/PXL_20221127_191828405.jpg new file mode 100644 index 0000000..d0ac0c8 Binary files /dev/null and b/images/PXL_20221127_191828405.jpg differ diff --git a/images/PXL_20221127_191903412.jpg b/images/PXL_20221127_191903412.jpg new file mode 100644 index 0000000..73e23da Binary files /dev/null and b/images/PXL_20221127_191903412.jpg differ diff --git a/images/PXL_20221217_193545205.jpg b/images/PXL_20221217_193545205.jpg new file mode 100644 index 0000000..6fbaef5 Binary files /dev/null and b/images/PXL_20221217_193545205.jpg differ diff --git a/images/Raspberry-Pi-GPIO-Header-with-Photo-1024x683.png b/images/Raspberry-Pi-GPIO-Header-with-Photo-1024x683.png new file mode 100644 index 0000000..fac852c Binary files /dev/null and b/images/Raspberry-Pi-GPIO-Header-with-Photo-1024x683.png differ diff --git a/images/phone2.jpg b/images/phone2.jpg new file mode 100644 index 0000000..9c39be3 Binary files /dev/null and b/images/phone2.jpg differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f2007d7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +gpiozero==1.6.2 +pyaudio==0.2.12 +pydub==0.25.1 +PyYAML==6.0 diff --git a/rotaryGuestBook.py b/rotaryGuestBook.py deleted file mode 100755 index f9c444e..0000000 --- a/rotaryGuestBook.py +++ /dev/null @@ -1,215 +0,0 @@ -#! /usr/bin/env python3 - -import os -import yaml -import pyaudio -import wave -from datetime import datetime -from gpiozero import Button -from multiprocessing import Process -from signal import pause -from pydub.scipy_effects import band_pass_filter -from pydub.effects import normalize -from pydub import AudioSegment -from pydub.playback import play - -with open("config.yaml") as f: - config = yaml.load(f, Loader=yaml.FullLoader) - -hook = Button(config["hook_gpio"]) -# rotaryDial = Button(pin=config['rotary_gpio'], hold_time=config['rotary_hold_time'], hold_repeat=config['rotary_hold_repeat']) - -""" -TODO: These globals are a temp solution for the rotary dialer, would love to not -depend on globals for this logic. -""" -# count = 0 -# dialed = [] -# reset_flag = False - - -class AudioInterface: - def __init__(self) -> None: - self.audio = pyaudio.PyAudio() - self.samp_rate = config["sample_rate"] - self.chunk = config["buffer_size"] - self.chans = config["channels"] - self.format = pyaudio.paInt16 # 16-bit resolution - self.frames = [] # raw data frames recorded from mic - - def record(self): - # create pyaudio stream - dev_index = 1 # device index found by p.get_device_info_by_index(ii) - self.stream = self.audio.open( - format=self.format, - rate=self.samp_rate, - channels=self.chans, - input_device_index=dev_index, - input=True, - frames_per_buffer=self.chunk, - ) - # loop through stream and append audio chunks to frame array - try: - # TODO: either pass hook as object into class, or figure out another cleaner solution - while hook.is_pressed: - data = self.stream.read(self.chunk, exception_on_overflow=True) - self.frames.append(data) - except KeyboardInterrupt: - print("Done recording") - except Exception as e: - print(str(e)) - - def play(self, file): - self.wf = wave.open(file, "rb") - self.stream = self.audio.open( - format=self.audio.get_format_from_width(self.wf.getsampwidth()), - channels=self.wf.getnchannels(), - rate=self.wf.getframerate(), - output=True, - ) - """ Play entire file """ - data = self.wf.readframes(self.chunk) - while len(data): - self.stream.write(data) - data = self.wf.readframes(self.chunk) - - def stop(self): - # stop the stream - self.stream.stop_stream() - # close it - self.stream.close() - # terminate the pyaudio instantiation - self.audio.terminate() - - def close(self, outputFile): - # save the audio frames as .wav file - with wave.open(outputFile, "wb") as wavefile: - wavefile.setnchannels(self.chans) - wavefile.setsampwidth(self.audio.get_sample_size(self.format)) - wavefile.setframerate(self.samp_rate) - wavefile.writeframes(b"".join(self.frames)) - - def postProcess(self, outputFile): - """ - TODO: Evaluate whether this is worthwhile... - """ - source = AudioSegment.from_wav(outputFile + ".wav") - - print("Filtering...") - filtered = band_pass_filter(source, 300, 10000) - print("Normalizing...") - normalized = normalize(filtered) - - # print("Compress Dynamic Range") - # compressed = compress_dynamic_range(normalized) - - print("Exporting normalized") - normalized.export(outputFile + "normalized.wav", format="wav") - - # print("Exporting compressed") - # compressed.export(outputFile + "compressed.mp3", format="mp3") - - print("Finished...") - - -def offHook(): - print("Phone off hook, ready to begin!") - # if dialed and dialed[0] == 0: - audioInterface = AudioInterface() - - # playback voice message through speaker - print("Playing voicemail message...") - play( - AudioSegment.from_wav( - os.path.dirname(os.path.abspath("rotaryGuestBook.py")) - + "/sounds/voicemail.wav" - ) - - config["playback_reduction"] - ) - - # start recording beep - print("Playing beep...") - play( - AudioSegment.from_wav( - os.path.dirname(os.path.abspath("rotaryGuestBook.py")) + "/sounds/beep.wav" - ) - - config["beep_reduction"] - ) - - # now, while phone is not off the hook, record audio from the microphone - print("recording") - audioInterface.record() - audioInterface.stop() - outputFile = ( - os.path.dirname(os.path.abspath("rotaryGuestBook.py")) - + "/recordings/" - + f"{datetime.now().isoformat()}" - ) - audioInterface.close(outputFile + ".wav") - print("finished recording") - - """ - post processing - """ - # print("spawn postProcessing thread") - # Process(target=audioInterface.postProcess, args=(outputFil e,)).start() - - """ - rotary dialer special messages - """ - # if dialed[0:3] == [9,2,7]: - # # play special vm - # play(AudioSegment.from_wav(os.path.dirname(os.path.abspath("rotaryGuestBook.py")) + "/sounds/927.wav") - config['playback_reduction']) - - # elif dialed[0:4] == [5,4,5,3]: - # # play special vm - # play(AudioSegment.from_wav(os.path.dirname(os.path.abspath("rotaryGuestBook.py")) + "/sounds/beep.wav") - config['beep_reduction']) - - -def onHook(): - print("Phone on hook. Sleeping...") - # print("Resetting dial list") - # global dialed - # dialed = [] - # reset_pulse_counter() - - -# def dialing(): -# if hook.is_pressed: -# global count, reset_flag -# count+=1 -# print(f"dialing, increment count: {count}") -# reset_flag = False - -# def reset_pulse_counter(): -# global count, reset_flag -# count = 0 -# print(f"reset count: {count}") -# reset_flag = True - - -# def held(): -# if not reset_flag: -# print("holding") -# print(count) -# global dialed -# if (count == 10): -# dialed.append(0) -# else: -# dialed.append(count) -# print(f"number dialed: {dialed}") -# offHook() -# reset_pulse_counter() - - -def main(): - # rotaryDial.when_pressed = dialing - # rotaryDial.when_held = held - - hook.when_pressed = offHook - hook.when_released = onHook - pause() - - -if __name__ == "__main__": - main() diff --git a/sounds/beep.wav b/sounds/beep.wav new file mode 100644 index 0000000..4322b15 Binary files /dev/null and b/sounds/beep.wav differ diff --git a/todo/audioGuestBookwithRotaryDialer.py b/todo/audioGuestBookwithRotaryDialer.py new file mode 100644 index 0000000..f35904f --- /dev/null +++ b/todo/audioGuestBookwithRotaryDialer.py @@ -0,0 +1,136 @@ +#! /usr/bin/env python3 + +import audioInterface +import os +import yaml +import sys + +from datetime import datetime +from gpiozero import Button +from multiprocessing import Process +from signal import pause +from pydub import AudioSegment +from pydub.playback import play +from pydub.scipy_effects import band_pass_filter +from pydub.effects import normalize, compress_dynamic_range + +try: + with open("config.yaml") as f: + config = yaml.load(f, Loader=yaml.FullLoader) +except FileNotFoundError as e: + print( + f"Could not find the config.yaml file. FileNotFoundError: {e}. Check config location and retry." + ) + sys.exit(1) + +hook = Button(config["hook_gpio"]) +rotaryDial = Button(pin=config['rotary_gpio'], hold_time=config['rotary_hold_time'], hold_repeat=config['rotary_hold_repeat']) + +""" +TODO: These globals are a temp solution for the rotary dialer, would love to not +depend on globals for this logic. +""" +count = 0 +dialed = [] +reset_flag = False + +def off_hook() -> None: + print("Phone off hook, ready to begin!") + # if dialed and dialed[0] == 0: + audio_interface = audioInterface.AudioInterface(config, hook) + + # playback voice message through speaker + print("Playing voicemail message...") + play( + AudioSegment.from_wav( + os.path.dirname(os.path.abspath(config["source_file"])) + + "/sounds/voicemail.wav" + ) + - config["playback_reduction"] + ) + + # start recording beep + print("Playing beep...") + play( + AudioSegment.from_wav( + os.path.dirname(os.path.abspath(config["source_file"])) + "/sounds/beep.wav" + ) + - config["beep_reduction"] + ) + + # now, while phone is not off the hook, record audio from the microphone + print("recording") + audio_interface.record() + audio_interface.stop() + output_file = ( + os.path.dirname(os.path.abspath(config["source_file"])) + + "/recordings/" + + f"{datetime.now().isoformat()}" + ) + audio_interface.close(output_file + ".wav") + print("Finished recording!") + + """ + post processing + """ + print("spawn postProcessing thread") + Process(target=audio_interface.postProcess, args=(output_file,)).start() + + """ + rotary dialer special messages + """ + if dialed[0:3] == [9,2,7]: + # play special vm + play(AudioSegment.from_wav(os.path.dirname(os.path.abspath(config["source_file"])) + "/sounds/927.wav") - config['playback_reduction']) + + elif dialed[0:4] == [5,4,5,3]: + # play special vm + play(AudioSegment.from_wav(os.path.dirname(os.path.abspath(config["source_file"])) + "/sounds/beep.wav") - config['beep_reduction']) + + +def on_hook() -> None: + print("Phone on hook. Sleeping...") + print("Resetting dial list") + global dialed + dialed = [] + reset_pulse_counter() + + +def dialing() -> None: + if hook.is_pressed: + global count, reset_flag + count+=1 + print(f"dialing, increment count: {count}") + reset_flag = False + +def reset_pulse_counter() -> None: + global count, reset_flag + count = 0 + print(f"reset count: {count}") + reset_flag = True + + +def held() -> None: + if not reset_flag: + print("holding") + print(count) + global dialed + if (count == 10): + dialed.append(0) + else: + dialed.append(count) + print(f"number dialed: {dialed}") + off_hook() + reset_pulse_counter() + + +def main(): + rotaryDial.when_pressed = dialing + rotaryDial.when_held = held + hook.when_pressed = off_hook + hook.when_released = on_hook + pause() + + +if __name__ == "__main__": + main()