VoxelEngine/doc/ru/audio.md
2025-11-07 23:05:50 +03:00

283 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Аудио
## Основные понятия
### Бекенд (Backend)
Вариант внутренней реализации звуковой подсистемы, управляющий выводом звука.
На данный момент в движке существует два:
- NoAudio - заглушка, используемая при невозможности инициализации OpenAL, либо, при отключенной через файл настроек, аудиосистеме: `[audio] enabled=false`. Данный бекенд загружает PCM данные только по требованию, не создает спикеров при попытке воспроизведения аудио.
- ALAudio - основной вариант. Вывод звука через OpenAL.
### Канал (Channel)
Определяет категорию источников аудио для регулирования громкости, наложения эффектов, паузы.
На данный момент существует следующий набор каналов:
- master - управляет громкостью остальных каналов. Не следует указывать как целевой канал при воспроизведении аудио.
- ui - звуки интерфейса
- regular - звуки игрового мира, ставятся на паузу вместе с игрой.
- ambient - то же, что и regular, но предназначается для фоновых звуков: погода и иной эмбиент.
- music - канал для воспроизведения музыки. Как правило, потокового аудио.
Каналы управляются самим движком.
### Спикер (Speaker)
Одноразовый контроллер проигрываемого аудио: звука или потока. Спикер уничтожается после остановки через вызов метода **stop** или при окончании аудио (поток также не удерживает спикер от уничтожения).
Контроллер продолжает жить при паузе.
> [!NOTE]
Доступ к спикерам производится по целочисленным id, которые не повторяются за время работы движка, следует избегать хранения прямых указателей на объекты класса.
Нумерация ID спикеров начинается с 1. ID 0 означает невозможность воспроизведения, по какой-либо причине.
### Звук (Sound)
Звуковые данные загруженные в память для возможности одновременного воспроизведения из нескольких источников. Может предоставлять доступ к PCM данным.
### Источник PCM (PCMStream)
Поток, используемый потоком как источник PCM-данных. Реализация зависит не от бекенда звуковой системы, а от формата файла. Реализация потокового аудио из сетевого соединения делается через реализацию данного интерфейса.
### Поток (Stream)
Потоковое аудио. Не загружается полностью в память, поэтому не требует предзагрузки через `preload.json`. Не может воспроизводиться через несколько спикеров одновременно.
## Поддержка форматов
На данный момент реализована поддержка двух форматов.
- WAV: поддерживаются 8 и 16 bit (24 bit не поддерживается OpenAL)
- OGG: реализовано через библиотеку libvorbis
## Дополнительно
> [!WARNING]
> При воспроизведении через OpenAL стерео звуки не будут учитывать расположение источников относительно игрока. Звуки, которые должны учитывать расположение, должны быть в моно.
## API аудио в скриптинге
### Воспроизведение аудио
Работа с аудио производится с библиотекой `audio`.
```lua
audio.play_stream(
-- путь к аудио-файлу (без точки входа, но с указанием расширения)
name: string,
-- позиция источника аудио в мире
x: number, y: number, z: number,
-- громкость аудио (от 0.0 до 1.0)
volume: number
-- скорость воспроизведения (положительное число)
pitch: number,
-- [опционально] имя канала: regular/ambient/music/ui (по-умолчанию - regular)
channel: string,
-- [опционально] зацикливание потока (по-умолчанию - false)
loop: bool
) -> int
```
Воспроизводит потоковое аудио из указанного файла, на указанной позиции в мире. Возвращает id спикера.
```lua
audio.play_stream_2d(
-- путь к аудио-файлу (без точки входа, но с указанием расширения)
name: string,
-- громкость аудио (от 0.0 до 1.0)
volume: number
-- скорость воспроизведения (положительное число)
pitch: number,
-- [опционально] имя канала: regular/ambient/music/ui (по-умолчанию - regular)
channel: string,
-- [опционально] зацикливание потока (по-умолчанию - false)
loop: bool
) -> int
```
Воспроизводит потоковое аудио из указанного файла. Возвращает id спикера.
```lua
audio.play_sound(
-- название загруженного звука без префикса пака, "sounds/", номера варианта и расширения
-- пример "steps/stone" для проигрывания звука, загруженного из "sounds/steps/stone.ogg" или любого из его вариантов
-- вариант звука выбирается случайно
name: string,
-- позиция источника аудио в мире
x: number, y: number, z: number,
-- громкость аудио (от 0.0 до 1.0)
volume: number
-- скорость воспроизведения (положительное число)
pitch: number,
-- [опционально] имя канала: regular/ambient/music/ui (по-умолчанию - regular)
channel: string,
-- [опционально] зацикливание потока (по-умолчанию - false)
loop: bool
) -> int
```
Воспроизводит звук на указанной позиции в мире. Возвращает id спикера.
```lua
audio.play_sound_2d(
-- название загруженного звука без префикса пака, "sounds/", номера варианта и расширения
-- пример "steps/stone" для проигрывания звука, загруженного из "sounds/steps/stone.ogg" или любого из его вариантов
-- вариант звука выбирается случайно
name: string,
-- громкость аудио (от 0.0 до 1.0)
volume: number
-- скорость воспроизведения (положительное число)
pitch: number,
-- [опционально] имя канала: regular/ambient/music/ui (по-умолчанию - regular)
channel: string,
-- [опционально] зацикливание потока (по-умолчанию - false)
loop: bool
) -> int
```
Воспроизводит звук. Возвращает id спикера.
### Взаимодействие со спикером.
При обращении к несуществующим спикером ничего происходить не будет.
```lua
-- остановить воспроизведение спикера
audio.stop(speakerid: integer)
-- поставить спикер на паузу
audio.pause(speakerid: integer)
-- снять спикер с паузы
audio.resume(speakerid: integer)
-- установить зацикливание аудио
audio.set_loop(speakerid: integer, state: bool)
-- проверить, зациклено ли аудио (false если не существует)
audio.is_loop(speakerid: integer) -> bool
-- получить громкость спикера (0.0 если не существует)
audio.get_volume(speakerid: integer) -> number
-- установить громкость спикера
audio.set_volume(speakerid: integer, volume: number)
-- получить скорость воспроизведения (1.0 если не существует)
audio.get_pitch(speakerid: integer) -> number
-- установить скорость воспроизведения
audio.set_pitch(speakerid: integer, pitch: number)
-- получить временную позицию аудио в секундах (0.0 если не существует)
audio.get_time(speakerid: integer) -> number
-- установить временную позицию аудио в секундах
audio.set_time(speakerid: integer, time: number)
-- получить позицию источника звука в мире (nil если не существует)
audio.get_position(speakerid: integer) -> number, number, number
-- установить позицию источника звука в мире
audio.set_position(speakerid: integer, x: number, y: number, z: number)
-- получить скорость движения источника звука в мире (nil если не существует)
-- (используется OpenAL для имитации эффекта Доплера)
audio.get_velocity(speakerid: integer) -> number, number, number
-- установить скорость движения источника звука в мире
-- (используется OpenAL для имитации эффекта Доплера)
audio.set_velocity(speakerid: integer, x: number, y: number, z: number)
-- получить длительность аудио в секуднах, проигрываемого источником
-- возвращает 0, если не спикер не существует
-- так же возвращает 0, если длительность неизвестна (пример: радио)
audio.get_duration(speakerid: integer) -> number
```
### Другие функции
```lua
-- получить текущее число живых спикеров
audio.count_speakers() -> integer
-- получить текущее число проигрываемых аудио-потоков
audio.count_streams() -> integer
```
### audio.PCMStream
```lua
-- создание источника PCM данных
local stream = audio.PCMStream(
-- частота дискретизации
sample_rate: integer,
-- число каналов (1 - моно, 2 - стерео)
channels: integer,
-- число бит на сэмпл (8 или 16)
bits_per_sample: integer,
)
-- подача PCM данных в поток
stream:feed(
-- PCM данные для подачи в поток
data: Bytearray
)
-- публикация источника PCM данных для использования системами движка
stream:share(
-- имя потокового аудио, которое можно будет указать в audio.play_stream
alias: string
)
-- создание звука из имеющихся в потоке PCM данных
stream:create_sound(
-- имя создаваемого звука
name: string
)
```
### Запись звука
```lua
-- запрашивает доступ к записи звука
-- при подтверждении, в callback передаётся токен для использовании в audio.input.fetch
audio.input.request_open(callback: function(string))
-- читает новые PCM данные аудио ввода
audio.input.fetch(
-- токен, полученный через audio.input.request_open
access_token: string,
-- максимальное размер буфера в байтах
[опционально] max_read_size: integer
)
```
### Пример генерации аудио:
```lua
-- для работы с 16-битными семплами используйте U16view поверх Bytearray
-- пример:
local max_amplitude = 32767
local sample_rate = 44100
local total_samples = sample_rate * 5 -- 5 секунд моно
local bytes = Bytearray(total_samples * 2) -- 5 секунд 16 бит моно
local samples = I16view(bytes)
local frequency_hz = 400
for i=1, total_samples do
local value = math.sin(i * math.pi * 2 / sample_rate * frequency_hz)
samples[i] = value * max_amplitude
end
local stream_name = "test-stream"
local stream = audio.PCMStream(sample_rate, 1, 16)
stream:feed(bytes)
stream:share(stream_name)
local volume = 1.0
local pitch = 1.0
local channel = "ui"
audio.play_stream_2d(stream_name, volume, pitch, channel)
```