Skip to content

Commit 9c6f62b

Browse files
authored
Merge pull request #2762 from makermelissa/main
Update Magic AI Storybook for Bookworm and new OpenAI API
2 parents 5047c10 + d6267a1 commit 9c6f62b

File tree

3 files changed

+28
-61
lines changed

3 files changed

+28
-61
lines changed

Magic_AI_Storybook/listener.py

Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,78 +2,45 @@
22
#
33
# SPDX-License-Identifier: MIT
44

5-
from queue import Queue
65
import time
76

87
import speech_recognition as sr
98

10-
119
class Listener:
1210
def __init__(
13-
self, api_key, energy_threshold=300, phrase_timeout=3.0, record_timeout=30
11+
self, api_key, energy_threshold=300, record_timeout=30
1412
):
1513
self.listener_handle = None
1614
self.microphone = sr.Microphone()
1715
self.recognizer = sr.Recognizer()
1816
self.recognizer.energy_threshold = energy_threshold
1917
self.recognizer.dynamic_energy_threshold = False
2018
self.recognizer.pause_threshold = 1
21-
self.last_sample = bytes()
2219
self.phrase_time = time.monotonic()
23-
self.phrase_timeout = phrase_timeout
2420
with self.microphone as source:
2521
self.recognizer.adjust_for_ambient_noise(
2622
source
2723
) # we only need to calibrate once, before we start listening
2824
self.record_timeout = record_timeout
29-
self.phrase_complete = False
30-
self.data_queue = Queue()
25+
self._audio = None
3126
self.listener_handle = None
3227
self.api_key = api_key
3328

3429
def listen(self, ready_callback=None):
3530
print("Start listening...")
36-
self.phrase_complete = False
37-
start = time.monotonic()
3831
self._start_listening()
3932
if ready_callback:
4033
ready_callback()
34+
4135
while (
4236
self.listener_handle and not self.speech_waiting()
43-
) or not self.phrase_complete:
44-
if self.phrase_time and time.monotonic() > start + self.phrase_timeout:
45-
self.last_sample = bytes()
46-
self.phrase_complete = True
47-
self.phrase_time = time.monotonic() - start
37+
):
38+
time.sleep(0.1)
4839
self.stop_listening()
4940

5041
def _save_audio_callback(self, _, audio):
5142
print("Saving audio")
52-
data = audio.get_raw_data()
53-
self.data_queue.put(data)
54-
55-
def _get_audio(self):
56-
"""Concatenate and convert the queued raw data back to audio and return it"""
57-
start = time.monotonic()
58-
if self.speech_waiting():
59-
self.phrase_complete = False
60-
if self.phrase_time and time.monotonic() > start + self.phrase_timeout:
61-
self.last_sample = bytes()
62-
self.phrase_complete = True
63-
self.phrase_time = time.monotonic() - start
64-
65-
# Concatenate our current audio data with the latest audio data.
66-
while self.speech_waiting():
67-
data = self.data_queue.get()
68-
self.last_sample += data
69-
70-
# Use AudioData to convert the raw data to wav data.
71-
return sr.AudioData(
72-
self.last_sample,
73-
self.microphone.SAMPLE_RATE,
74-
self.microphone.SAMPLE_WIDTH,
75-
)
76-
return None
43+
self._audio = audio
7744

7845
def _start_listening(self):
7946
if not self.listener_handle:
@@ -93,20 +60,19 @@ def is_listening(self):
9360
return self.listener_handle is not None
9461

9562
def speech_waiting(self):
96-
return not self.data_queue.empty()
63+
return self._audio is not None
9764

9865
def recognize(self):
99-
audio = self._get_audio()
100-
if audio:
66+
if self._audio:
10167
# Transcribe the audio data to text using Whisper
10268
print("Recognizing...")
10369
attempts = 0
10470
while attempts < 3:
10571
try:
10672
result = self.recognizer.recognize_whisper_api(
107-
audio, api_key=self.api_key
73+
self._audio, api_key=self.api_key
10874
)
109-
75+
self._audio = None
11076
return result.strip()
11177
except sr.RequestError as e:
11278
print(f"Error: {e}")

Magic_AI_Storybook/make_shortcut.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def main():
2828
APP_PATH = "~/Magic_AI_Storybook/story.py"
2929
APP_ICON = "~/Magic_AI_Storybook/images/magic_book_icon.png"
3030
FILENAME = "storybook.desktop"
31+
ENV_PATH = "~/story"
3132
AUTO_START = True
3233

3334
if os.geteuid() == 0:
@@ -41,12 +42,16 @@ def main():
4142

4243
APP_PATH = APP_PATH.replace("~", user_homedir)
4344
APP_ICON = APP_ICON.replace("~", user_homedir)
45+
PYTHON_PATH = "python"
46+
if ENV_PATH is not None:
47+
ENV_PATH = ENV_PATH.replace("~", user_homedir)
48+
PYTHON_PATH = ENV_PATH + "/bin/" + PYTHON_PATH
4449

4550
shortcut_template = f"""[Desktop Entry]
4651
Comment=Run {APP_TITLE}
4752
Terminal={"true" if RUN_IN_TERMINAL else "false"}
4853
Name={APP_TITLE}
49-
Exec=sudo python {APP_PATH}
54+
Exec=sudo -E env PATH=$PATH {PYTHON_PATH} {APP_PATH}
5055
Type=Application
5156
Icon={APP_ICON}
5257
"""

Magic_AI_Storybook/story.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import board
1717
import digitalio
1818
import neopixel
19-
import openai
19+
from openai import OpenAI
2020
import pygame
2121
from rpi_backlight import Backlight
2222
from adafruit_led_animation.animation.pulse import Pulse
@@ -87,12 +87,11 @@
8787

8888
# ChatGPT Parameters
8989
SYSTEM_ROLE = "You are a master AI Storyteller that can tell a story of any length."
90-
CHATGPT_MODEL = "gpt-3.5-turbo"
90+
CHATGPT_MODEL = "gpt-3.5-turbo" # You can also use "gpt-4", which is slower, but more accurate
9191
WHISPER_MODEL = "whisper-1"
9292

9393
# Speech Recognition Parameters
9494
ENERGY_THRESHOLD = 300 # Energy level for mic to detect
95-
PHRASE_TIMEOUT = 1.0 # Space between recordings for separating phrases
9695
RECORD_TIMEOUT = 30 # Maximum time in seconds to wait for speech
9796

9897
# Do some checks and Import API keys from API_KEYS_FILE
@@ -118,7 +117,10 @@
118117
if len(config["openai"]["OPENAI_API_KEY"]) < 10:
119118
print("Please set OPENAI_API_KEY in your API keys file with a valid key.")
120119
sys.exit(1)
121-
openai.api_key = config["openai"]["OPENAI_API_KEY"]
120+
openai = OpenAI(
121+
# This is the default and can be omitted
122+
api_key=config["openai"]["OPENAI_API_KEY"],
123+
)
122124

123125
# Check that the prompt file exists and load it
124126
if not os.path.isfile(PROMPT_FILE):
@@ -250,7 +252,7 @@ def start(self):
250252

251253
# Initialize the Listener
252254
self.listener = Listener(
253-
openai.api_key, ENERGY_THRESHOLD, PHRASE_TIMEOUT, RECORD_TIMEOUT
255+
openai.api_key, ENERGY_THRESHOLD, RECORD_TIMEOUT
254256
)
255257

256258
# Preload remaining images
@@ -704,18 +706,11 @@ def _sleep(self):
704706
self._sleeping = True
705707
self._set_status_color(NEOPIXEL_SLEEP_COLOR)
706708
self.sleep_check_delay = 0
707-
self.saved_screen = self.screen.copy()
708-
self.screen.fill((0, 0, 0))
709-
pygame.display.update()
710709
self.backlight.power = False
711710

712711
def _wake(self):
713712
# Turn on the screen
714713
self.backlight.power = True
715-
if self.saved_screen:
716-
self.screen.blit(self.saved_screen, (0, 0))
717-
pygame.display.update()
718-
self.saved_screen = None
719714
self.sleep_check_delay = 0.1
720715
self._set_status_color(NEOPIXEL_READING_COLOR)
721716
self._sleeping = False
@@ -728,8 +723,9 @@ def _make_story_prompt(self, request):
728723
def _sendchat(self, prompt):
729724
response = ""
730725
print("Sending to chatGPT")
726+
print("Prompt: ", prompt)
731727
# Package up the text to send to ChatGPT
732-
completion = openai.ChatCompletion.create(
728+
stream = openai.chat.completions.create(
733729
model=CHATGPT_MODEL,
734730
messages=[
735731
{"role": "system", "content": SYSTEM_ROLE},
@@ -738,9 +734,9 @@ def _sendchat(self, prompt):
738734
stream=True,
739735
)
740736

741-
for chunk in completion:
742-
if "delta" in chunk.choices[0] and "content" in chunk.choices[0]["delta"]:
743-
response += chunk.choices[0]["delta"]["content"]
737+
for chunk in stream:
738+
if chunk.choices[0].delta.content is not None:
739+
response += chunk.choices[0].delta.content
744740
if self._sleep_request:
745741
return None
746742

0 commit comments

Comments
 (0)