
Прошлогоднюю статью
«Рисуем звук» я завершил признанием:
«А можно ли нарисовать звук с чистого листа, не обводя спектрограмму аудиозаписи? Скажу честно, у меня не получилось.» Но недавно я узнал про
S.A.M. — выпущенный в
1982 г. компанией Don’t Ask Software, он стал первой коммерчески успешной программой для синтеза речи на ПК. В середине
2000-х немецкие демосценщики Tobias Korbmacher и Sebastian Macke взяли ассемблерный листинг S.A.M. для Commodore 64 и сконвертировали его в нечитаемый, но работоспособный код на Си; затем в
2014 г. британец Vidar Hokstad постарался привести код на Си в читаемый вид — вручную давая переменным осмысленные названия и заменяя
goto
на циклы и ветвления; и наконец, в
2017 г. ещё один немец Christian Schiffler переписал код с Си на JavaScript. Испробовать его в действии как «чёрный ящик» можно на
discordier.github.io/sam.
По-моему, примитивный синтезатор речи на JavaScript — самый удобный подопытный образец для тех, кто хочет разобраться, как в целом работает синтез речи. Мой форк S.A.M. с существенно почищенным кодом и комментариями доступен на
github.com/tyomitch/sam. К сожалению, у предыдущих авторов интерес к S.A.M. успел угаснуть, и им сейчас не до разбора пулл-реквестов в хобби-проект многолетней давности.
S.A.M. состоит из четырёх функциональных компонентов:
- Reciter переводит текст на английском в фонемную запись: например, «A LITTLE TOO LOW» (пример из приложенной к S.A.M. демо-программы) превращается в «AH LIHTUL TUW5 LOW».
- Parser превращает фонемную запись в фонетическую: из «AH LIHTUL TUW5 LOW» получается "
AH, ,L,IH,DX,AX,LX, ,T,*,*,UX,WX, ,L,OW,WX
". Для каждого выводимого фона Parser задаёт также длительность и тон.
- Renderer строит по фонетической записи массив частот, амплитуд и прочих акустических характеристик;
- Последний, безымянный компонент (функция
ProcessFrames
) превращает массив частот и амплитуд в PCM-поток для аудиовывода.
В этой статье я разберу все четыре компонента по очереди.