Skip to content

Commit 4a1f324

Browse files
authored
Merge pull request #2479 from makermelissa/main
Magic AI Storybook
2 parents 47c79e8 + 8e12d6c commit 4a1f324

13 files changed

+829
-0
lines changed

Magic_AI_Storybook/bookprompt.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Write a complete story with a title and a body of approximately
2+
{STORY_WORD_LENGTH} words long and a happy ending. The specific
3+
story request is "{STORY_REQUEST}".
Binary file not shown.
53.2 KB
Binary file not shown.
57.1 KB
Binary file not shown.
66.8 KB
Binary file not shown.
23.9 KB
Loading
23.5 KB
Loading

Magic_AI_Storybook/images/loading.png

761 KB
Loading
589 KB
Loading

Magic_AI_Storybook/images/welcome.png

747 KB
Loading

Magic_AI_Storybook/keys.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[openai]
2+
OPENAI_API_KEY = sk-...

Magic_AI_Storybook/listener.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
from datetime import datetime, timedelta
6+
from queue import Queue
7+
8+
import speech_recognition as sr
9+
10+
11+
class Listener:
12+
def __init__(self, energy_threshold=1000, phrase_timeout=3.0, record_timeout=30):
13+
self.listener_handle = None
14+
self.recognizer = sr.Recognizer()
15+
self.recognizer.energy_threshold = energy_threshold
16+
self.recognizer.dynamic_energy_threshold = False
17+
self.recognizer.pause_threshold = 1
18+
self.last_sample = bytes()
19+
self.phrase_time = datetime.utcnow()
20+
self.phrase_timeout = phrase_timeout
21+
self.record_timeout = record_timeout
22+
self.phrase_complete = False
23+
# Thread safe Queue for passing data from the threaded recording callback.
24+
self.data_queue = Queue()
25+
self.mic_dev_index = None
26+
27+
def listen(self, ready_callback=None):
28+
self.phrase_complete = False
29+
start = datetime.utcnow()
30+
self.start_listening()
31+
if ready_callback:
32+
ready_callback()
33+
while (
34+
self.listener_handle
35+
and not self.speech_waiting()
36+
or not self.phrase_complete
37+
):
38+
if self.phrase_time and start - self.phrase_time > timedelta(
39+
seconds=self.phrase_timeout
40+
):
41+
self.last_sample = bytes()
42+
self.phrase_complete = True
43+
self.phrase_time = start
44+
self.stop_listening()
45+
46+
def start_listening(self):
47+
if not self.listener_handle:
48+
with sr.Microphone() as source:
49+
self.recognizer.adjust_for_ambient_noise(source)
50+
self.listener_handle = self.recognizer.listen_in_background(
51+
sr.Microphone(),
52+
self.record_callback,
53+
phrase_time_limit=self.record_timeout,
54+
)
55+
56+
def stop_listening(self, wait_for_stop=False):
57+
if self.listener_handle:
58+
self.listener_handle(wait_for_stop=wait_for_stop)
59+
self.listener_handle = None
60+
61+
def is_listening(self):
62+
return self.listener_handle is not None
63+
64+
def record_callback(self, _, audio: sr.AudioData) -> None:
65+
# Grab the raw bytes and push it into the thread safe queue.
66+
data = audio.get_raw_data()
67+
self.data_queue.put(data)
68+
69+
def speech_waiting(self):
70+
return not self.data_queue.empty()
71+
72+
def get_speech(self):
73+
if self.speech_waiting():
74+
return self.data_queue.get()
75+
return None
76+
77+
def get_audio_data(self):
78+
now = datetime.utcnow()
79+
if self.speech_waiting():
80+
self.phrase_complete = False
81+
if self.phrase_time and now - self.phrase_time > timedelta(
82+
seconds=self.phrase_timeout
83+
):
84+
self.last_sample = bytes()
85+
self.phrase_complete = True
86+
self.phrase_time = now
87+
88+
# Concatenate our current audio data with the latest audio data.
89+
while self.speech_waiting():
90+
data = self.get_speech()
91+
self.last_sample += data
92+
93+
# Use AudioData to convert the raw data to wav data.
94+
with sr.Microphone() as source:
95+
audio_data = sr.AudioData(
96+
self.last_sample, source.SAMPLE_RATE, source.SAMPLE_WIDTH
97+
)
98+
return audio_data
99+
100+
return None

0 commit comments

Comments
 (0)