Advertisements

Sound processing X – Tymczasowe problemy

Zapowiedzianego w ostatnim poście pierwszego próbnego gameplayu nie będzie:

Pomimu kilku prób, mój ThinkPad nie potrafił wydusić z siebie wystarczająco głośnego dźwięku, aby drugi komputer mógł go z powodzeniem odczytać, kończyło się tak, że transmisja była dobra, pomijając w kilku miejsach źle przesłaną cyfrę, co jeśli chodzi o znaki ASCII kończyło się wstawieniem np. znaku pi zamiast dwukropka i w efekcie złym sparsowaniem jsona (modem jest na tyle dobry, że praktycznie zawsze nadawca podsłuchując to co wysyła potrafi to z powrotem sparsować, więc kwestią zostaje głośność).

Prawdopodobnie mógłbym to rozwiązać mikrofonami kierunkowymi, chociaż pozostałby niesmak, że to już nie jest tym, czym było w zamierzeniach (transmisja z urządzenia na urządzenie, bez internetu i specjalnych przyrządów).

Tak czy inaczej, projekt będzie kontynuowany, mam zamiar zakupić pierwszy lepszy kierunkowy mikrofon x2 i nagrać jakiś film, a na ten moment szukam innego wyjścia, w końcu ktoś już napisał podobne aplikacje:

Możliwe, że to kwestia zejścia poziom niżej i napisania własnego wykrywania częstotliwości na podstawie DFT, co nie byłoby takie złe.

W weekend powinien pojawić się nowy post, bo nie sądzę, żebym trafił na ścianę nie do przejścia.


dsp2017-1

Różne V – Sphinx i rozpoznawanie mowy

Jak już wspominałem przy okazji posta z serii Pokaż kod, pisząc Speechlist, na początku byłem nastawiony na rozwiązywanie testów głosem. Częściowo mi się to udało, chociaż przeniesienie tego na telefon nie było łatwe, żeby nie powiedzieć – sprawiało kłopoty, dlatego finalnie je porzuciłem na rzecz zwykłego wstawiania tekstu dotykiem. Zostałem z częściowo już napisanym frontendem pod rozpoznawanie głosu, możecie go zobaczyć w działaniu tutaj:

Kod Speechlist udostępniłem za darmo pod adresem: https://bitbucket.org/dbeef/speechlist

Nie rozpisałem się wtedy co do samego Sphinxa, a to jest temat na większy post.

Czym jest CMUSphinx

CMUSphinx jest opensourcowym narzędziem do rozpoznawania mowy, wspierające C, C++, C#, Python, Ruby, Java, Javascript i rozpoznające m.in. angielski, francuski i niemiecki (chociaż społeczność zbudowała modele rozpoznające także chociażby rosyjski, lista modeli do pobrania jest tutaj).

Wielkim plusem w stosunku chociażby do androidowych usług rozpoznawnia głosu (komunikujących się z serwerami Google) z których można korzystać pisząc aplikacje jest to, że Sphinx działa całkowicie offline.

Do jego działania potrzebne są 3 pliki: acoustic model, dictionary i language model.

Acoustic model zależy od języka i sami go raczej nie stworzymy, pobieramy gotowy z listy którą zamieściłem wyżej. Przykładowo, dla języka angielskiego pobieramy model en-us.

Language model i dictionary generujemy sami za pomocą narzędzi do których odniosę się później, ale można je także pobrać.

Teraz najważniejsze: aby uzyskać niemal 100% skuteczność rozpoznawania, jak na filmiku wyżej, nie używałem gotowego słownika i modelu językowego z ich strony (zawierającego wszystkie słowa). CMUSphinx udostępnia narzędzie, pozwalające zbudować słownik tylko konkretnych słów:

http://www.speech.cs.cmu.edu/tools/lextool.html

Dzięki temu, że zamieściłem tam tylko te słowa, które muszą być rozpoznane w Speechlistowym teście, poprawność była tak dobra, że trudno uwierzyć.

Przy tym wszystkim warto też dodać, jak niewiele zasobów wymaga Sphinx. Słowniki i modele ważą tyle co nic, a działanie nie wymaga dużej mocy od komputera.

PocketSphinx z którego korzystam na filmiku i w kodzie poniżej, waży 6.7 kB!

https://bitbucket.org/dbeef/speechlist/src/990c18c459d5f6b89971fc6b93d24fa4730ba07f/android/libs/pocketsphinx.jar?at=master&fileviewer=file-view-default

Praktyka

Wykorzystanie Sphinxa w przykładzie z filmiku wygląda następująco:

Tworzymy obiekt edu.cmu.sphinx.api.Configuration, aby załadować z plików modele i słowniki.

configuration

Tworzymy obiekt edu.cmu.sphinx.api.LiveSpeechRecognizer, dostarczamy mu konfigurację w konstruktorze i stawiamy warunek który skończy rozpoznawanie mowy (np. while(!lastRecognizedWord.equals(“apple“)), w poniższym przykładzie rozpoznawanie trwa przez cały czas (while(true)).

sphinx2

To wszystko. Teraz, we frontendowej części kodu odczytujemy zapisane przez Sphinx słowa i wyświetlamy na ekranie:

frontend

Cały plik znajduje się pod adresem:
 https://bitbucket.org/dbeef/speechlist/src/990c18c459d5f6b89971fc6b93d24fa4730ba07f/core/src/com/dbeef/speechlist/recognition/SpeechRecognizer.java?at=master&fileviewer=file-view-default

 

Linki:

Repozytorium Speechlist (branch master jest pod rozpoznawanie mowy):

https://bitbucket.org/dbeef/speechlist/src/990c18c459d5?at=master

Artykuł opisujący dostosowywanie CMU pod język polski:

 https://www.researchgate.net/publication/282323232_Tuning_a_CMU_Sphinx-III_speech_recognition_system_for_Polish_language

Dla szukających więcej informacji na temat słowników:

https://cmusphinx.github.io/wiki/tutorialdict/

Mam jednak zastrzeżenie co do ich słów:

There is no need to remove unused words from the dictionary unless you want to save memory, extra words in the dictionary do not affect accuracy.

Z moich doświadczeń wynika, że jest zupełnie odwrotnie. Przekonajcie się sami, zapiszcie sobie kilka słów na kartce, które chcecie wypróbować na Sphinxie, pobierzcie cały słownik z ich strony, wypróbujcie dokładność, a potem zróbcie to samo generując własny za pomocą ich narzędzia, ale wstawiając do niego tylko te słowa, które potem będziecie wymawiać.


dsp2017-1

Robimy MMOSG I – Wstęp i research.

 

tldr: Nowa seria – robimy ogame.


Ostatnio dla zabawy próbowałem stworzyć lekki webowy frontend do bazy danych (z losowo wygenerowanymi użytkownikami) przy użyciu JQuery i Spring Boota z MongoDB po stronie serwera. Całkiem i się to spodobało i teraz w głowie mam większy pomysł, czyli stworzenie chociaż najbardziej badziewnego, ale działającego massively multiplayer online strategic game (ogame/plemiona/ikariam etc). Bazy danych + technologie webowe wyglądają znacznie lepiej w parze, jeśli patrzeć pod kątem CV, a wydaje mi się że zasadniczo dobrze jest umieć na jakimś poziomie zwizualizować backend. Zacznijmy więc może od przedstawienia kilku problemów które będą po drodze.

Problemy i research

W jaki sposób serwer będzie wiedział, kiedy może odesłać przeglądarce stronę dostępną tylko dla zalogowanych, a kiedy nie, a bardziej ogólnie: skąd wie, czy użytkownik jest zalogowany?

Z tego co wygooglowałem i sam się domyśliłem, wygląda to tak:

Użytkownik loguje się pomyślnie na stronie (hash hasła z jego formularza pokrywa się z tym w bazie danych), na serwerze zostaje wygenerowany unikatowy token i zapisany w bazie danych razem z adresem IP osoby która się zalogowała a także datą wygaśnięcia tokena. Do tej samej osoby, odsyłany jest token, zapisywany jest w formie ciasteczka, a następnie kiedy tylko ta osoba zażąda strony, która dostępna jest tylko dla zalogowanych, serwer porówna token przesłany z ciasteczek oraz adres IP, jeśli wszystko się zgadza, to odsyła stronę, jeśli nie, redirect na stronę logowania.

Na co warto rzucić okiem:

https://pl.wikipedia.org/wiki/HTTP_cookie

https://blog.risingstack.com/web-authentication-methods-explained/

https://security.stackexchange.com/questions/755/how-does-basic-http-auth-work

http://viralpatel.net/blogs/spring-mvc-cookie-example/ 

http://stackoverflow.com/questions/12050059/user-session-tracking-in-javascript

Trzymanie haseł

Minimum bezpieczeństwa, czyli trzymanie hashy. Samo przesyłanie informacji z przeglądarki na serwer również powinno być jakoś zabezpieczane:

https://stackoverflow.com/questions/4101440/jquery-sending-password-via-ajax

https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet

Wygląd strony

Jestem w tym zielony (nie znam niczego poza CSS, HTML), ale póki co nie przejmuję się tym, frontend wyjdzie po drodze i będę wyszukiwał na bieżąco gdy tylko znajdzie się czas. Co do tego, znalazłem całkiem fajną stronę:

https://uptodate.frontendrescue.org/

Materiały do nauki

Okazuje się, że w sieci jest całkiem sporo opensourcowych klonów Ogame, przykładowa lista:

https://freevps.us/thread-7746.html

W razie problemów, będe zaglądał w kod.


Oczywiście wszystko krok po kroku, zanim dojdziemy do pierwszego prototypu minie czas i jestem świadom że nie zrobię wszystkiego powyższego w jeden weekend.

Gdy zaczniemy pisać kod do tej serii, udostępnię go na moim Githubie:

https://github.com/dbeef


dsp2017-1

 

Sound processing IX – Udało się!

tldr: Nadawanie wiadomości z wewnątrz gry i odbieranie jej stamtąd działa pełną parą. Pozostało zaimplementować rozgrywkę z prawdziwego zdarzenia i podsumować serię w okrągłych dziesięciu postach.


Rozwiązanie problemu z głośnikami

Przekopałem całą dokumentację Beads, nie znalazłem niczego co wskazywałoby na problem albo chociaż pozwalało nadać wszystko ciągiem – znalazłem jednak możliwość zapisywania wygenerowanych częstotliwości jako plik .wav i… bingo.

Zapisuję każdą jedną z częstotliwości dotychczas już generowanych na dysku (każda po ~32.kB) , po czym łączę je w jeden duży (5.8 MB) plik i odtwarzam wewnątrz Mad Pirates. Takich mniejszych plików jest 188, wygenerowanie ich zajmuje około 6 sekund.

Screenshot from 2017-05-27 21-27-10.pngWygenerowane pliki.

Odtworzenie całej wiadomości jako plik .wav nie stwarza najmniejszych problemów z głośnikami, więc największy problem transmisji jest już na dobre zamknięty.

Screenshot from 2017-05-27 21-31-57.pngKlasa FileMerger.java którą łączę powstałe pliki .wav.
filerecorder.pngKlasa FileRecorder.java która jest przerobionym tutorialem Beads ze strony:
https://github.com/orsjb/beads/blob/master/packages/Beads/beads_tutorial/Lesson09_RecordToSample.java

Emitowanie i nasłuchiwanie

Komputer musi w jakiś sposób wiedzieć, czy ta część wiadomości, którą do tej pory odebrał, nadaje się do parsowania. Rozwiązałem to, tworząc cyklicznie co sekundę nowy wątek próbujący sparsować obecny bufor z informacjami z głośników. Jeśli się nie powiedzie – trudno, exception, wątek ginie, tworzy się nowy i próbuje to samo. Jeśli się uda – transmisję uznajemy za udaną i podstawiamy wyniki ze sparsowanego obiektu do symulacji. Działa, ale niezbyt eleganckie. Do wymiany w przyszłości.

sounddecoder.png

Jeśli chodzi o emitowanie wiadomości, to skorzystałem ze starej klasy StarterBroadcaster.java, która zamiast odtwarzać dźwięki, to zapisuje je, a po skończeniu tego procesu odczytuje główny plik ze zlepionymi dźwiękami – po wszystkim wątek się zamyka. StarterBroadcaster tworzony jest na żądanie, przyciskiem klawisza G.

Reorganizacja kodu

Do utrzymania porządku dopisałem kilka klas, m.in. FileLoader.java, z którego biorę referencje do tekstur które załadował, a także HUD.java, który trzyma wszystkie możliwe opcje wyświetlania interfejsu użytkownika, tj. tryb w którym wypisuje “Tura planowania”, tryb z wypisywaniem “Tura symulacji” i tak dalej. KeyboardInterpreter.java odpowiada za logikę przy wciskaniu klawiszy a InputModel.java został dodany dla łatwego transportu informacji pomiędzy klasami.

Nie do końca jestem zadowolony ze zmian (logika działania nadal nie jest do końca czytelna i można się pogubić w boolean-ach), ale na ten moment wystarczy.

Efekty

Oto zakończona sukcesem próba przesłania obecnej pozycji statku dźwiękiem (odbiorcą i nadawcą był ten sam komputer).

Co dalej

Aby uczynić owoce tej pracy użytecznymi, zamiast przesyłać pozycję XY statku będziemy przesyłać pozycję kliknięcia myszką gracza i do tego na start gry przyjmować w argumencie konsoli to, którym graczem gramy (wtedy obie strony wzajemnie znają początkową pozycję początkową siebie i przeciwnika) – tak właściwie, to już to zrobiłem, tym urywkiem kodu:
pozycje

Aby przetestować to rozwiązanie prosto w IDE, wystarczy wyedytować konfigurację startową:

konf

Jesteśmy na skraju celu, którym jest faktycznie móc się poruszać po planszy co turę, grając w dwie osoby, więc to już w następnym, podsumowującym poście.


Jak zawsze, kod dostępny jest na moim GitHubie:

https://github.com/dbeef/soundcoding

dsp2017-1

Sound processing VIII – Shadery i nadawanie pozycji

Cotygodniowy postęp prac stopniowo się zmniejsza ze względu na zbliżającą się maturę. Mam nadzieję, że po mniej więcej połowie maja będę miał więcej czasu na blogowanie.


Problemem z Shaderami który zgłosiłem w ostatnim poście okazało się podlinkowanie modułu libgdx-contribs do projektu core, ale nie do desktop – przez to projekt z kodem rzeczywiście widział bibliotekę i mógł bez problemu korzystać z jej klas, ale projekt który uruchamiał LibGDX i wykorzystywał kod z core już dostępu do nich nie miał:

Screenshot from 2017-04-23 22-04-27.pngPrawidłowo podlinkowane libgdx-contribs

Do tego drobiazgi, takie jak ustawienie prawidłowej ścieżki w klasie ShaderLoader, ze standardowego:

public static String BasePath = "";

Na odpowiadające mojej strukturze:

public static String BasePath = "core/assets/shaders/";

Co wynika z tego całego dobrego podlinkowania? Pozwala dodać mi krzywiznę do dwuwymiarowego obrazu, oto jak się przezentuje:

Jest kilka innych fajnych efektów które można dodać za pośrednictwem tej biblioteki, opisałem je w moim starym poście na ten temat:

https://dbeef.wordpress.com/2016/05/01/easy-shaders-via-libgdx-contribs-postprocessing/

Dodałem również wspominany już wiele razy modem – teraz w momencie zatrzymania symulacji umożliwia się nam wysłanie jsona z naszą pozycją. Na ten moment nie jest to użyteczne i służy bardziej do nauki komputera jak ma debugować wiadomość w której brakuje informacji (powiedzmy, że znak klamry przesłał się jako znak którego nie można przepisać do żadnego z tabeli ASCII bo komputer zamiast usłyszeć 3900 Hz, dostał 4000 Hz, głupio byłoby tylko z tego powodu wysyłać wiadomość drugi raz, skoro można nauczyć go pewnych wzorców).

Do tego pojawia się pewien problem – przy dłuższym czasie nadawania (więcej niż 10 sekund) głośniki potrafią wariować, kaszleć i szumieć zamiast wydawać piski. Mam wrażenie, że próbują wtedy odtwarzać kilka wysokich częstotliwości na raz, co wskazuje na błąd w kodzie. Zobaczymy następnym razem.

Poniżej krótki film z nadawania. Komputer odszyfrował bardziej coś jsono-podobnego, ze względu na powyższe (na filmie słychać te objawy), a także krótkie interwały pomiędzy częstotliwościami, na które naciskam, aby transmisja się nie wlekła. Nadal pracuję nad lepszymi wynikami przesyłu.

Odnośniki:

Kod jak zawsze dostępny jest na moim GitHubie:

https://github.com/dbeef/soundcoding/branches


dsp2017-1

Różne IV – SSTV

W temacie przesyłania danych w nie-internetowy sposób warto wspomnieć o SSTV (przesyłanie obrazów falami krótkimi). Cokolwiek bym tu napisał i tak lepiej wyłoży to ten artykuł, którego nie chcę tu cytować, bo stanowiłby większość posta.

Z racji tego (i chwilowego braku czasu), jest to zbiór odnośników, które wprowadzają w temat.

Jak pogadać z ISS

To zdecydowanie najbardziej ekscytująca sprawa:

Most of the astronauts on the International Space Station are licenced Radio Amateurs and sometimes during their spare time they talk to other Radio Amateurs back on earth. There is a special thrill in talking to an astronaut out in space!

Polecam cały artykuł:
https://amsat-uk.org/beginners/how-to-hear-the-iss/

Nadawanie obrazów z ISS

https://amsat-uk.org/beginners/iss-sstv/

Kod

Na GitHubie znalazłem sporo otwartych programów do nadawania obrazów SSTV:

https://github.com/search?utf8=%E2%9C%93&q=sstv&type=

Inne

Trafiłem w międzyczasie na mikrosatelitę ufundowaną przez stowarzyszenia radioamatorskie, również jej można posłuchać z odpowiednim sprzętem:

http://www.amsat.org/?page_id=2039

Swoją drogą, wysłano ją 34 metrowym przerobionym radzieckim ICBM-em, dla porównania, Saturn V (ten od wysłania ludzi na księżyc) miał 110 metrów.

Tutaj można podsłuchiwać poszczególne długości fali (np. ISS czy wspomniany wcześniej Funcube, jeśli nie zasłania ich aktualnie Ziemia):

http://websdr.suws.org.uk/