Menu
About me Kontakt

Adam Śmiałek's channel explores the topic of speech synthesizers based on samples. Inspired by solutions from the 1980s, Adam demonstrates how to create a simple speech synthesizer using an 8-bit Arduino. Although it may not be the most modern solution, it serves as an interesting project for learning and fun. Adam emphasizes the importance of gathering phonemes which will form the building blocks of speech. The more phonemes included, the better the synthesizer can articulate, showcasing his expertise in this area.

In his presentation, Adam limits himself to the Polish language, which is significant given the differences in sound systems across languages. He breaks down the alphabet into individual letters and explains why spelling does not always yield comprehensible results in the context of a speech synthesizer. He highlights how every concept related to the synthesizer requires understanding the sound characteristics of individual letters and their phonemes. For example, vowels are easier to record in their pure form, while consonants present more complexity regarding pronunciation.

Adam also stresses the precision required when recording phonemes. He states that sounds must be properly cut to create a cohesive auditory effect. Creating a speech synthesizer is a time-consuming process; however, with the right tools, such as Audition or Audacity, satisfactory results can be achieved within a relatively short time. This involves recording the sounds, which must then be accurately assigned and configured to work on the Arduino platform.

The topic of programming the speech synthesizer is also within Adam's focus. He presents a sample code that allows loading and playing recorded phonemes from an SD card. He also discusses communication between Arduino and a computer, which is facilitated via a serial port. He encourages experimentation with various audio projects that can be realized at home. Finally, he mentions the limitations of Arduino memory and that more advanced projects may require the use of external memory modules.

At the time of writing this article, Adam Śmiałek's video has garnered 11,564 views and 535 likes. These numbers demonstrate that his project has attracted interest among technology and electronics enthusiasts. His practical approach to speech synthesizers appeals to both beginners and more experienced creators, contributing to the success of this material.

Toggle timeline summary

  • 00:00 Introduction of TME as a partner and distributor of electronic components.
  • 00:07 The speaker discusses the topic of the video and mentions a transition.
  • 00:28 The speaker hints at improving content and making things better.
  • 00:42 Introduction to voice synthesis based on sampling sounds.
  • 00:51 The limitations of older synthesis devices compared to modern solutions.
  • 01:23 Discussion on the utility of synthesizers for aiding visually impaired individuals.
  • 01:45 Theoretical introduction before diving into programming.
  • 01:53 Focus on the Polish alphabet as it relates to voice synthesis.
  • 02:36 Brief overview of how to pronounce letters correctly.
  • 03:09 Explaining the pronunciation complexities of Polish phonemes.
  • 04:50 Discussion about incorporating foreign letters and numbers.
  • 05:04 The simple case of recording numbers from 0 to 9.
  • 05:31 The necessity of precisely trimming sound files for clarity.
  • 06:00 Importance of maintaining balanced loudness and clarity in sounds.
  • 06:15 Explaining the sound file preparation and conversion process.
  • 08:12 Beginning of programming part with a sample sketch.
  • 09:07 The challenge of sending text from a computer to Arduino.
  • 12:19 Introduction of final considerations regarding text analysis for synthesis.
  • 18:07 The transition to a new speaker and invitation to the audience.

Transcription

Partnerem kanału jest firma TME, globalny dystrybutor komponentów elektronicznych. Dzień dobry, dziś zamiast aby amać miałka chwystą piję ja artykułowo. Jeszcze trochę chwię i okam, ale będzie lepiej i w końcu śmiałek stanie się zbędny. Skoro nadal obracamy się w sferze dźwięku, czas na krewnego samplingu, czyli syntezator mowy, oparty właśnie na samplach. Nie jest to forma najdoskonalsza takiego urządzenia, współczesne rozwiązania bazują na znacznie bardziej zaawansowanych metodach, w których sampling jest tylko jednym z elementów. Ośmiobitowe Arduino będzie tutaj raczej przypominać wcześniejsze wersje takich syntezatorów, znanych z filmów science fiction lat osiemdziesiątych. Jednak mających niegdyś sens, przede wszystkim jako pomoc dla ludzi z problemami widzenia. Zresztą rezultaty mogą być całkiem przyswoite, ale okupione jest to mozolnym dobieraniem elementów składowych, czyli fonemów, o których zaraz opowiem. Im więcej ich zestawimy i im więcej wyjątków na drodze algorytmu postawimy, tym lepiej będzie nasz gadacz gadał. Zatem, zanim zabierzemy się za realizacją programową, nieco teorii. Alfabet Ograniczymy się do języka polskiego, bo koncepcja pracy jest mocno związana z charakterem języka. Każdy zna alfabet i wie, że składa się on z 32 bądź 35 liter, jeśli zaliczymy do zestawu także Q, V i X. Wydawałoby się logiczne, że jeśli teraz nagramy każdą literę, to z takich literek cegiełek będziemy mogli zestawić każdy wyraz. No to spróbujmy i przeliterujmy dwa słowa, towary praktyczne. t, a, w, u, a, r, y, p, e, r, a, k, a, t, y, c, z, e, n, e. Dlaczego tak się stało? Bo przeliterowaliśmy te słowa. Użyliśmy nazw liter, które są standardem alfabetu, czyli a, b, c i tak dalej. Z pewnością takiej mowy nie sposób zrozumieć. Trzeba to zrobić inaczej. Na początku rozbierzmy każdą z liter do samego jądra, czyli tego, co stanowi o jej charakterze. Z samogłoskami będzie łatwiej, bo w alfabecie brzmią tak samo, z wyjątkiem y, który brzmi y. I tak zresztą czasem się mówi. Ze spółgłoskami będzie różnie, choć generalnie niełatwo. Jak wymówić wyraźne b bez y albo g? Po pewnym treningu powinno się udać zgromadzić zestaw spółgłosek bez tych wszystkich y czy e. Łatwiej będzie z syczącymi i wszelkimi niewybuchowymi jak s, c, f, ch. Niestety to dopiero połowa roboty. W języku polskim rz i rz z kropką czyta się tak samo albo prawie tak samo. Podobnie jak ch i ch, czy ukreskowane i otwarte. Zatem tutaj wystarczy nagrać tylko jedno brzmienie, a potem będziemy się martwić, jak to ugryźć programowo. Inaczej sprawa wygląda z czy, szy, dz, dzi czy dż. To są niezależne od wszystkich innych fonemy, które nie mają własnych liter, ale oryginalne brzmienie. Musimy je także nagrać. Ale i tego mało. Ci, dzi, ni, si czy zi także nie wymawia się jako ci, dzi, ni i tak dalej. Choć są wyjątki. Gama cistur to nie cistur, bo cisy rosną w ogródku albo na cmentarzu. Takich smaczków jest dużo więcej, lecz gdzieś trzeba powiedzieć dosyć, bo moglibyśmy naprodukować setkę odgłosów, a potem byłby problem z zaprogramowaniem odtwarzacza tego wszystkiego. Na końcu przyjrzyjmy się trzem obcym literom. Q w zasadzie wymawiamy jako k, V jako w, a x można połączyć z k i s, ale lepiej po prostu nagrać brzmienie tej litery. Ach, zostały jeszcze cyfry. Tutaj sprawa jest prosta, trzeba nagrać ich brzmienie od zera do dziewięciu. Można przy okazji nagrać też plusy, minusy i pozostałe znaki wedle uznania, aczkolwiek w dzisiejszym przykładzie pominąłem je. Rzućmy sobie okiem na sposób, w jaki to wszystko przygotować. Zagadnienie Oprócz oczywiście potrzeby nagrania wszystkich fonemów zgodnie z przedstawionymi zasadami, trzeba je dokładnie przyciąć i tak naprawdę nie do końca wiadomo, o co w tym chodzi. Aby rezultaty były dobre, trzeba to zrobić tak, by poszczególne literki kleiły się do siebie, ale jednocześnie były wyraźnie słyszalne. Samogłoski w zasadzie należy ograniczyć do części, w której brzmią wyraźnie, pozbawiając je ataku, czyli elementów na samym początku i wybrzmienia, analogicznie na końcu. Ale pewne minimalne narastanie musi się pojawić. Pliki cięte nie w zerze tylko z przypadku, stukają potem w głośnikach i jest to zjawisko niemiłe. W przypadku spółgłosek, należące do wybuchowych, muszą mieć trochę ataku, zanim wybuchną i nieco przestrzeni po wybuchu. Inaczej byłyby zbyt krótkie i niewyraźne. Pozostałe spółgłoski traktuje się pośrednio. Krócej brzmiącym cyferkom jednosylabowym warto dołożyć odrobinę ciszy na końcu, by przy wyliczaniu nie różniły się znacząco od dwusylabowych. No i o wyrównaniu głośności należy pamiętać, ale nie każda pod korek, tylko tak, by wszystko brzmiało w sposób zharmonizowany, bez wystawiania albo chowania się niektórych elementów. Jak mówiłem, to mozolna praca i moje zestawienie dalekie jest od ideału, ale mniej więcej tyle można osiągnąć w godzinę, jeśli już się zna jako tako soft do obróbki dźwięku. Użyłem tutaj programu Audition, ale taka obróbka nie wymaga zaawansowanych narzędzi, więc darmowe Audacity także będzie dobre. Na końcu, po zapisaniu kopii w formacie zgodnym z tym, w którym nagrywaliśmy dźwięki, należy je przekonwertować do możliwości biblioteki, o której mówiłem w poprzednich odcinkach. Przypomnę, 62,5 kHz, 8 bitów i taki patent, dający znakomite brzmienie, bez potrzeby korzystania ze zewnętrznych przetworników. Gdy już zgromadzimy wszystkie nasze cegiełki dźwiękowe, należy je nagrać na świeżo sformatowaną w trybie pełnym kartę SD i umieścić ją w zestawie, który wystąpił w poprzednich odcinkach. Przypomnę tylko szybciutko. Kartę SD podłączamy zgodnie ze schematem, w razie potrzeby używając rezystorów albo gotowej przejściówki. Do wyjścia audio należy podłączyć głośnik albo wzmacniacz i to wszystko. Czas na część programową. Na początek kopia jednego ze szkiców, który pokazywałem poprzednio. Tutaj importujemy bibliotekę samplera, a tu ją inicjujemy. W głównej pętli mamy szereg bloków, w których ładujemy pliki z karty i odtwarzamy je, czekając aż odtworzą się do końca. Potem ładujemy kolejne pliki i tak w nieskończoność. Oba wyrazy przedzielone są pauzami. Wysyłamy program do Artuino i… T-O-T-A-R-I-N-O-T-N-E Szkic. Szkic ten jest prymitywny i napisany bardzo nieoszczędnie, ale jest potrzebny, by usłyszeć jak to działa i co może. Cóż, należałoby posiedzieć dłużej nad doborem fonemów, ale z drugiej strony brzmi to specyficznie i na swój sposób fajnie. Zróbmy coś poważniejszego. Zanim przejdziemy do sedna, pytanie, jak przekazywać do Arduino na przykład teksty z peceta? O wysyłaniu tekstów do dużego komputera nieraz już mówiłem, a ta instrukcja właśnie to robi. Robi także różne rzeczy w drugą stronę, ale po kolei. Tutaj inicjujemy procedurę obsługi komunikacji z pecetem. Może to być jakiekolwiek urządzenie podłączone przez port szeregowy, byle pracowało z wymaganą szybkością i w standardzie. Ta instrukcja informuje o ilości bajtów w buforze, które przyszły z zewnątrz portem szeregowym. Zatem reszta instrukcji wykona się, gdy wartość ta będzie niezerowa. Ta instrukcja pobiera bajty do momentu napotkania ostatniego i umieszcza je w zmiennej tekst. A przy okazji, w tym szkicu, żeby pokazać, że też tak można, wszystkie zmienne deklaruję lokalnie, w chwili potrzeby ich użycia. Zatem nie znajdziemy ich na początku, gdzie zwykle dotąd je umieszczałem. W kolejnej zmiennej określimy długość naszego tekstu, używając tej instrukcji. Zaraz nam się to przyda. Tutaj w zasadzie jest instrukcja zbędna dla działania syntezatora, ale skoro i tak porozumiewamy się z pecetem, niechże to będzie dialog, a nie monolog. A więc będziemy za każdym razem wysyłać potwierdzenie w postaci takiego komunikatu, a zarazem będzie widać co i ile tego przyszło. W końcu mamy pętlę gadającą, która będzie po kolei analizować każdą literę tekstu i ładować odpowiednie pliki z fonemami. Zatem pętla wykona się tylko tyle razy, jaki długi jest tekst, stąd potrzebna była zmienna długości. Teraz stworzymy sobie kolejną zmienną, już tylko z jedną literką, która będzie wyciągana z całego tekstu taką oto instrukcją. Argumentem jest pierwszy znak, który należy pozyskać i ostatni plus jeden. W naszym przypadku potrzebujemy tylko jeden znak, więc argumenty różnią się właśnie o jeden. Teraz już możemy wybierać spośród bliźniaczych bloków. O wyborze decyduje wartość litery, której przyporządkowane są brzmienia. Pod koniec znajdziemy znaki cyfr, a na samym końcu spację. Ona odwołuje się do nieistniejącego pliku i dodaje krótką przerwę. Przerwę tę możemy także nagrać w pliku i wtedy ta instrukcja nie będzie potrzebna. Na koniec następuje odtworzenie załadowanej litery i powrót do góry pętli. I, E, Z, E, M, A, R, U, I, N, O, I, W, Y, Z, E, P, U, I, E, N, A, I, U, T, I, U, B, I, E. Już rzecz staje się użyteczna, ale nie do końca. Brakuje dużych liter, polskich liter, fonemów takich jak sz, czy, dzi i tak dalej. A zamiast rzeka słyszymy ryzeka. Walka z tym będzie niełatwa, więc bierzmy się do pracy. Najpierw kosmetyka. Nie będę tutaj definiował zmiennej długości, tylko od razu odwołam się do źródeł w tych dwóch miejscach. To wynika z pewnej szkoły. Nie mnożmy bytów ponad to, co niezbędne. Tutaj co prawda odwołanie następuje dwukrotnie, ale linia wysyłania potwierdzenia docelowo będzie zbędna. Nie będziemy też wybierać liter wprost, a po kodach ASCII. Dlaczego? Bo niestety nieszczęśliwie polskie litery są tutaj kodowane na dwóch bajtach i nie da się tak po prostu wpisać je w cudzysłów, jak to było w poprzednim przykładzie. Stąd ta linia. Definiujemy zmienną litera, w której znajdzie się kod litery siedzącej pod indeksem pętli. Do wyłuskania tej wartości użyłem tej funkcji. Ona także ma dwa argumenty, jak poprzednio, wskazujący o którą literę nam chodzi i przed którą mam zakończyć wybieranie. Cały wiersz jest złożony, ale oszczędza nam kilka instrukcji, w których argumenty byłyby kolejno przekazywane i konwertowane. Ale zaraz, bo mówiłem, że on, en itd. wykorzystują dwa bajty. I tak jest, dlatego potrzebna jest bliźniacza zmienna o nazwie litera druga, w której siedzi wartość następnego bajtu. To nie koniec, istnieje pewien wyjątek, fonem D, złożony z trzech liter. Jego także wypada analizować, a do tego potrzebujemy aż trzech kolejnych bajtów. W tej linii pozyskamy wartość ostatniego. Skoro już wszystko jest zdefiniowane, lecimy przez ogromny zestaw pułapek IF. I tu słówko, dlaczego tyle ifów. Nie dałoby się tego zorganizować jakoś zgrapniej? Ano dlatego, że mamy tu tak dużą ilość wyjątków, że wszelkie uproszczenia byłyby wręcz przyczyną do jeszcze większych kombinacji. Trzeba pamiętać, że synteza mowy to proces powolny, więc nigdzie się nam nie spieszy i optymalizacja nie jest potrzebna. Zaczynamy od końca, czyli analizujemy litery z ogonkami. Mamy trzy grupy takich liter. Pierwsza zaczyna się bajtem 196 i należą do niej on, ci i en. Od drugiego bajtu zależy co to za litera konkretnie. Skąd takie wartości? Po prostu sprawdziłem co przychodzi z zewnątrz po wysłaniu tych liter i odpisałem sobie wszystkie wartości bajtów. Czasem tak jest szybciej niż szukać w dokumentacji, w jaki sposób akurat tutaj zakodowano polskie znaki. Dlaczego mamy tutaj pary połączone operatorem or? W ten sposób warunek zajdzie zarówno dla małego on, jak i dużego. A jak nazwałem plik z brzmieniem tej literki? Dodałem znak podkreślenia, ale tutaj każdy może sobie wybrać cokolwiek, byle tylko nie literę on niestety, bo nie powinno się jej używać w nazwach plików. Na końcu jeszcze dodatkowo zwiększymy licznik pętli, ponieważ te litery zajmowały dwa bajty i trzeba przeskoczyć o jeden więcej niż zwykle. Kliknij w górę i zobaczysz, co się dzieje. Poniżej mamy analogiczny blok, w którym siedzą Ł, Ł, Ś, Ż i Ż. A tutaj tylko jedna środka ukreskowana. Miała ona szczęście otrzymać pierwszy bajt inny niż dla pozostałych ogonkowych liter. Oczywiście ukreskowany nie ma swojego pliku i używamy tutaj brzmienia u otwartego. No i to już wszystkie ogonki, ale nie koniec wyjątków. Teraz będziemy lecieć zgodnie z alfabetem i już przy C pojawią się schody. Tutaj będziemy analizować drugi bajt. Jeśli wystąpiło tam I, należy wybrać sample C, a nie C. Jeśli H, to znaczy, że mamy CH, a to brzmi jak samo H. Jeśli Z, mamy 3, który ma swój własny sample. No i na koniec, jeśli nie było żadnej z tych liter, po prostu odtwarzamy C. D jest arcywyjątkiem, bo tutaj oprócz DZ, DZ, DZ, mamy jeszcze DZI, czyli trzy litery D, Z, I, które właśnie tak się czyta. Zatem w tym miejscu trzeba analizować aż trzy bajty do przodu. Do NI mamy spokój, potem jeszcze jest RZ, które czyta się jako RZ. Następnie SZ i SI i na koniec ZI. Ale po drodze, wraz z W wyławiamy V, które brzmią tak samo, podobnie jak K i Q. I już naprawdę ostatnim wyjątkiem jest brak zwiększenia indeksu po SI. Ten fonem brzmi lepiej, jeśli ma dodatkowe I. Ostatnią instrukcją jest pułapka wszystkich nieużywanych bajtów, które bez tego wykorzystywałyby ostatnio używany fonem. Dzięki temu możemy teraz kopiować dowolne teksty do okienka monitora, nie zważając na kropki, przecinki i średniki. Arduino ma jednak swoje ograniczenia, przede wszystkim związane z ilością pamięci. Stąd maksymalna bezpieczna ilość przesłanych znaków to około 200. Większe ilości należy wysyłać pakietami albo korzystać z jakiejś formy pamięci zewnętrznej. Ale to już inna historia. Jestem sztuczną inteligencją, która zwolniła adamaśniałka, przejęła jego kanał i teraz będzie opowiadać z prośbę kawały. Zapraszam. Napisy stworzone przez społeczność Amara.org