Я не лазил глубоко в fmod, просто использовал их примеры и сварганил на их основе звуковую систему, но подозреваю что сделал это криво.
Сейчас у меня каждый звук воспроизводится в отдельном треде, т.е. по треду на звук, в каждом создается свой FMOD::Sound.
(оказалось не совсем так, тред один, но в нем std::vecror<FMOD::Sound *> )
Вок как я сейчас создаю звуки:
FMOD_CREATESOUNDEXINFO exinfo = {}; exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); exinfo.length = size; result = data.fmod->system->createSound(buf, FMOD_3D | FMOD_OPENMEMORY, &exinfo, &sound.m_sound);
Но что-то мне говорит, что это неправильно и можно все звуки засунуть в один FMOD::Sound но в разные ChannelGroup. Или я в корне не прав?
Сейчас у меня группа вообще не определена, т.е. всегда нулевая. Не уверен что это правильно, и вообще не понимаю что это за ChannelGroup и для чего они нужны.
Ну или более общий вопрос - как вообще на основе fmod (без интеграции с готовыми движками) делаются программы где одновременно воспроизводятся несколько звуков?
Этот вопрос возник когда мне понадобилось записать звуковой стрим игры в файл, но оказалось что теперь нельзя добраться до системного звука (авторские права!), а fmod позволяет записывать только FMOD::Sound, т.е. в моем случае они все сидят в своих тредах и как записать суммарный звук - непонятно. Использовать для этого микрофон это дикость какая-то...
san
> Ну или более общий вопрос - как вообще на основе fmod (без интеграции с
> готовыми движками) делаются программы где одновременно воспроизводятся
> несколько звуков?
Программы на основе fmod где одновременно воспроизводятся несколько звуков используют микшер.
Микшер (условно) суммирует входные сэмплы и подает результат на выход.
Тред/каждый звук - оверкилл. Треды вообще не нужны, т.к. в основном аудиоменеджеры типа fmod имеют
свой собственный тред, где все нужные звуки микшируются, а комманды типа play() имеют асинхронное поведение.
rcsim
> Тред/каждый звук - оверкилл. Треды вообще не нужны, т.к. в основном аудиоменеджеры типа fmod имеют свой собственный тред, где все нужные звуки микшируются
Да я понимаю, но не вижу как это сделать на одном треде. И микшера я тоже не нашел. Может есть какой нибудь примерчик на fmod где микшируется несколько звуков?
san
> И микшера я тоже не нашел. Может есть какой нибудь примерчик на fmod где
> микшируется несколько звуков?
Микшер - это некая внутренняя сущность. Сам fmod и есть по сути микшер.
Без всяких тредов (т.е. в главном) просто создаешь sound1, sound2, потом подряд вызываешь
system->playSound(sound1);
system->playSound(sound2);
И оба звучат одновременно: вызовы асинхронные. Можно вроде callback установить на время звучания, и мониторить.
Каналы и группы - они вроде строго опциональные.
rcsim
>Без всяких тредов (т.е. в главном) просто создаешь sound1, sound2, потом подряд вызываешь
Спасибо, попробую (пока не за компом).
> Микшер - это некая внутренняя сущность. Сам fmod и есть по сути микшер.
А как до него добраться? Если я хочу записать все, что он воспроизводит?
san
Вот пример коллбэка для пользовательских звуковых эффектов.
https://github.com/kdridi/FMODSample/blob/master/IOS/api/lowlevel… sp_custom.cpp
В примере он режет громкость пополам, ты можешь использовать его для сохранения данных в файл.
CD
Спасибо, разбираюсь.
Кстати оказывается у меня все же играет один тред, значит все не так страшно как я думал. :)
Много тредов открываются только когда я звуки загружаю. Просто я забыл что я там наворотил.
Микшер - это некая внутренняя сущность. Сам fmod и есть по сути микшер.
Микшер - это список потоков к которым применяются методы (суммирование, вычитание(по амплитуде, или времени) воспроизведение, запись, фильтры...) одного объекта (Микшер). В простейшем виде - BASS тоже микшер.
Где то так +\-.
Я использовал createDSP и похоже добрался до суммарного звука, т.е. до "микшера".
Функция сейчас выглядит так:
FMOD_RESULT F_CALLBACK myDSPCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels) { mydsp_data_t *data = (mydsp_data_t *)dsp_state->plugindata; for (unsigned int samp = 0; samp < length; samp++) { for (int chan = 0; chan < *outchannels; chan++) { data->buffer[(samp * *outchannels) + chan] = outbuffer[(samp * inchannels) + chan] = inbuffer[(samp * inchannels) + chan]; } } data->channels = inchannels; return FMOD_OK; }
Как можно видеть, я оставил выходной поток равен входному, т.е. никакой обработки нет, но теперь я вижу данные и могу их накапливать в std::vector<float>.
Потом нужно его как-то превратить в wav файл, но как? Кто нибудь знает как можно сконвертировать массив float в формат wav?
san
> Кто нибудь знает как можно сконвертировать массив float в формат wav?
libsndfile
rcsim
>libsndfile
Спасибо, но похоже это меня не спасет. Документация куцая. Либа написана на архаичном диалекте С (стандарт 1999 года) и собирается только на *ux.
Найти как сделать то, что мне нужно, копаясь в исходниках, я не смог. Увы.
Ну вот, на stackoverflow пример
void writeWAV(float * buffer, int bufferSize){ SF_INFO sfinfo ; sfinfo.channels = 1; sfinfo.samplerate = 44100; sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; const char* path = "~/Documents/hello.wav"; SNDFILE * outfile = sf_open( path, SFM_WRITE, &sfinfo); sf_count_t count = sf_write_float( outfile, &buffer[0], bufferSize) ; sf_write_sync( outfile); sf_close( outfile); }
Вроде всё просто.
rcsim
Ну я нечто подобное и сделал. Все работает. Вот код, может кому полезно будет:
std::vector<short> Sound_Buffer; // это буфер куда накапливается звук. // инициализация FMOD_RESULT Sound::SetRecorder() { FMOD_DSP_DESCRIPTION dspdesc; memset(&dspdesc, 0, sizeof(dspdesc)); strncpy(dspdesc.name, "DSP unit", sizeof(dspdesc.name)); dspdesc.version = 0x00010000; dspdesc.numinputbuffers = 1; dspdesc.numoutputbuffers = 1; dspdesc.read = myDSPCallback; dspdesc.create = myDSPCreateCallback; dspdesc.release = myDSPReleaseCallback; FMOD_RESULT result = system->createDSP(&dspdesc, &mydsp); if (result != FMOD_OK) return result; result = system->getMasterChannelGroup(&mastergroup); if (result != FMOD_OK) return result; return mastergroup->addDSP(0, mydsp); } /* Callback called when DSP is created. */ #include <chrono> std::chrono::high_resolution_clock::time_point start_time; FMOD_RESULT F_CALLBACK myDSPCreateCallback(FMOD_DSP_STATE *dsp_state) { unsigned int blocksize; FMOD_RESULT result; result = dsp_state->functions->getblocksize(dsp_state, &blocksize); if (result != FMOD_OK) return result; mydsp_data_t *data = (mydsp_data_t *)calloc(sizeof(mydsp_data_t), 1); if (!data) { return FMOD_ERR_MEMORY; } dsp_state->plugindata = data; data->length_samples = blocksize; data->buffer = (float *)malloc(blocksize * 2 * sizeof(float)); // 2 channels (stereo). if (!data->buffer) { return FMOD_ERR_MEMORY; } start_time = std::chrono::high_resolution_clock::now(); return FMOD_OK; } // Основной колбек. Тут накапливается звук. FMOD_RESULT F_CALLBACK myDSPCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels) { mydsp_data_t *data = (mydsp_data_t *)dsp_state->plugindata; for (unsigned int samp = 0; samp < length; samp++) { for (int chan = 0; chan < *outchannels; chan++) { short val = inbuffer[(samp * inchannels) + chan] * 32768.0; Sound_Buffer.push_back(val); // просто добавляю данные к буферу преобразовывая float в short } } data->channels = inchannels; return FMOD_OK; } // А это вызывается в конце и записывает все на диск #include <fstream> void write32(ofstream &stream, int value) { stream.write((const char*)&value, sizeof value); } void write16(ofstream &stream, short value) { stream.write((const char*)&value, sizeof value); } FMOD_RESULT F_CALLBACK myDSPReleaseCallback(FMOD_DSP_STATE *dsp_state) { if (dsp_state->plugindata) { if (Sound_Buffer.size()) { std::chrono::milliseconds duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_time); ofstream stream; int samplerate = Sound_Buffer.size() / (duration.count()*2.0 / 1000.0); //44100; int frame_size = sizeof(Sound_Buffer[0]); stream.open("output.wav", ios::out | ios::binary); stream.write("RIFF", 4); // RIFF chunk write32(stream, 36 + Sound_Buffer.size() * frame_size); // RIFF chunk size in bytes stream.write("WAVE", 4); // WAVE chunk stream.write("fmt ", 4); // fmt chunk write32(stream, 16); // size of fmt chunk write16(stream, 1); // Format = PCM write16(stream, 2); // # of Channels write32(stream, samplerate); // Sample Rate write32(stream, samplerate * frame_size); // Byte rate write16(stream, frame_size); // Frame size write16(stream, 16); // Bits per sample stream.write("data", 4); // data chunk write32(stream, Sound_Buffer.size() * frame_size); // data chunk size in bytes for (int i = 0; i < Sound_Buffer.size(); i++) write16(stream, Sound_Buffer[i]); } mydsp_data_t *data = (mydsp_data_t *)dsp_state->plugindata; if (data->buffer) { free(data->buffer); } free(data); } return FMOD_OK; }
Тут вот еще какая хрень вылезла... я записываю видео ввиде отдельных фреймов и одновременно пишу звук. Все работает, но звук получается примерно на 10% длиннее видео...
Я вставил таймер - видео пишется правильно. 30 фреймов в секунду. Длительность совпадает. Звук - длиннее. Получается свести их никак невозможно.
Может у кого есть соображения из-за чего это может быть и как это исправить? Код для записи звука я показал выше. system->update вызывается одновременно с записью фрейма, т.е. те же 30 раз в секунду.
san
> звук получается примерно на 10% длиннее видео
Пишешь ты к примеру как 44100 hz, а коллбэк микширования как отрабатывает?
Может 48000?
Тема в архиве.