Цифровой звук — это представление аудиосигналов в цифровом формате. Одним из основных способов кодирования звука в цифровом виде является импульсно-кодовая модуляция (PCM). PCM представляет собой метод, при котором аналоговый звуковой сигнал преобразуется в цифровые данные путем дискретизации и квантования. Дискретизация — это процесс измерения амплитуды звука через равные промежутки времени, а квантование — это процесс округления этих значений до ближайшего целого числа.
Параметры звука
При работе с цифровым звуком важно учитывать следующие параметры:
- Частота дискретизации (Sample Rate): количество измерений амплитуды звука в секунду. Обычно выражается в герцах (Гц). Стандартное значение для CD качества звука — 44100 Гц.
- Битовая глубина (Bit Depth): количество битов, используемых для представления каждой амплитуды. Чем выше битовая глубина, тем более точно можно представить амплитуду.
- Количество каналов (Channels): количество отдельных звуковых дорожек. Один канал соответствует моно-звуку, два канала — стерео.
Создание звука с помощью dlib.audio
Библиотека dlib предоставляет множество полезных инструментов для работы с цифровым звуком. Рассмотрим, как создать объект Sound
, модифицировать его и сохранить в файл WAV.
Генерация синусоидального сигнала
Пример кода для генерации синусоидального сигнала:
import dlib.audio;
import std.math : PI, sin;
import std.stdio : File;
void generateSineWave() {
double frequency = 440.0; // Частота сигнала (Гц)
double duration = 2.0; // Длительность сигнала (секунды)
int sampleRate = 44100; // Частота дискретизации
double amplitude = 0.5; // Амплитуда сигнала (0.0 - 1.0)
// Вычисление количества сэмплов
int numSamples = cast(int)(sampleRate * duration);
// Создание объекта Sound
auto sound = new Sound(sampleRate, 1, numSamples);
// Заполнение сэмплов синусоидальным сигналом
foreach (i; 0 .. numSamples) {
double t = i / sampleRate;
sound[0][i] = cast(short)(amplitude * 32767 * sin(2 * PI * frequency * t));
}
// Сохранение в файл
auto file = File("sine_wave.wav", "wb");
sound.writeWave(file);
file.close();
}
void main() {
generateSineWave();
}
Использование ADSR-огибающей
ADSR-огибающая (Attack, Decay, Sustain, Release) позволяет моделировать изменение громкости звука в четыре стадии:
- Attack (Атака): время, за которое звук достигает максимальной громкости.
- Decay (Спад): время, за которое громкость уменьшается до уровня поддержки.
- Sustain (Поддержка): уровень громкости, который сохраняется после стадии спада.
- Release (Затухание): время, за которое громкость уменьшается до нуля после окончания звука.
Пример кода для моделирования ADSR:
import dlib.audio;
import std.math : PI, sin;
import std.stdio : File;
void applyADSR(double[] samples, double attack, double decay, double sustain, double release, int sampleRate) {
int attackSamples = cast(int)(attack * sampleRate);
int decaySamples = cast(int)(decay * sampleRate);
int releaseSamples = cast(int)(release * sampleRate);
int sustainSamples = samples.length - attackSamples - decaySamples - releaseSamples;
// Атака
foreach (i; 0 .. attackSamples) {
samples[i] *= i / double(attackSamples);
}
// Спад
foreach (i; 0 .. decaySamples) {
samples[attackSamples + i] *= 1.0 - (1.0 - sustain) * (i / double(decaySamples));
}
// Поддержка
foreach (i; 0 .. sustainSamples) {
samples[attackSamples + decaySamples + i] *= sustain;
}
// Затухание
foreach (i; 0 .. releaseSamples) {
samples[attackSamples + decaySamples + sustainSamples + i] *= sustain * (1.0 - i / double(releaseSamples));
}
}
void generateSineWaveWithADSR() {
double frequency = 440.0; // Частота сигнала (Гц)
double duration = 2.0; // Длительность сигнала (секунды)
int sampleRate = 44100; // Частота дискретизации
double amplitude = 0.5; // Амплитуда сигнала (0.0 - 1.0)
double attack = 0.1; // Время атаки (секунды)
double decay = 0.1; // Время спада (секунды)
double sustain = 0.7; // Уровень поддержки
double release = 0.2; // Время затухания (секунды)
// Вычисление количества сэмплов
int numSamples = cast(int)(sampleRate * duration);
// Создание объекта Sound
auto sound = new Sound(sampleRate, 1, numSamples);
// Заполнение сэмплов синусоидальным сигналом
foreach (i; 0 .. numSamples) {
double t = i / sampleRate;
sound[0][i] = cast(short)(amplitude * 32767 * sin(2 * PI * frequency * t));
}
// Применение ADSR
auto samples = sound[0];
applyADSR(samples, attack, decay, sustain, release, sampleRate);
// Сохранение в файл
auto file = File("sine_wave_adsr.wav", "wb");
sound.writeWave(file);
file.close();
}
void main() {
generateSineWaveWithADSR();
}
Частотная модуляция
Частотная модуляция (FM) — это метод генерации звука, при котором частота одного сигнала изменяется в зависимости от амплитуды другого сигнала. Пример кода для FM-синтеза:
import dlib.audio;
import std.math : PI, sin;
import std.stdio : File;
void generateFMSound() {
double carrierFreq = 440.0; // Частота несущего сигнала (Гц)
double modulatorFreq = 220.0; // Частота модулирующего сигнала (Гц)
double modulationIndex = 100.0; // Индекс модуляции
double duration = 2.0; // Длительность сигнала (секунды)
int sampleRate = 44100; // Частота дискретизации
double amplitude = 0.5; // Амплитуда сигнала (0.0 - 1.0)
int numSamples = cast(int)(sampleRate * duration);
auto sound = new Sound(sampleRate, 1, numSamples);
foreach (i; 0 .. numSamples) {
double t = i / sampleRate;
double modulator = sin(2 * PI * modulatorFreq * t);
double carrier = sin(2 * PI * carrierFreq * t + modulationIndex * modulator);
sound[0][i] = cast(short)(amplitude * 32767 * carrier);
}
auto file = File("fm_sound.wav", "wb");
sound.writeWave(file);
file.close();
}
void main() {
generateFMSound();
}
Считывание нот из строки и создание объекта звука
Таблица частот для нот:
Нота | Частота (Гц) |
---|---|
C4 | 261.63 |
D4 | 293.66 |
E4 | 329.63 |
F4 | 349.23 |
G4 | 392.00 |
A4 | 440.00 |
B4 | 493.88 |
Пример кода для считывания нот:
import dlib.audio;
import std.math : PI, sin;
import std.stdio : File;
double getFrequency(string note) {
switch (note) {
case "C4": return 261.63;
case "D4": return 293.66;
case "E4": return 329.63;
case "F4": return 349.23;
case "G4": return 392.00;
case "A4": return 440.00;
case "B4": return 493.88;
default: return 440.00;
}
}
void generateMelody(string
melody, double duration, int sampleRate) {
double amplitude = 0.5;
int numSamples = cast(int)(sampleRate * duration);
auto sound = new Sound(sampleRate, 1, numSamples);
int sampleIndex = 0;
foreach (note; melody.split(" ")) {
double frequency = getFrequency(note);
int noteSamples = cast(int)(sampleRate * (duration / melody.split(" ").length));
foreach (i; 0 .. noteSamples) {
double t = (sampleIndex + i) / sampleRate;
sound[0][sampleIndex + i] = cast(short)(amplitude * 32767 * sin(2 * PI * frequency * t));
}
sampleIndex += noteSamples;
}
auto file = File("melody.wav", "wb");
sound.writeWave(file);
file.close();
}
void main() {
generateMelody("C4 E4 G4 C5", 4.0, 44100);
}
Полный исходный код программы
import dlib.audio;
import std.math : PI, sin;
import std.stdio : File;
double getFrequency(string note) {
switch (note) {
case "C4": return 261.63;
case "D4": return 293.66;
case "E4": return 329.63;
case "F4": return 349.23;
case "G4": return 392.00;
case "A4": return 440.00;
case "B4": return 493.88;
default: return 440.00;
}
}
void applyADSR(double[] samples, double attack, double decay, double sustain, double release, int sampleRate) {
int attackSamples = cast(int)(attack * sampleRate);
int decaySamples = cast(int)(decay * sampleRate);
int releaseSamples = cast(int)(release * sampleRate);
int sustainSamples = samples.length - attackSamples - decaySamples - releaseSamples;
foreach (i; 0 .. attackSamples) {
samples[i] *= i / double(attackSamples);
}
foreach (i; 0 .. decaySamples) {
samples[attackSamples + i] *= 1.0 - (1.0 - sustain) * (i / double(decaySamples));
}
foreach (i; 0 .. sustainSamples) {
samples[attackSamples + decaySamples + i] *= sustain;
}
foreach (i; 0 .. releaseSamples) {
samples[attackSamples + decaySamples + sustainSamples + i] *= sustain * (1.0 - i / double(releaseSamples));
}
}
void generateMelody(string melody, double duration, int sampleRate) {
double amplitude = 0.5;
double attack = 0.1;
double decay = 0.1;
double sustain = 0.7;
double release = 0.2;
int numSamples = cast(int)(sampleRate * duration);
auto sound = new Sound(sampleRate, 1, numSamples);
int sampleIndex = 0;
foreach (note; melody.split(" ")) {
double frequency = getFrequency(note);
int noteSamples = cast(int)(sampleRate * (duration / melody.split(" ").length));
foreach (i; 0 .. noteSamples) {
double t = (sampleIndex + i) / sampleRate;
sound[0][sampleIndex + i] = cast(short)(amplitude * 32767 * sin(2 * PI * frequency * t));
}
sampleIndex += noteSamples;
}
auto samples = sound[0];
applyADSR(samples, attack, decay, sustain, release, sampleRate);
auto file = File("melody_adsr.wav", "wb");
sound.writeWave(file);
file.close();
}
void main() {
generateMelody("C4 E4 G4 C5", 4.0, 44100);
}
Этот код создаст файл melody_adsr.wav
, содержащий мелодию с примененной ADSR-огибающей, что придаст звуку более естественное звучание.
Автор статьи:
Обновлено:
Добавить комментарий