Аудио манипуляции в C ++ - PullRequest
3 голосов
/ 21 марта 2011

Надеюсь, это правильное место для публикации, и кто-то может помочь.

Я студент музыкальных технологий, и недавно я начал изучать C ++, так как это очень помогло бы моей карьере, зная язык программирования, особенно этот, поскольку он используется в индустрии видеоигр.

В любом случае на главную тему. Я хочу создать программу (на C ++), которая позволяет пользователю загружать 16-битный линейный файл PCM WAVE. Затем я хочу манипулировать данными аудиосэмпла в этом волновом файле. Я хочу либо удалить каждую n-ю выборку, либо рандомизировать их в пределах определенного параметра (± 10%). Затем запишите его как новый файл WAVE.

Я знаком со структурой файлов WAVE и заголовком RIFF. В настоящее время я также использую Xcode в качестве своей IDE (поскольку мой MacBook Pro - это мой рабочий компьютер), но я могу при необходимости кодировать на своем ПК, используя кодовые блоки.

Значит, в простых терминах должно отображаться что-то похожее на это? Я знаю, что в этом есть ошибки, просто чтобы вы поняли, что мне нужно:

#include <iostream>
using namespace std;

class main()    //function start
{
    string fileinput;   //variable
    string outlocation; //variable

    cout << "please type file path directory: \n \n";
    cin >> fileinput;   //navigate to file by typing

    cout << "Where would you like to save new file? \n \n";
    cin >> outlocation; //select output by typing

    // Then all the maths and manipulation is done

    cout << "Your file has been created at ";
    cout << outlocation;
    cout << "\n \n";

    system("pause");

    return 0;
}

Возможно ли это сделать в Xcode, если вообще? Какие библиотеки мне понадобятся? Я понимаю, что это не простая вещь, поэтому любая помощь будет принята с благодарностью.

Спасибо за вашу помощь и время.

Джеймс

Ответы [ 3 ]

5 голосов
/ 21 марта 2011

Если вам известна файловая структура RIFF, возможно, вы уже знаете, как в ней хранится звук PCM.

Общий формат - 16-битный стерео PCM. В этом случае каждая выборка составляет 2 байта, и две выборки принадлежат друг другу (слева + справа). Но вы должны проверить формат чанка для точного формата. Но я полагаю, что сейчас вы управляете 16-битным стереофоническим PCM-WAV-файлом.

Вы можете манипулировать выборками, используя 16-битный целочисленный тип (short, _int16, int16_t). Например, чтобы уменьшить громкость, вы можете разделить каждый семпл на некоторое число. Но если вы разделите его на 2, это не означает, что оно станет вдвое громче. Смотрите этот пост .

Если вы просто манипулируете семплами, заголовки RIFF не меняются, поэтому вы можете скопировать их из источника.

Если вы хотите удалить или добавить сэмплы, размер фрагмента данных изменится, а также размер всего файла в заголовке рифа. Например, вы можете просто отбросить каждый 10-й пример, затем скопировать 9 * 4 = 36 байтов из блока данных, пропустить 4 байта, скопировать 36 байтов и так далее. Но если вы сделаете что-то подобное, это будет звучать очень плохо. Лучший способ услышать результат - манипулировать синусоидальной волной. Если синус не совсем правильный, его будет легко услышать. Чтобы правильно отбросить выборки, вам, вероятно, нужно использовать быстрое преобразование Фурье (БПФ).

В качестве дополнения на основе ваших комментариев добавляю следующее:

См. C ++ Двоичный файл ввода / вывода для быстрого ознакомления с файловым вводом / выводом. Ваша ссылка с описанием формата RIFF выглядит правильно, но не завершена. Согласно этому описанию заголовок всегда составляет 44 байта. Но можно добавить больше информации в шапку.

Что вам нужно сделать, это пропустить первые 12 байтов (хотя вы можете использовать его, чтобы проверить, является ли файл действительно волновым файлом). А затем в цикле прочитать имя и размер следующего куска. Если это фрагмент, который вы знаете ('fmt' или 'data'), вы можете обработать его, в противном случае пропустите его.

Так это может выглядеть, например:

ifstream myFile ("example.wav", ios::in | ios::binary);
char buffer[12];
myFile.read (buffer, 12); // skip RIFF header

char chunkName[5];
unsigned long chunksize;
while (myFile.read (chunkName, 4)) {
    chunkName[4]='\0'; // add trailing zero
    myFile.read((char*)&chunksize, 4);

    // if chunkname is 'fmt ' or 'data' process it here,
    // otherwise skip any unknown chunk:
    myFile.seekg(chunksize, ios_base::cur);
}
0 голосов
/ 21 марта 2011
0 голосов
/ 21 марта 2011

Вот (относительно) переносимые источники моих утилит wav2pcm и pcm2wav: http://nishi.dreamhosters.com/u/wav2pcm_v0.rar

...