W poprzednich artykułach opisałem, jak udało mi się uruchomić CarPlay na Raspberry Pi, uzyskać stabilne połączenie z telefonem i zaimplementować sterowanie dotykowe.
Pozostawał jednak jeszcze jeden ważny element każdego samochodowego systemu multimedialnego — dźwięk.
Na pierwszy rzut oka zadanie wydaje się proste: odebrać dane audio z Carlinkit i przesłać je na wyjście dźwiękowe. W praktyce jednak właśnie obsługa dźwięku okazała się jednym z najbardziej czasochłonnych etapów rozwoju CaRadio.
Szczególnie gdy przyszła kolej na komunikaty nawigacyjne.
Regulacja głośności, na którą zabrakło miejsca
Zacznę od dość prostego, ale nieoczekiwanego problemu.
W pierwotnej konstrukcji radia fizycznie nie miałem gdzie zamontować regulatora głośności.
Początkowo regulowałem dźwięk za pomocą małej bezprzewodowej klawiatury. Do eksperymentów takie rozwiązanie całkiem się nadawało, ale do codziennego użytkowania w samochodzie było niewygodne.
Ostatecznie dodałem drugi ekran, na którym umieściłem przyciski regulacji głośności.
Przewidziałem tam również możliwość ponownego uruchomienia systemu, gdyby coś poszło nie tak.
Nie było to dokładnie takie rozwiązanie, jakie wyobrażałem sobie na początku prac, ale pozwoliło obejść ograniczenia konstrukcyjne bez poważnej przebudowy obudowy radia.
Jeden PCM5102 do całego dźwięku
Do odtwarzania dźwięku w CaRadio wykorzystałem przetwornik cyfrowo-analogowy PCM5102.
O ile dobrze zrozumiałem podczas analizy FastCarPlay, w oryginalnej implementacji dźwięk był rozdzielony pomiędzy dwa urządzenia.
Ja miałem natomiast tylko jeden PCM5102, przez który musiałem odtwarzać wszystko: muzykę, komunikaty nawigacyjne i pozostałe dźwięki CarPlay.
Chodzi o to, że CarPlay przesyła różne rodzaje dźwięku w osobnych strumieniach.
Z punktu widzenia samego systemu jest to wygodne, ale stwarza dodatkowe zadanie, gdy wszystko trzeba skierować na jedno fizyczne wyjście audio.
Dlatego musiałem zaimplementować programowe miksowanie dźwięku.
W rezultacie kilka strumieni audio było łączonych i przesyłanych do jednego przetwornika DAC.
Jak się jednak okazało, samo miksowanie wcale nie było najtrudniejszą częścią pracy.
Kiedy nawigacja zaczyna mówić
Wyobraźmy sobie zwykłą sytuację.
W samochodzie gra muzyka, kierowca zbliża się do skrzyżowania, a nawigacja informuje, gdzie należy skręcić.
W prawidłowo działającym systemie multimedialnym muzyka na czas komunikatu staje się cichsza, głos nawigacji jest dobrze słyszalny, a po zakończeniu komunikatu poprzedni poziom głośności zostaje przywrócony.
Właśnie takie działanie chciałem uzyskać w CaRadio.
Problem polegał na tym, że nie miałem wszystkich informacji potrzebnych do prawidłowego sterowania głośnością.
Albo Carlinkit nie przesyłał odpowiednich danych w dostępnej części protokołu, albo osobom zajmującym się jego inżynierią wsteczną nie udało się wówczas ustalić, jak te dane uzyskać.
Nie mogę stwierdzić, która z tych możliwości była prawdziwa.
W szczególności nie było jasne, jaki poziom głośności należy ustawiać dla muzyki i komunikatów głosowych oraz w którym dokładnie momencie trzeba ściszać muzykę i przywracać jej poprzednią głośność.
Musiałem więc poszukać własnego sposobu sterowania.
Timer zamiast dokładnych informacji z CarPlay
Podstawą rozwiązania stało się monitorowanie napływającego strumienia audio z komunikatami nawigacyjnymi.
Gdy zaczynał napływać komunikat głosowy, CaRadio mogło ściszyć muzykę, aby głos nawigacji nie ginął w tle.
Pojawił się jednak kolejny problem: jak ustalić, że komunikat rzeczywiście się zakończył?
Jeśli przywrócić głośność muzyki natychmiast po ustaniu kolejnej porcji danych audio, można uzyskać gwałtowne przejście, a nawet uciąć końcówkę wypowiedzi.
Komunikaty nawigacyjne nie zawsze docierają jako jeden nieprzerwany fragment dźwięku. Pomiędzy poszczególnymi częściami mogą występować krótkie przerwy.
Dlatego dodałem opóźnienie przed przywróceniem głośności muzyki.
Po ustaniu strumienia komunikatu uruchamiał się timer na około 300–500 milisekund.
Jeśli w tym czasie napływały nowe dane audio, przywrócenie głośności było odkładane.
W ten sposób udało się zmniejszyć ryzyko gwałtownych przełączeń, trzasków i ucinania ostatnich słów komunikatu nawigacyjnego.
Rozwiązanie nie było idealne, ale w praktyce okazało się wystarczająco skuteczne.
Najbardziej niewygodne debugowanie
Chyba właśnie ten etap prac zapamiętałem najlepiej.
Problem polegał na tym, że komunikaty nawigacyjne nie były odtwarzane zawsze i w każdych warunkach.
Samo uruchomienie radia na stole i sprawdzenie dźwięku nie wystarczało.
Aby przeprowadzić pełny test, musiałem zamontować CaRadio w samochodzie i wyruszyć w trasę z uruchomioną nawigacją.
Jeśli podczas jazdy pojawiał się problem, starałem się zapamiętać, w jakich okolicznościach wystąpił.
Po powrocie musiałem ponownie wymontować radio, zabrać je do domu, zmienić kod i powtórzyć test.
I tak wiele razy.
Szczególnie irytujące było to, że niektóre błędy ujawniały się tylko w określonych sytuacjach. Można było przejechać część trasy bez żadnego problemu, a następnie usłyszeć niewłaściwie ściszoną muzykę lub ucięty komunikat.
Po każdej kolejnej zmianie trzeba było ponownie sprawdzać zachowanie systemu w rzeczywistych warunkach.
Trudno nazwać taki sposób debugowania wygodnym, ale wtedy nie miałem innej wystarczająco niezawodnej metody odtworzenia wszystkich tych sytuacji.
Nieidealne, ale działa
Stopniowo udało mi się uzyskać takie działanie, w którym muzyka i komunikaty nawigacyjne przestały sobie nawzajem przeszkadzać.
Oczywiście nie mogę nazwać tej implementacji idealną.
Pozostały pewne niedociągnięcia, a algorytm sterowania głośnością można byłoby dalej udoskonalać.
Najważniejsze jednak, że system zaczął działać stabilnie.
Muzyka jest odtwarzana, komunikaty nawigacyjne są słyszalne, a po ich zakończeniu głośność wraca do poprzedniego poziomu.
Dla mnie był to rezultat, do którego dążyłem.
CaRadio nie powstawało przecież jako projekt demonstracyjny, który wystarczy raz uruchomić na stole.
Chciałem codziennie korzystać z własnego radia.
Pierwsza wersja CaRadio
Na tym chyba można zakończyć opowieść o tworzeniu pierwszej wersji CaRadio.
Podczas prac musiałem rozwiązać wiele problemów: od uruchomienia CarPlay przez Carlinkit, przez obsługę sterowania dotykowego i zarządzanie zasilaniem, aż po odtwarzanie dźwięku.
Niektóre rozwiązania okazały się dość proste, inne wymagały licznych eksperymentów.
Nie zabrakło też kompromisów wynikających z konstrukcji radia, dostępnego sprzętu i czasu, który mogłem poświęcić na projekt.
Ostatecznie jednak z zestawu komponentów i eksperymentów programistycznych powstał działający samochodowy system multimedialny.
Niepozbawiony wad, bez wszystkich funkcji, które chciałbym zrealizować, ale wystarczająco stabilny do codziennego użytkowania.
I to chyba największe osiągnięcie pierwszej wersji CaRadio.
Na tym historia projektu się nie kończy. Po prostu dalej zaczyna się już kolejny etap jego rozwoju.