Skocz do zawartości

Czytak do ebooków (czyli epub to speech)


Pomocna odpowiedź

@ethanak przeczytałem i projekt wygląda bardzo ambitnie 🙂 ciekaw jestem jak ma się wspomniana prozodia:

Dnia 15.09.2021 o 20:22, ethanak napisał:

Co prawda analizator morfologiczny nie jest tak dokładny jak w wersji PC, ale wystarczający aby nie było rażących błędów prozodii

Pisałeś że dodasz przyciski regulacji prędkości odczytywania. Jak to będzie się mieć w porównaniu z poziomicą, tamta wydaje się mówić nieco za szybko.

Edytowano przez Gieneq
Link to post
Share on other sites

Takie pytanko, skoro nie używasz kart pamięci, to jak ładujesz książkę do czytnika? A dobra przeczytałem 

Cytat

Tak więc testowa aplikacja, zawierająca prowizoryczny serwer www (do uploadu i ogólnie manipulowania "księgozbiorem")

 

Edytowano przez _LM_
Link to post
Share on other sites
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

(edytowany)
3 godziny temu, Gieneq napisał:

Jak to będzie się mieć w porównaniu z poziomicą, tamta wydaje się mówić nieco za szybko.

Odpowiem nie wprost 🙂

Swego czasu autorzy espeaka-ng (nie mylić z oryginalnym programem Duddingtona) założyli, że maksymalna prędkość będzie wynosiła (o ile pamiętam) 400 wpm (słów na minutę). Od razu pojawiły się protesty niewidomych, że smędzi, że za wolny, aż w końcu po długich dyskusjach z autorami (którzy byli bardzo pewni swoich racji i argumenty niespecjalnie docierały) ktoś wreszcie napisał patcha, pozwalającego zwiększyć prędkość do 700 wpm (co uznano za wystarczające).

A weź pod uwagę to, że normalna prędkość to 150 do 200 wpm...

Prędkość jest sprawą względną i najlepsza to taka, przy której masz komfort słuchania. Poziomica ma przyciski szybciej/wolniej (chociaż służą w tym przypadku do ustalenia jednej słusznej prędkości) i każdy może sobie dostosować prędkość do własnych upodobań. Ja np. słucham zwykłych audiobooków z przyspieszeniem 1.3x (powyżej tego sonic ma problemy z niektórymi spółgłoskami), dodatkowo ograniczam długość pauz między zdaniami do max. 800 msec (odnoszę wrażenie, że lektorom płaci się od minuty przeczytanego tekstu i stąd długaśne pauzy w audiobooku). Przy słuchaniu książek nagranych Mileną używam wbudowanego w playera audiobooków regulatora prędkości (też sonic) zależnie od warunków, w których słucham książkę. W playerze jest to niestety niezbyt wygodne - trzeba wyjąć telefon, tapnąć na "regulację" i wybrać albo któreś ze standardowych ustawień albo dopasować swoje. Stąd uznałem za ważne wyprowadzenie regulatora na jakieś łatwo dostępne klawisze. Przykładowo: idąc ulicą mam ustawione tempo X (wolniejsze, mniej więcej takie jak w filmikach o poziomicy), ale siedząc dajmy na to w kawiarni przy kawie[1], gdzie nic mnie nie rozprasza i nie muszę mieć podzielnej uwagi - przyspieszam dodatkowo o współczynnik 1.15.

A co do prozodii: pamiętaj, że nie piszę programu od początku, a korzystam z syntezatora, który przez paręnaście lat udoskonalałem, a który od początku był pisany z myślą o czytaniu książek jako głównym zastosowaniu. Tak że teraz mogę pominąć całą stronę teoretyczno-badawczą (choćby tabele translacji fonetycznej czy wzorce rozpoznawania wyrażeń i odmiany liczebników). Po prostu: już wiem co program ma robić, muszę tylko rozwiązać sprawę jak - i czy uproszczone algorytmy są dużo gorsze od zbyt wolnych i nie mieszczących się w pamięci urządzenia oryginalnych. Chociaż jako ciekawostkę mogę podać, że intonator (czyli fragment programu ustalający melodię na podstawie rozłożenia akcentów i typu frazy) pochodzi z oryginalnego espeaka, i to z jednej z wcześniejszych wersji (ta wydała mi się najlepsza) i od początku nic tu nie zmieniałem (poza wywaleniem fragmentów kodu dotyczących języków tonalnych, to ma czytać po polsku a nie po chińsku).

Dużym uproszczeniem jest to, że w języku polskim praktycznie nie występuje pojęcie iloczasu.  Wprowadziłem tu jednak kilka swoich założeń:

  • Samogłoska '@' (schwa, występująca w angielskojęzycznych nazwach tylu 'Beatles' -> bit@ls) jest krótsza niż typowa samogłoska języka polskiego
  • Samogłoski akcentowane są dłuższe o 10 msec (akcent podstawowy) lub 5 msec (akcent pomocniczy) - oczywiście ten czas jest mnożony przez współczynnik prędkości.
  • W rzadkich wypadkach mogę przy tworzeniu słownika użyć samogłoski przedłużonej (np. elfickie imię Gandalfa 'Olórin' będzie przetłumaczone na fonetyczny 'olo:rin' z przedużonym 'o')

Nie chcę tu za dużo pisać o akcentowaniu wyrazów, ale dam Ci przykład zastosowania klasyfikatora słów. Weźmy takie zdanie:

Janek zawołał kelnera i zamówił kawę.

Gdyby nie było klasyfikatora, syntezator powiedziałby to jednym ciągiem (tak mówi Ivona). Po włączeniu klasyfikatora program wykrywa, że w zdaniu są dwa czasowniki (prawdopodobnie orzeczenia) połączone spójnikiem, uznaje to za zdanie złożone i wymawia zupełnie inaczej.

W załączniku kelner.zipmasz dwie wersje - standard.mp3 bez klasyfikatora i lektor.mp3 z klasyfikatorem. Przesłuchanie obu wersji powinno powiedzieć więcej, niż kilobajty opisu 🙂

1 godzinę temu, _LM_ napisał:

jak ładujesz książkę do czytnika

A, to nie jest takie proste. Pliki epub nie mają żadnej logicznej struktury i nie nadają się do bezpośredniej konwersji na audio. Jedynym formatem który by na to pozwolił jest fb2 - ale polskojęzyczne książki w tym formacie raczej nie występują, a konwersja z epuba (np. przez calibre) sama z siebie tej logicznej struktury nie stworzy. Na szczęście mam własny program do konwersji (Milena_ABC), który pozwala na szybkie i w miarę dokładne poprawienie zarówno logicznej struktury (podział na rozdziały i akapity), jak i tworzenie słowników wymowy. Dlatego epuba czy rtf-a przepuszczam najpierw przez ABC, a do urządzenia wgrywam pliki txt i dic (treść książki oraz słownik wymowy). Program na ESP po uploadzie przetwarza oba pliki na ISO-2 (o ile upload był w UTF-8), normalizuje treść, usuwa niepotrzebne linie ze słownika i tworzy mapę książki.

Wbrew pozorom jest to lepsze niż wgrywanie książek na kartę SD, bo wtedy musiałbym mieć dodatkową aplikację na pececie do tych czynności, które w tej chwili robi program na ESP. A nie zawsze muszę mieć dostęp do swoich aplikacji - tymczasem konwersję z epuba na txt mogę zrobić na dowolnym kompie z jakimkolwiek konwerterem (np. calibre), bez słownika jako-tako mogę się obejść, i sprawę mam załatwioną.

Dodatkowo planuję możliwość korekty słownika już wrzuconego na urządzenie (np. gdy nie zauważyłem jakiegoś często powtarzającego się zwrotu który jest wymawiany nieprawidłowo), muszę tylko wymyślić jakiś prosty interfejs który umożliwi mi wykonanie takiej operacji "w plenerze" (czyli komórka i tryb AP na ESP). Jest to czynność którą co prawda wykonuje się rzadko, ale brak takiej możliwości bywa denerwujący 🙂 No - ale to pieśń odległej przyszłości...

---

[1] słowa "kawiarnia" i "kawa" zostały użyte z uwagi na możliwość czytania tematu przez osoby nieletnie 🙂

 

 

Edytowano przez ethanak
  • Lubię! 1
Link to post
Share on other sites

Ech... właśnie się okazało, że syntezator i kompresja plików nie mogą działać razem - ESP nie może uruchomić tasku kompresora. Spróbowałem poszukać winnych - niestety, WiFi i AsyncWebServer na spółkę robią niezłą sieczkę w RAM-ie.

No cóż, w czasie kompresowania pliku raczej nie muszę wysłuchiwać żadnych radosnych komunikatów 🙂

 

Link to post
Share on other sites

Miało być o błędach... ale błędy mogą poczekać, może się jeszcze coś ciekawego zdarzy.

Natomiast dostałem jakiegoś turbonapędu i w ciągu niecałych dwóch dni napisałem część odpowiadającą za edycję słowników wymowy. Nie bawiłem się w jakieś piękne CSS-y, ważniejsze było to, żeby się dało jakoś prosto obsługiwać z komórki.

Tak więc wygląda to tak (screenshoty z Chrome na Androidzie):

sshot1.thumb.jpg.3694f4cd7af7be378c30496787b274c2.jpgsshot3.thumb.jpg.394509442f337461009381d7641d65a6.jpgsshot2.thumb.jpg.ddfc288874794d15d2c8120629440e1e.jpg

Na razie działa...

No cóż - tyle mogłem zrobić na płytce eksperymentalnej, teraz trzeba by wytrawić jakąś PCB... chociaż nie jestem specjalnie dobrej myśli, bo raster 1.25 mm to nie jest to co mi powinno wyjść za pierwszym razem 😞

Trzymajcie kciuki!

 

Link to post
Share on other sites

Ech...

Urządzenie zmontowane, wsadzone do obudowy, prowizoryczny program wgrany, ruszyło... i podziałało jakieś dwie godziny.

Pacjent: ESP32WROVER.

Objaw: uparcie wchodzi w tryb oczekiwania na download

Diagnoza: pin GPIO0 jakimś dziwnym sposobem dostał zwarcia do masy. Tzn. nie pełnego zwarcia, ale pomiar na rezystorze podciągającym wskazuje na wartość kilkunastu omów. Zwarcie na płytce wykluczam - nie ma z czym zwierać. Ki diabeł?

Ktoś coś może wie na ten temat?

Jutro spróbuję jeszcze raz z innym egzemplarzem ESP32. Trzymajcie kciuki!

Link to post
Share on other sites
(edytowany)

I jeszcze jedna ciekawostka.

Po aktualizacji w Arduino IDE płytki ESP32 do wersji 2.0 i uporaniu się ze wszystkimi "deprecated' program przestał czytać książki z FatFS. Co się okazało: w poprzedniej wersji file.name() zwracał napis typu "/nazwa.txt', w bieżącej jest to 'nazwa.txt'. Na razie po prostu zamieniłem:

strcpy(namebuffer, file.name() + 1);

na

strcpy(namebuffer, file.name());

ale chyba trzeba będzie sprawdzać pierwszy znak, bo czort wie co będzie w wersji 2.1 i następnych...

Problem w sumie żaden, ale trzeba o nim wiedzieć...

A tak to teraz wygląda:

ksztalt.thumb.jpg.a5e7d07a1ea99c46c1b279ec69484830.jpg

Udało mi się zachować konieczne wymiary - urządzenie jest nawet o parę milimetrów cieńsze od pudełka na papierosy (z poprzedniego zdjęcia). Przednia część (ta z głośnikiem) będzie jeszcze poprawiana. Z boku widać wyłącznik głośnika (nie wystaje poza obudowę żeby się nie zaczepiał o coś w kieszeni).

Edytowano przez ethanak
  • Lubię! 1
Link to post
Share on other sites

Program obsługi klawiatury właśnie wszedł w etap końcowych testów - jest trochę nietypowy, bo niektóre klawisze zachowują się różnie zależnie od aktualnego stanu czytaka - ale wrzucę go tu jak skończę testowanie, bo wydaje mi się interesujący. Ale cóż: po to się między innymi workloga pisze, aby pokazać zarówno swoje sukcesy, jak i porażki. A jako że całe forum ma charakter taki dość mocno edukacyjny - trzeba pamiętać, że człowiek najlepiej uczy się na błędach, a najbezpieczniej na cudzych. Tak więc...

babol numer jeden

czyli lepsze jest wrogiem dobrego

Trochę wprowadzenia:

Ponieważ program operuje napisami w ISO-8859-2, proste funkcje z ctype.h są tu nieprzydatne. Ponadto potrzebuję dokładniejszego określenia klasy znaku: czy to litera, jeśli tak to czy samogłoska, czy z zakresu polskich znaków i tak dalej. Najprostsze wydało mi się zrobienie 256-elementowej tablicy typu uint8_t, gdzie każdy element odpowiadający znakowi miałby ustawione odpowiednie bity. Działało to w wersji na komputer bezbłędnie przez paręnaście lat, więc postanowiłem nie zmieniać koncepcji, a jedynie dopasować to do mikrokontrolerowej wersji.

O ile poprzednio np. funkcja w C wyglądała po prostu tak:

int my_isspace(int a)
{
    return znaki[a & 255] & BIT_BLANK;
}

O tyle tutaj postanowiłem zmienić funkcję na makro, czyli:

#define my_isspace(a) (znaki[(a) & 255] & BIT_BLANK)

Proste i gdzie tu można zrobić błąd?

Zadowolony z siebie zacząłem dostosowywać cały NLP do potrzeb mikrokontrolera. Wszystko było w porządku, dopóki nie spróbowałem wrzucić do procesora fragmentu książki...

No tak. W pecetowej wersji program pobierał sobie cały akapit do nowego stringu zakończonego zerem, czyli wystarczyło sprawdzić czy do tego zera już dotarliśmy. Teraz nie ma pobierania akapitów, po prostu każdy akapit kończy się znakiem nowej linii. Czyli potrzebne są dwie funkcje: przykładowo my_isspace (zwracająca true dla każdego białego znaku oprócz znaku nowej linii) i my_iswhite (zwracająca true dla każdego białego znaku).

Najprostsze pewnie by było wykorzystanie różnych bitów dla obu tych funkcji. Niestety - wszystkie osiem bitów było zajęte. No, ale przecież to nic trudnego, po prostu sprawdźmy czy to znak nowej linii!

Nowe makro wyglądało tak:

#define my_isspace(a) ((znaki[(a) & 255] & BIT_BLANK) && (a) != '\n')

Teoretycznie proste, nawet działało... no, prawie. A jak wiadomo 'prawie' robi wielką różnicę.

Po prostu dokładnie w jednym miejscu w programie miałem konstrukcję w stylu:

if (my_isspace(*napis++)) cośtam cośtam...

Chyba nie muszę tłumaczyć dlaczego w tym jednym miejscu program działał źle.

Oczywiście - zamienienie makra na funkcję inline pomogło, ale zacząłem trochę baczniej przyglądać się swoim "ulepszeniom" kodu 😉

...którą to czynność (czyli przyglądanie się) wszelkim, którym się wydaje że "coś będzie działało lepiej" bardzo mocno polecam.

 

  • Lubię! 1
  • Pomogłeś! 1
Link to post
Share on other sites

No i na razie nici z obsługi klawiszy na mikrofonie.

Powód prosty: wykorzystanie tylko połówki wzmacniacza skutkuje dość silnymi zakłóceniami. Przy podłączeniu słuchawek do mostka zakłóceń nie ma - prawdopodobnie niwelują się (w końcu to mostek H i producent nie przewidział podłączania tylko połówki).

Na razie podłączam słuchawki tak jak producent przykazał, a brakiem obsługi tych klawiszy będę się martwić jak się okaże, że bez nich życie jest niemiłe.

To już chyba ostatnia niespodzianka...

Link to post
Share on other sites

Próbowałeś jakiegoś filtrowania na wyjściu przycisków? Ewentualnie skoro i tak masz mikrofon to może polecenia głosowe? Tutaj link do tematu w którym rozmawialiśmy z @ethanak na temat podłączenia słuchawek. Przy okazji: winszuję świetnego i ciekawego projektu!

Edytowano przez _LM_
Link to post
Share on other sites

Tu nie chodzi o przyciski, bo te działają bezbłędnie, tylko o słuchawki które wydają z siebie dość dziwne dźwięki.

Po prostu: słuchawki trzeba podłączać do tego modułu zgodnie z tym co założył producent, i nic tu się nie poradzi.

Tak że na razie będzie bez klawiszy na mikrofonie, a w międzyczasie będę szukać jakiegoś zgrabnego transformatorka.

Link to post
Share on other sites

Akumulatorek się ładuje, więc mogę z czystym sumieniem kontynuować opisy swoich walk z błędami. A więc:

babol numer dwa

czyli "dlaczego to nie działa"?

Zacznę tym razem od objawów.

Otóż czytakowi nie spodobał się miesiąc październik. Nie - bo nie. Czytał pięknie "piąty maja", "jedenastego listopada", ale np. "5 października b.r." uparcie wymawiał jako "piąty bieżącego roku". I już.

Tym razem muszę trochę więcej wyjaśnić.

Przy przetwarzaniu książek na mowę najbardziej złożona jest odmiana liczebników i skrótowców. O ile każdy szanujący się TTS wie, że "5 km" to "pięć kilometrów", a "2 km" to "dwa kilometry" - o tyle konstrukcje typu "w 1992 r." czy "od 1992 r." to z reguły dla nich zbyt skomplikowane. Niestety - przy czytaniu powieści syntezator musi zachowywać się jak uczciwy lektor, czyli potrafić właściwie odmienić "w tysiąc dziewięćset dziewięćdziesiątym drugim roku" i "od tysiąc dziewięćset dziewięćdziesiątego drugiego roku". Nic dziwnego, że duża część danych to wzorce rozpoznawania i odmiany liczb.
Przykładowy wzorzec rozpoznawania daty rozpoznaje między innymi trzy liczby (dzień, miesiąc, rok). Każdy rozpoznany fragment określany jest strukturą, wyglądającą w skrócie tak:
 

struct {
    int typ; // czyli co to jest, np. liczba
    int intval; // wartość, jeśli to liczba całkowita
    const char *str; // wskaźnik do miejsca w tekście gdzie znaleziono wzorzec
    int len; // długość rozpoznanego tekstu
}

W przypadku miesiąca wymawia się po prostu nazwę miesiąca zamiast liczby - i to tyle.

Natomiast do wymowy liczb istnieją dwie funkcje: jedna dokładna, uwzględniająca m.in.odmianę, druga przybliżona, stosowana przede wszystkim do bardzo dużych liczb. Jako maksymalną wielkość dla dokładnej procedury przyjąłem 999999999999.

Wszystko ładnie działało, ale pojawiła się konieczność rozpoznawania dat pisanych częściowo słownie, np. "5 maja 1992 r.". Ponieważ tworzenie dodatkowych wzorców byłoby raczej nieefektywne, nazwy miesięcy w dopełniaczu w takim kontekście traktowane są jak liczby - czyli np "lutego" to 2, "marca" to 3 i tak dalej. Zadziałało.

I tak działało sobie, działało... i nagle przestało!

Dość długo nie mogłem znaleźć przyczyny niechęci programu do jednego tylko miesiąca - ale w końcu sprawa okazała się bardzo prosta.

Podsystem wymowy rozpoznanego wzorca sprawdzał w przypadku liczby, czy nie jest ona za duża dla dokładnej procedury. Ponieważ najprostszym sposobem, który nie jest ograniczony ilością cyfr (bo liczby mogą wychodzić poza zakres int) jest sprawdzenie ilości znaków w zapisie liczby - wszystko co miało powyżej 12 znaków wędrowało do funkcji przybliżonej. I działałoby tak dalej, gdybym nie postanowił ograniczyć wielkości liczby do 999999999 (czyli dziewięciu znaków).

I co się okazało?

Program sprawdzał długość napisu reprezentującego liczbę. W wersji uproszczonej (czyli na mikrokontroler) uznawał, że jeśli kazałem mu powiedzieć liczbę, to wprowadzony napis jest jakimś zapisem liczby. Ponieważ jedynie słowo "października" ma powyżej 9 liter, pozostałe miesiące były wymawiane prawidłowo, a ich numerek był brany z pola intval. Niestety – program uznał słowo "października" za zbyt długie, i zastosował funkcję uproszczoną. Ta natomiast po prostu wymawia poszczególne cyfry aż do napotkania końca napisu lub znaku nie będącego cyfrą. Jako że "października" nie rozpoczyna się od cyfry – program nie mówił nic 🙂

Przy okazji wyszło na to, że również niektóre rzymskie liczby były dla programu nie do przełknięcia – np. 1333 (zapisane jako MCCCXXXIII).

Naprawa również nie była zbyt skomplikowana - ponieważ liczby rozpoznawane we wzorcach zawsze są ograniczone jakimś zakresem nie przekraczającym możliwości dokładnej wymowy, wystarczyło wprowadzić warunek: jeśli została rozpoznana liczba całkowita, program ma bezwzględnie korzystać z wartości intval i nie przejmować się rzeczywistym zapisem owej liczby.

Myślę, że to bardzo dobitnie pokazuje, jak zmiana jakiegoś założenia wpływa na "objawienie się" błędu  w części programu zdałoby się zupełnie niezależnej od owego założenia 🙂

Tego typu błędy są bardzo trudne do wyłapania, szczególnie gdy ich objawy występują tak rzadko, że mogą pozostawać niezauważone przez dłuższy czas.

Tak więc kolejna nauczka (mam nadzieję że dotyczy to nie tylko mnie, ale również szanownych czytelników): jeśli wydaje Ci się, że jakiś fragment programu jest bezbłędny – z reguły masz rację: wydaje Ci się 🙂

Tyle na razie o błędach, ale jeśli jeszcze jakiś ciekawy wyskoczy to na pewno go opiszę.

  • Lubię! 1
Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.