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/

Sound Processing VII – Przewidywanie trajektorii

W tej części wprowadzimy przewidywanie ścieżki ruchu i podzielimy ruch na tury. Zapraszam.


Na czym skończyliśmy

Po ostatniej części serii odziedziczyliśmy zaimplementowany silnik fizyczny, sterowanie statkiem i prosty zoom. Do gry dorzucimy teraz przewidywanie ruchu (jak w Angry Birds) – rysowana będzie trajektoria ruchu do wskazanego punktu.

Zajmiemy się również podziałem ruchu na tury.

Przewidywanie ścieżki

Mój pomysł zamknął się w tym, aby posiadać 2 obiekty symulujące fizykę (Box2D World), mające te same tablicę z ciałami (Array<BodyDef>; statki, wyspy itd.), jeden byłby do symulacji aktualnej pozycji, drugi do przewidywania tej wskazanej (jeśli dałoby się ustawić ujemną deltę czasu, czyli cofnąć symulację po sprawdzeniu trajektorii, to wystarczyłby jeden).

W momencie wskazania punktu do którego statek ma zmierzać, ustawiam obiektom będącym w silniku do trajektorii pozycje i prędkości ciał z właściwego silnika (czyli dokładnie te same prędkości i pozycje ciał co w momencie kliknięcia) oraz ustawiam mu deltę czasu odpowiadającą czasowi jaki upłynie podczas tury symulacji (czyli jakieś 3-4 sekundy, w zależności jak będzie się wygodnie grało):

captureSimulationDot to klasa składająca się wyłącznie z wartości x, y oraz angle. Służy do zapisu kolejnych pozycji trajektorii do późniejszego rysowania.

Metoda captureSimulation() wywoływana jest w momencie kliknięcia.

Dla każdego ciała dodatkowo ustawiam flagi active, ponieważ po zmienieniu pozycji ciała (setTransform()) silnik automatycznie ustawia ją na false, co skutkuje nie sprawdzaniem kolizji dla tego ciała.

Symulacja fizyki w silniku trajektorii, pomijając ustawienie na sztywno delty i powiązanie jej z timerem, następuje dokładnie w ten sam sposób, co w zwykłym (metoda simulateAlternativeWorld() pod wieloma względami pokrywa się z simulate()), dodałem jednak do niej timer i symulację kilku następnych sekund realizuję co 200 ms, zamiast całość symulacji na jeden raz, aby nie skutkowało to krótkim dławieniem gry.

drawingDots.pngRysowanie poszczególnych punktów przewidzianego toru ruchu. Rysuję tylko pierwsze 400 punktów.

Niestety ten sposób z nieznanych mi przyczyn nie uwzględnia kolizji w trakcie ruchu,  więc trajektoria przebiega po obiektach które normalnie wchodzą w kolizję ze statkiem – do naprawienia w przyszłości.

Ruch w turach

Ruch w turach jest bardzo prosty do zrealizowania. Deklaruję timer, powiedzmy, że z wartością 3 – dopóki nie upłyną 3 sekundy, wywołuję za każdą klatką metodę simulate(),jeśli upłyną – symulacja ustaje, obiekty również zamierają w ruchu.

Klawiszem P przechodzę do następnej tury symulacji, znów ustawiając nim timer na 3.

Do tego wszystkiego dorzuciłem również nową mapę i ograniczyłem zasięg kamery do mapy – nie powiązałem nastomiast zasięgu kamery z zoomem, przez co przy krawędziach mapy, duży zoom-in nie centruje kamery na statku.

Wyniki prac wrzuciłem na YT:

Co dalej?

Próbowałem dorzucić shadery, ale muszę źle ustawiać zależności między projektami, bo Java nie jest w stanie znaleźć odpowiednich klas (z którymi IDE nie ma problemu) – również do zrobienia następnym razem.

W następnej części chciałbym dorzucić jakiś zalążek interfejsu i podłączyć modem dla pierwszej próby gry przez dźwięk.

Odnośniki:

Jak zawsze kod dostępny jest na GitHubie:

https://github.com/dbeef/soundcoding/tree/game


dsp2017-1