ПрограммированиеФорумЗвук

Fmod, одновременное воспроизведение звуков и запись их в файл

Страницы: 1 2 Следующая »
#0
17:52, 23 июля 2020

Я не лазил глубоко в 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, т.е. в моем случае они все сидят в своих тредах и как записать суммарный звук - непонятно. Использовать для этого микрофон это дикость какая-то...

#1
21:26, 23 июля 2020

san
> Ну или более общий вопрос - как вообще на основе fmod (без интеграции с
> готовыми движками) делаются программы где одновременно воспроизводятся
> несколько звуков?

Программы на основе fmod где одновременно воспроизводятся несколько звуков используют микшер.
Микшер (условно) суммирует входные сэмплы и подает результат на выход.

Тред/каждый звук - оверкилл. Треды вообще не нужны, т.к. в основном аудиоменеджеры типа fmod имеют
свой собственный тред, где все нужные звуки микшируются, а комманды типа play() имеют асинхронное поведение.

#2
22:27, 23 июля 2020

rcsim
> Тред/каждый звук - оверкилл. Треды вообще не нужны, т.к. в основном аудиоменеджеры типа fmod имеют свой собственный тред, где все нужные звуки микшируются
Да я понимаю, но не вижу как это сделать на одном треде. И микшера я тоже не нашел. Может есть какой нибудь примерчик на fmod где микшируется несколько звуков?

#3
0:16, 24 июля 2020

san
> И микшера я тоже не нашел. Может есть какой нибудь примерчик на fmod где
> микшируется несколько звуков?

Микшер - это некая внутренняя сущность. Сам fmod и есть по сути микшер.

Без всяких тредов (т.е. в главном) просто создаешь sound1, sound2, потом подряд вызываешь
  system->playSound(sound1);
  system->playSound(sound2);

И оба звучат одновременно: вызовы асинхронные. Можно вроде callback установить на время звучания, и мониторить.
Каналы и группы - они вроде строго опциональные.

  • Ну вот пример, первое что нашел https://gist.github.com/mitchcurtis/4a004db5df944d606190d3b0f6022716
  • Он для одного звука. Модифицируй его как я сказал, будет несколько звуков.

    #4
    3:39, 24 июля 2020

    rcsim
    >Без всяких тредов (т.е. в главном) просто создаешь sound1, sound2, потом подряд вызываешь
    Спасибо, попробую (пока не за компом).

    > Микшер - это некая внутренняя сущность. Сам fmod и есть по сути микшер.
    А как до него добраться? Если я хочу записать все, что он воспроизводит?

    #5
    15:47, 24 июля 2020

    san
    Вот пример коллбэка для пользовательских звуковых эффектов.
    https://github.com/kdridi/FMODSample/blob/master/IOS/api/lowlevel… sp_custom.cpp
    В примере он режет громкость пополам, ты можешь использовать его для сохранения данных в файл.

    #6
    20:41, 24 июля 2020

    CD
    Спасибо, разбираюсь.

    Кстати оказывается у меня все же играет один тред, значит все не так страшно как я думал.  :)
    Много тредов открываются только когда я звуки загружаю. Просто я забыл что я там наворотил.

    #7
    16:21, 25 июля 2020

    Микшер - это некая внутренняя сущность. Сам fmod и есть по сути микшер.

    Микшер - это список потоков к которым применяются методы (суммирование, вычитание(по амплитуде, или времени) воспроизведение, запись, фильтры...) одного объекта (Микшер). В простейшем виде - BASS тоже микшер.
    Где то так +\-.

    #8
    19:02, 25 июля 2020

    Я использовал 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?

    #9
    13:40, 26 июля 2020

    san
    > Кто нибудь знает как можно сконвертировать массив float в формат wav?

    libsndfile

    #10
    18:35, 26 июля 2020

    rcsim
    >libsndfile
    Спасибо, но похоже это меня не спасет. Документация куцая. Либа написана на архаичном диалекте С (стандарт 1999 года) и собирается только на *ux.
    Найти как сделать то, что мне нужно, копаясь в исходниках, я не смог. Увы.

    #11
    18:59, 26 июля 2020

    Ну вот, на 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);
    }

    Вроде всё просто.

    #12
    20:09, 26 июля 2020

    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;
    }
    #13
    0:34, 27 июля 2020

    Тут вот еще какая хрень вылезла... я записываю видео ввиде отдельных фреймов и одновременно пишу звук. Все работает, но звук получается примерно на 10% длиннее видео...
    Я вставил таймер - видео пишется правильно. 30 фреймов в секунду. Длительность совпадает. Звук - длиннее. Получается свести их никак невозможно.
    Может у кого есть соображения из-за чего это может быть и как это исправить? Код для записи звука я показал выше. system->update вызывается одновременно с записью фрейма, т.е. те же 30 раз в секунду.

    #14
    0:58, 27 июля 2020

    san
    > звук получается примерно на 10% длиннее видео

    Пишешь ты к примеру как 44100 hz, а коллбэк микширования как отрабатывает?
    Может 48000?

    Страницы: 1 2 Следующая »
    ПрограммированиеФорумЗвук

    Тема в архиве.