[{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/002/236/original/TalkingClock.png?1769458096","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n        \u003ch1\u003eThe Idea\u003c/h1\u003e\n\u003cp\u003eThe Fruit Jam, with its built‑in audio, Wi‑Fi, HSTX display, SD card, generous memory, and other little goodies, is a fantastic platform for mixing creativity with a bit of technical magic. Add CircuitPython to the mix and you’ve got a wonderfully flexible development environment.\u003c/p\u003e\n\u003cp\u003eWith one of my Fruit Jams, I built a display that shows an analog clock face, rotates through an album of photos of my grandkids as the background, and pulls weather data from Adafruit IO that I can scroll through using an IR remote.\u003c/p\u003e\n\u003cp\u003eWith the built‑in TLV320 DAC, I also wanted it to sound like a clock — a really big clock. I wrote functions to synthesize polyphonic tones that mimic the classic Westminster Quarters every fifteen minutes, and at the top of each hour it plays a WAV file of a single church‑bell toll, repeated once for each hour. It turned into a great little project that let me explore different elements of CircuitPython and many parts of the Fruit Jam board.\u003c/p\u003e\n\u003cp\u003eIt’s been running for months now and has become a charming addition to my office. And the best part is that I can keep extending it whenever a new idea strikes.\u003c/p\u003e\n\u003ch1\u003eI Discovered Copilot Audio Expression\u003c/h1\u003e\n\u003cp\u003eThe other day, while playing with Copilot, I noticed a link to Labs at the top of the screen. Clicking through, I found Copilot Audio Expression, described as “\u003cem\u003ean experimental tool designed for effortless audio creation using Copilot’s latest voice‑generation models.\u003c/em\u003e” You simply type a word or phrase, and it “speaks” it for you — then you can download the result as an MP3.\u003c/p\u003e\n\u003cp\u003eA few quick prompts and some experimenting with the different settings sparked an idea: \u003cstrong\u003eWhy not make my Fruit Jam clock talk?\u003c/strong\u003e\u003c/p\u003e\n\u003ch3\u003eEverything old is new again...\u003c/h3\u003e\n\u003cp\u003eStringing together audio samples to form phrases is nothing new — people have been doing it for decades, long before digital audio. One of my earliest Learn Guide projects, the Titano Weather Station, used prerecorded samples as alarms (“time for bed”). But once you go beyond a handful of words, the challenge grows quickly. A talking clock or calendar needs nearly a hundred different clips: dates, day names, months, ordinals, and more. Gathering, recording, editing, and normalizing all of that becomes a real chore.\u003c/p\u003e\n\u003cp\u003eThat’s where this new AI tool shines. I can generate clean, consistent audio samples for every word or phrase I need in just a few minutes.\u003c/p\u003e\n\u003cp\u003eSo let’s build a Fruit Jam clock that can announce the time and date.\u003c/p\u003e\n      \n\n\n","metadata":{}},{"element_type":"text","content":"\n        \u003ch1\u003eStep by Step\u003c/h1\u003e\n\u003cp\u003eThis article focuses specifically on adding the talking capability to my existing Fruit Jam clock project. The display, weather integration, IR remote navigation, and Westminster chimes are all already in place — and I can cover those in a future article if there’s interest. For now, we’ll walk through the audio‑generation workflow that makes the clock speak.\u003c/p\u003e\n\u003cp\u003eThe overall process breaks down into four main steps:\u003c/p\u003e\n\u003cp style=\"padding-left: 40px;\"\u003e1. \u0026nbsp; \u0026nbsp; Generate all the required words and phrases using \u003cstrong\u003eCopilot Audio Expression\u003c/strong\u003e\u003cbr\u003e2. \u0026nbsp; \u0026nbsp; Convert the MP3 files to WAV format using \u003cstrong\u003eFFmpeg\u003c/strong\u003e\u003cbr\u003e3. \u0026nbsp; \u0026nbsp; Organize the files on the SD card and create logical CircuitPython lists\u003cbr\u003e4. \u0026nbsp; \u0026nbsp; Write functions that assemble and play the spoken phrases\u003c/p\u003e\n\u003ch1\u003eGenerating the samples\u003c/h1\u003e\n\u003cp\u003eFor my project, I needed the Fruit Jam to be able to speak:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThe \u003cstrong\u003etime\u003c/strong\u003e (hours, minutes, AM, PM, noon, midnight)\u003c/li\u003e\n\u003cli\u003eThe \u003cstrong\u003edate\u003c/strong\u003e (day name, month name, ordinal date, year)\u003c/li\u003e\n\u003cli\u003eThe \u003cstrong\u003equarter hours\u003c/strong\u003e (“quarter past”, “half past”, “quarter til”)\u003c/li\u003e\n\u003cli\u003eAnd as a bonus: holidays, special days, and fun extras\u0026nbsp;\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eCopilot Audio Expression made this surprisingly easy.\u003c/p\u003e\n\u003cp\u003eBefore we dive in, note that Copilot Audio Expression is part of Copilot AI Labs and is marked as experimental. It may change or disappear at any time. The site I used is: \u003ca title=\"Copilot Audio Expression\" href=\"https://copilot.microsoft.com/labs/audio-expression\" target=\"_blank\"\u003eCopilot Audio Expression Website.\u003c/a\u003e\u003c/p\u003e\n\u003ch3\u003e\u003cstrong\u003eCopilot Audio Expression Website\u003c/strong\u003e\u003c/h3\u003e\n      ","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/002/237/original/CAE.png?1769460138","metadata":{}},{"element_type":"text","content":"\n  \n  \n        \u003cp\u003eThe interface is simple and intuitive. You get three main controls to shape the voice output:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eMode (Emotive, Story and Scripted)\u003c/li\u003e\n\u003cli\u003eVoice\u003c/li\u003e\n\u003cli\u003eStyle\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3\u003eChoosing the right settings\u003c/h3\u003e\n\u003cp\u003eFor \u003cstrong\u003eMode\u003c/strong\u003e, I used \u003cstrong\u003eScripted\u003c/strong\u003e. This mode sticks closely to the text you type. The other modes tend to embellish or expand on the prompt. For example, entering “The rain in Spain falls mainly on the plain” in Emotive mode produced a 31‑second commentary — entertaining, but not ideal for a talking clock.\u003c/p\u003e\n\u003cp\u003eFor \u003cstrong\u003eVoice\u003c/strong\u003e, I chose \u003cstrong\u003eRain\u003c/strong\u003e, a female voice with a British accent. Fun fact: she sounds exactly like my old boss - so even thought I am happily retired she still speaks to me every day!\u003c/p\u003e\n\u003cp\u003eFor\u0026nbsp;\u003cstrong\u003eStyle\u003c/strong\u003e, I used \u003cstrong\u003eNeutral\u003c/strong\u003e for most clips but switched to \u003cstrong\u003eExcitement\u003c/strong\u003e for special phrases like “Happy Birthday”. Have a little fun with it!\u003c/p\u003e\n\u003ch3\u003eGenerating the Words\u003c/h3\u003e\n\u003cp\u003eOnce your settings are dialed in, the workflow is straightforward:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eType a word or phrase\u003c/li\u003e\n\u003cli\u003eClick\u0026nbsp;\u003cstrong\u003eGenerate\u003c/strong\u003e\n\u003c/li\u003e\n\u003cli\u003eListen to the preview\u003c/li\u003e\n\u003cli\u003eif you like the sample that was generated, click the download icon to save the MP3\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eAbout 95% of the time, the first result was perfect. If something sounded off — pronunciation, pacing, tone — clicking Generate again usually produced a slightly different version. I rarely needed more than one retry.\u003c/p\u003e\n\u003cp\u003eAs you go along, it’s best to rename each MP3 file right after downloading it. I use a simple, consistent pattern:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eall lowercase\u003c/li\u003e\n\u003cli\u003eunderscores instead of spaces\u003c/li\u003e\n\u003cli\u003edescriptive but short\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFor example:\u0026nbsp;\u003c/p\u003e\n\u003cp style=\"padding-left: 40px;\"\u003e\u003cem\u003ejanuary.mp3\u003c/em\u003e\u003cbr\u003e\u003cem\u003etwenty_sixth.mp3\u003c/em\u003e\u003cbr\u003e\u003cem\u003equarter_past.mp3\u003c/em\u003e\u003cbr\u003e\u003cem\u003ehappy_birthday.mp3\u003c/em\u003e\u003c/p\u003e\n\u003ch3\u003eHandling Tricky Words\u003c/h3\u003e\n\u003cp\u003eOccasionally the model misunderstands. For example, when I typed \u003cstrong\u003e“AM”\u003c/strong\u003e, it pronounced it as the word \u003cem\u003e“am”\u003c/em\u003e. Trying \u003cstrong\u003e“A M”\u003c/strong\u003e (with a space) fixed it immediately. Small variations in spacing or capitalization often help.\u003c/p\u003e\n\u003ch3\u003eRate Limits\u003c/h3\u003e\n\u003cp\u003eI also discovered there’s a soft limit to how many clips you can generate in one session. After about 30 phrases, I received a “Too Busy – Try Again Later” message. I took a break, came back a couple of hours later, and generated another batch. In total, it took me three short sessions to create all the samples I needed — not bad for a free tool.\u003c/p\u003e\n      \n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch1\u003eConverting MP3 Files to WAV with FFmpeg\u003c/h1\u003e\n\u003cp\u003eOnce you’ve generated all your spoken phrases with Copilot Audio Expression, place them all in a single folder containing only these MP3 files. Before we can use them on the Fruit Jam, we need to convert them into WAV format, which is what the FruitJam helper library expects. (Although Fruit Jam can handle MP3s, I’ve found WAV files tend to perform more reliably.)\u003c/p\u003e\n\u003ch3\u003eWhat Is FFmpeg?\u003c/h3\u003e\n\u003cp\u003eFFmpeg is a \u003cstrong\u003efree, open‑source command‑line tool\u003c/strong\u003e used for converting, processing, and analyzing audio and video files. It’s extremely powerful and widely used in everything from video editors to streaming services.\u003c/p\u003e\n\u003cp\u003eFor our purposes, we’re using it for one simple job: \u003cstrong\u003eConvert MP3 files into WAV files\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eFFmpeg is \u003cstrong\u003enot built into Windows\u003c/strong\u003e, so you’ll need to install it. It also works on macOS and Linux, so this step is not Windows‑only — but the installation process varies slightly by platform.\u003c/p\u003e\n\u003ch3\u003eInstalling FFmpeg on Windows\u003c/h3\u003e\n\u003cp\u003eIf you’re on Windows 10 or 11, the easiest method is:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eDownload a prebuilt FFmpeg ZIP from a trusted distributor (e.g., Gyan.dev or BtbN).\u003c/li\u003e\n\u003cli\u003eExtract the ZIP.\u003c/li\u003e\n\u003cli\u003eAdd the \u0026nbsp;folder to your system PATH so you can run \u0026nbsp;from any terminal.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e(If any of this reads like Greek to you, consult your friendly chat-bot - it should be able to walk you through this).\u003c/p\u003e\n\u003cp\u003eOnce installed, open \u003cstrong\u003ePowerShell\u003c/strong\u003e — the built‑in command‑line tool in Windows — and type:\u003c/p\u003e\n\u003cp style=\"padding-left: 40px;\"\u003e\u003cem\u003e\u003cstrong\u003effmpeg -version\u003c/strong\u003e\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eIf you see version information, you’re ready to go.\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"alert","content":"\u003cp\u003ePowerShell is a command‑line tool that comes built into Windows 10 and 11. It lets you run commands and scripts — including FFmpeg — without installing anything extra.\nTo open it:\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003eClick the Start button\u003c/li\u003e\n\u003cli\u003eType PowerShell\u003c/li\u003e\n\u003cli\u003ePress Enter\nYou’ll see a black or blue background window where you can type commands.\u003c/li\u003e\n\u003c/ul\u003e","metadata":{"markdown":"PowerShell is a command‑line tool that comes built into Windows 10 and 11. It lets you run commands and scripts — including FFmpeg — without installing anything extra.\nTo open it:\n - Click the Start button\n - Type PowerShell\n - Press Enter\nYou’ll see a black or blue background window where you can type commands.","alert_type":"primary","icon":"thinking"}},{"element_type":"alert","content":"\u003cp\u003eInstalling FFmpeg on macOS or Linux\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003emacOS (Homebrew): brew install ffmpeg\u003c/li\u003e\n\u003cli\u003eLinux: sudo apt install ffmpeg\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eOnce installed, the usage is identical across all platforms.\u003c/p\u003e","metadata":{"markdown":"Installing FFmpeg on macOS or Linux\n - macOS (Homebrew): brew install ffmpeg\n - Linux: sudo apt install ffmpeg\n\nOnce installed, the usage is identical across all platforms.","alert_type":"info","icon":"check"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eBatch‑Converting MP3s to WAV\u003c/h3\u003e\n\u003cp\u003eWith FFmpeg installed and your MP3s neatly organized in one folder, converting them is straightforward.\u003c/p\u003e\n\u003cp\u003eTo convert a single file, open a terminal in that folder and run:\u003c/p\u003e\n\u003cp style=\"padding-left: 40px;\"\u003e\u003cem\u003e\u003cstrong\u003effmpeg -i input.mp3 output.wav\u0026nbsp;\u003c/strong\u003e(where 'input' is the file to convert and 'output' is the new file name)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eBut doing this one file at a time gets old fast. Instead, you can batch‑convert everything in the directory with PowerShell in Windows: In PowerShell window, type:\u003c/p\u003e\n\u003cp style=\"padding-left: 40px;\"\u003e\u003cem\u003e\u003cstrong\u003eforeach ($f in Get-ChildItem *.mp3) {ffmpeg -i \"$($f.Name)\" \"$($f.BaseName).wav\"}\u003c/strong\u003e\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eor in macOS/Linux:\u003c/p\u003e\n\u003cp style=\"padding-left: 40px;\"\u003e\u003cem\u003e\u003cstrong\u003efor f in *.mp3; do\u003cbr\u003e\u0026nbsp; \u0026nbsp; ffmpeg -i \"$f\" \"${f%.mp3}.wav\"\u003cbr\u003edone\u003c/strong\u003e\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eAfter running the batch conversion, you’ll have a matching set of WAV files with the same names as your MP3s — perfect for use on the Fruit Jam.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch1\u003ePreparing the File Names for CircuitPython Lists\u003c/h1\u003e\n\u003cp\u003eOnce all of the WAV files are generated and organized, the next step is getting those filenames into CircuitPython in a clean, structured way. You can type them manually, but with dozens of files it’s far easier — and far less error‑prone — to automate the process.\u003c/p\u003e\n\u003cp\u003eHere’s the workflow I used to turn a folder full of WAV files into neatly formatted Python list entries.\u003c/p\u003e\n\u003ch3\u003eGenerate a Text File Containing All Filenames\u003c/h3\u003e\n\u003cp\u003eFirst, create a simple text file listing every file in the folder. There are several ways to do this (PowerShell, Command Prompt, Finder/Terminal on macOS), but the goal is the same: produce a list like:\u003c/p\u003e\n\u003cp style=\"padding-left: 40px;\"\u003e\u003cem\u003ejanuary.wav\u003cbr\u003efebruary.wav\u003c/em\u003e\u003cbr\u003e\u003cem\u003etwenty_six.wav\u003c/em\u003e\u003cbr\u003e\u003cem\u003equarter_past.wav\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eI save this file with a \u003cstrong\u003e.csv\u003c/strong\u003e extension so Excel would open it directly.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"alert","content":"\u003cp\u003eTo create the text file, start PowerShell in the directory with the WAV files and type:\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003eGet-ChildItem -Name \u003e filelist.csv\u003c/li\u003e\n\u003c/ul\u003e","metadata":{"markdown":"To create the text file, start PowerShell in the directory with the WAV files and type:\n - Get-ChildItem -Name \u003e filelist.csv","alert_type":"primary","icon":"bolt"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eImport the CSV into Excel \u0026amp; Create the Full Path for Your Code\u003c/h3\u003e\n\u003cp\u003eOpen the CSV in Excel and you’ll see one filename per row in column A. This becomes the foundation for building the full file paths that CircuitPython will use.\u003c/p\u003e\n\u003cp\u003e\u003c/p\u003e\n\u003cp\u003eThe goal is to create a fully qualified filename for each entry that we can paste into a CircuitPython list. Let’s assume the folder on the SD card containing all WAV files is named \u003cstrong\u003etvoice\u003c/strong\u003e. For the list, we need the full path:\u0026nbsp;\u003cem\u003e\u003cstrong\u003e\"/sd/words/filename.wav\".\u003c/strong\u003e\u003c/em\u003e For the actual list, each entry needs:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eFour leading spaces (for indentation)\u003c/li\u003e\n\u003cli\u003eSingle quotes around the file name\u003c/li\u003e\n\u003cli\u003eA trailing comma\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003c/p\u003e\n\u003cp\u003eTo generate this automatically, put the following formula in \u003cstrong\u003ecell B1\u003c/strong\u003e:\u003c/p\u003e\n\u003cp style=\"padding-left: 40px;\"\u003e\u003cem\u003e\u003cstrong\u003e=\" \u0026nbsp; \u0026nbsp;\"\u0026amp;CHAR(39)\u0026amp;\"/sd/tvoice\"\u0026amp;A1\u0026amp;CHAR(39)\u0026amp;\",\"\u003c/strong\u003e\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003c/p\u003e\n\u003cp\u003eCopy this formula down for all filenames.\u003c/p\u003e\n\u003cp\u003eNext, select all the generated full paths, \u003cstrong\u003eCopy\u003c/strong\u003e, then \u003cstrong\u003ePaste → Values\u003c/strong\u003e into a new sheet. This gives you plain text entries you can sort and organize without affecting formulas\u003c/p\u003e\n\u003ch3\u003eOrganize the Entries into Logical Groups\u003c/h3\u003e\n\u003cp\u003eTo make the audio easy to access in code, I grouped the files into logical lists. Then, when I want a particular file, I can refer to its position in the list.\u003c/p\u003e\n\u003cp\u003eI use six groupings:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eTIMES\u003c/li\u003e\n\u003cli\u003eDAY_NAMES\u003c/li\u003e\n\u003cli\u003eMONTHS\u003c/li\u003e\n\u003cli\u003eDAY_NUMBERS\u003c/li\u003e\n\u003cli\u003eYEARS\u003c/li\u003e\n\u003cli\u003eSPECIALS\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cspan\u003e\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003eI create a column for each category (columns C–H) and drag each filename into the appropriate column.\u003c/p\u003e\n\u003cp\u003eThen I rearrange each column into logical order. For example, the \u003cstrong\u003eMONTHS\u003c/strong\u003e list is ordered January → December, matching Python’s structured time values.\u003c/p\u003e\n\u003cp\u003eSo when I want the WAV file for “January,” I simply call\u003c/p\u003e\n\u003cp style=\"padding-left: 40px;\"\u003e\u003cstrong\u003eMONTHS[0]\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan\u003e(remember CircuitPython is zero based indexing).\u003c/span\u003e\u003c/p\u003e\n\u003ch2\u003eCopy files to your SD card\u003c/h2\u003e\n\u003cp\u003eCreate a folder on your SD card (in this example, \u003cem\u003e\u003cstrong\u003etvoice\u003c/strong\u003e\u003c/em\u003e) and copy all of your WAV files into it. No subfolders are needed — the organizing happens in code.\u003c/p\u003e\n\u003ch1\u003eCreate the File of Lists\u003c/h1\u003e\n\u003cp\u003e\u003c/p\u003e\n\u003cp\u003eNow that the full filenames are neatly sorted, create a \u003ccode\u003e.py\u003c/code\u003e file containing the lists. Although you \u003cem\u003ecan\u003c/em\u003e put everything in \u003ccode\u003ecode.py\u003c/code\u003e, I prefer a separate library file due to the size.\u003c/p\u003e\n\u003cp\u003eCreate a blank Python file named \u003cstrong\u003evoices.py\u003c/strong\u003e in the \u003ccode\u003elib\u003c/code\u003e folder on your Fruit Jam.\u003c/p\u003e\n\u003cp\u003eStart each list by typing the list name (e.g., \u003ccode\u003eTIMES\u003c/code\u003e), then an equals sign and an opening bracket. Copy the ordered filenames from Excel and paste them in. Close the list with a closing bracket.\u003c/p\u003e\n\u003cp\u003eRepeat for each category.\u003c/p\u003e\n\u003cp\u003eHere is what my \u003cstrong\u003evoices.py\u003c/strong\u003e file looks like:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"TIMES = [\n    '/sd/tvoice/the_time_is.wav',\n    '/sd/tvoice/1oclock.wav',\n    '/sd/tvoice/2oclock.wav',\n    '/sd/tvoice/3oclock.wav',\n    '/sd/tvoice/4oclock.wav',\n    '/sd/tvoice/5oclock.wav',\n    '/sd/tvoice/6oclock.wav',\n    '/sd/tvoice/7oclock.wav',\n    '/sd/tvoice/8oclock.wav',\n    '/sd/tvoice/9oclock.wav',\n    '/sd/tvoice/10oclock.wav',\n    '/sd/tvoice/11oclock.wav',\n    '/sd/tvoice/12_oclock.wav',\n    '/sd/tvoice/midnight.wav',\n    '/sd/tvoice/noon.wav',\n    '/sd/tvoice/a_m.wav',\n    '/sd/tvoice/p_m.wav',\n    '/sd/tvoice/currently it is.wav',\n    '/sd/tvoice/currently.wav',\n    '/sd/tvoice/time_to_wake_up.wav',\n    '/sd/tvoice/quarter past.wav',\n    '/sd/tvoice/half_past.wav',\n    '/sd/tvoice/quarter_til.wav',\n    '/sd/tvoice/it is.wav',\n]\n\nDAY_NAMES = [\n    '/sd/tvoice/monday.wav',\n    '/sd/tvoice/tuesday.wav',\n    '/sd/tvoice/wednesday.wav',\n    '/sd/tvoice/thursday.wav',\n    '/sd/tvoice/friday.wav',\n    '/sd/tvoice/saturday.wav',\n    '/sd/tvoice/sunday.wav',\n    '/sd/tvoice/it is.wav',\n]\nMONTHS = [\n    '/sd/tvoice/january.wav',\n    '/sd/tvoice/february.wav',\n    '/sd/tvoice/march.wav',\n    '/sd/tvoice/april.wav',\n    '/sd/tvoice/may.wav',\n    '/sd/tvoice/june.wav',\n    '/sd/tvoice/july.wav',\n    '/sd/tvoice/august.wav',\n    '/sd/tvoice/september.wav',\n    '/sd/tvoice/october.wav',\n    '/sd/tvoice/november.wav',\n    '/sd/tvoice/december.wav',\n]\nDAY_NUMBERS = [\n    '/sd/tvoice/first.wav',\n    '/sd/tvoice/second.wav',\n    '/sd/tvoice/third.wav',\n    '/sd/tvoice/fourth.wav',\n    '/sd/tvoice/fifth.wav',\n    '/sd/tvoice/sixth.wav',\n    '/sd/tvoice/seventh.wav',\n    '/sd/tvoice/eighth.wav',\n    '/sd/tvoice/nineth.wav',\n    '/sd/tvoice/tenth.wav',\n    '/sd/tvoice/eleventh.wav',\n    '/sd/tvoice/twelth.wav',\n    '/sd/tvoice/thirtenth.wav',\n    '/sd/tvoice/fourteenth.wav',\n    '/sd/tvoice/fifteenth.wav',\n    '/sd/tvoice/sixteenth.wav',\n    '/sd/tvoice/seventeenth.wav',\n    '/sd/tvoice/eighteenth.wav',\n    '/sd/tvoice/nineteenth.wav',\n    '/sd/tvoice/tweentith.wav',\n    '/sd/tvoice/twenty_first.wav',\n    '/sd/tvoice/twenty_second.wav',\n    '/sd/tvoice/twenty_third.wav',\n    '/sd/tvoice/twenty_fourth.wav',\n    '/sd/tvoice/twenty_fifth.wav',\n    '/sd/tvoice/twenty_sixth.wav',\n    '/sd/tvoice/twenty_seventh.wav',\n    '/sd/tvoice/twenty_eighth.wav',\n    '/sd/tvoice/twenty_ninth.wav',\n    '/sd/tvoice/thirtieth.wav',\n    '/sd/tvoice/thirty_first.wav',\n]\nYEARS = [\n    '/sd/tvoice/2026.wav',\n    '/sd/tvoice/2027.wav',\n    '/sd/tvoice/2028.wav',\n    '/sd/tvoice/2029.wav',\n    '/sd/tvoice/2030.wav',\n]\nSPECIALS = [\n    '/sd/tvoice/christmas.wav',\n    '/sd/tvoice/happy_birthday.wav',\n    '/sd/tvoice/happy_christmas.wav',\n    '/sd/tvoice/happy_new_year.wav',\n    '/sd/tvoice/its_your_birthday.wav',\n    '/sd/tvoice/merry_christmas.wav',\n\n]","metadata":{"language":"python","linenums":false,"filename":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch1\u003eFinally... we code\u003c/h1\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"alert","content":"\u003cp\u003eNote these are just code snippets specifically for the addition of the talking features to the existing Fruit Jam clock project. This assumes we are using the adafruit_fruitjam library and are setting it up with 'jam = FruitJam()'\u003c/p\u003e","metadata":{"markdown":"Note these are just code snippets specifically for the addition of the talking features to the existing Fruit Jam clock project. This assumes we are using the adafruit_fruitjam library and are setting it up with 'jam = FruitJam()'","alert_type":"info","icon":"info"}},{"element_type":"code","content":"## First we need to import our lists at the top of the file with the rest of the import statements\n\nfrom voices import TIMES, DAY_NAMES, MONTHS, DAY_NUMBERS, YEARS, SPECIALS\n\n## Functions to play the samples from the various lists\n\ndef play_time_wav(nbr):\n\tprint(\"Playing wav file:\", TIMES[nbr])\n\tjam.peripherals.play_file(TIMES[nbr], wait_to_finish=True)\n    \ndef the_time_is():\n\tprint(\"The Time Is\")\n\tjam.peripherals.play_file(TIMES[0], wait_to_finish=True)\n    \ndef play_year_wav(nbr):\n\tprint(\"Playing year file:\", YEARS[nbr])\n\tjam.peripherals.play_file(YEARS[nbr], wait_to_finish=True)\n    \ndef play_day_number_wav(nbr):\n\tprint(\"Playing day number file:\", DAY_NUMBERS[nbr])\n\tjam.peripherals.play_file(DAY_NUMBERS[nbr], wait_to_finish=True)\n    \ndef play_month_wav(nbr):\n\tprint(\"Playing month file:\", MONTHS[nbr])\n\tjam.peripherals.play_file(MONTHS[nbr], wait_to_finish=True)\n    \ndef play_day_name_wav(nbr):\n\tprint(\"Playing day name file:\", DAY_NAMES[nbr])\n\tjam.peripherals.play_file(DAY_NAMES[nbr], wait_to_finish=True)\n    \ndef play_special_wav(nbr):\n  print(\"Playing special file:\", SPECIALS[nbr])\n  jam.peripherals.play_file(SPECIALS[nbr], wait_to_finish=True)\n  \n## Functions to make different announcements:\n\ndef announce_hour():\n\tt = time.localtime()\n\thour = t.tm_hour\n\n    # Play \"the time is\"\n\tplay_time_wav(0)\n\t\n\t# Determine which hour and period to play\n\tif hour == 0:\n\t\t# Midnight\n\t\tplay_time_wav(12)  # 12 o'clock\n\t\tplay_time_wav(13)  # midnight\n\telif hour == 12:\n\t\t# Noon\n\t\tplay_time_wav(12)  # 12 o'clock\n\t\tplay_time_wav(14)  # noon\n\telse:\n\t\t# Regular hour with AM/PM\n\t\thour12 = hour if hour \u003c 13 else hour - 12\n\t\tplay_time_wav(hour12)  # Play hour (1-12)\n\t\t\n\t\tif hour \u003c 12:\n\t\t\tplay_time_wav(15)  # AM\n\t\telse:\n\t\t\tplay_time_wav(16)  # PM\n            \ndef announce_date():\n\tt = time.localtime()\n\t\n\t# Play \"It is\" (DAY_NAMES[7])\n\tplay_day_name_wav(7)\n\t\n\t# Get day of week (0=Monday, 6=Sunday in time.localtime())\n\tday_of_week = t.tm_wday\n\tplay_day_name_wav(day_of_week)\n\t\n\t# Get and play month (0=January in MONTHS, but tm_mon is 1-12)\n\tmonth = t.tm_mon - 1  # Convert from 1-12 to 0-11\n\tplay_month_wav(month)\n\t\n\t# Get and play day of month (1st=0 in DAY_NUMBERS, tm_mday is 1-31)\n\tday = t.tm_mday - 1  # Convert from 1-31 to 0-30\n\tplay_day_number_wav(day)\n\t\n\t# Get and play year (2026=0 in YEARS)\n\tyear = t.tm_year\n\tif 2026 \u003c= year \u003c= 2030:\n\t\tyear_index = year - 2026\n\t\tplay_year_wav(year_index)\n\telse:\n\t\tprint(f\"Year {year} not in YEARS array (2026-2030)\")\n        \n        \ndef quarter_hour():\n  play_time_wav(20)\n  \ndef half_hour():\n  play_time_wav(21)\n  \ndef quarter_til():\n  play_time_wav(22):","metadata":{"language":"python","linenums":false,"filename":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eWith these functions you can add talking features for the different elements already in the code. In the while: loop we are getting the time from the RTC and updating the display, and taking other actions at each quarter hour.\u0026nbsp;\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"## This function is called in the while loop each minute\n\ndef get_formatted_time():\n\t\"\"\"\n\tReturns the current time formatted as:\n\tmmm-dd-yyyy \\n hh:mm am/pm\n\tExample: Sep-15-2025 \\n 10:15 pm\n\t\"\"\"\n\tt = time.localtime()\n\tmonths = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n\tmonth_str = months[t.tm_mon - 1]\n\tday = t.tm_mday\n\tyear = t.tm_year\n\thour = t.tm_hour\n\tminute = t.tm_min\n\tam_pm = \"am\" if hour \u003c 12 else \"pm\"\n\thour12 = hour % 12\n\tif hour12 == 0:\n\t\thour12 = 12\n\tformatted = f\"{hour12}:{minute:02d} {am_pm}\"\n    \n\tif minute == 15: # Detects the quarter hour and runs the function 'qhr()' which handles playing Westminster Quarters and then announces it is the quarter hour\n\t\tprint(\"Quarter Hour\")\n\t\tqhr()\n\telif minute == 30:\n\t\t\tprint(\"Half Hour\") # Same as quarter hour\n\t\t\thhr()\n\telif minute == 45:\n\t\t\tprint(\"Three Quarter Hour\") # Same as quarter hour\n\t\t\ttqhr()\n\telif minute == 0:\n\t\t\tprint(\"Top of the Hour\") # Calls the full hour function to sound the Quarters, ring the bell and announce the time\n\t\t\tfhr()\n\telse:\n\t\tpass\n\treturn formatted\n\n## This function is called when the top of the hour is reached\n\ndef fhr():\n\tprint(\"On the Hour\")\n\t\n\t# Announce the time with voice\n\tannounce_hour()\n\t\n\t# Restart the mixer/synth after voice announcements\n\tjam.peripherals.audio.play(mixer)\n\tmixer.voice[0].play(synth)\n\tmixer.voice[1].play(chorus)\n\t\n\tprint(\"Finished announcing hour, now playing tones.\")\n\tfor i in wc4:\n\t\ttime.sleep(0.01)\n\t\tn = wcn[i][0]\n\t\tt = wcn[i][1]\n\t\tnote = synthio.Note(synthio.midi_to_hz(n), bend=lfo)\n\t\tsynth.press(note)\n\t\ttime.sleep(t)\n\t\tsynth.release(note)\n\t\tif i in (1, 3):\n\t\t\ttime.sleep(0.5)\n\n\tprint(\"Finished playing tones for hour.\")\n\t\n\t# Play the gong for the hour\n\tt = time.localtime()\n\thour = t.tm_hour\n\tgong(hour if hour \u003c 13 else hour - 12)","metadata":{"language":"python","linenums":false,"filename":""}},{"element_type":"text","content":"\n  \n        \u003ch1\u003eIn closing\u003c/h1\u003e\n\u003cp\u003eA little more to do... I haven't yet done anything with the SPECIALS. There are slightly longer pauses between each WAV file than ideal (meaning it does not sound like completely normal speech) but is sounds really good to me. I will continue to play around with this - maybe some tweaking can be done to improve the spacing, but overall, I am really happy with how this turned out.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eAnd of course - totally new and different things to try down the road.\u003c/p\u003e\n      \n","metadata":{}}]