Проблема записи двоичных данных с ofstream - PullRequest
0 голосов
/ 11 февраля 2011

Привет всем, я пишу приложение, которое записывает вход микрофона в файл WAV. Ранее я написал это, чтобы заполнить буфер указанного размера, и это работало нормально. Теперь я хотел бы иметь возможность записи произвольной длины. Вот что я пытаюсь сделать:

  • Настройка 32 небольших аудио буферов (круговая буферизация)
  • Запустите WAV-файл с помощью ofstream - напишите заголовок с длиной PCM, установленной в 0
  • Добавить буфер для ввода
  • Когда буфер завершит работу, добавьте его данные в файл WAV и обновите заголовок; перезапустить буфер
  • Когда пользователь нажимает «стоп», записать оставшиеся буферы в файл и закрыть

Это работает в том смысле, что файлы выходят с правильной длиной (заголовок и размер файла и являются правильными). Тем не менее, данные чертовски сомнительны. Я могу разобрать видимость того, что я сказал - и время выбрано правильно - но есть этот повторяющийся блок искажений. По сути, похоже, что только половина данных попадает в файл.

Вот некоторые переменные, которые использует код (в заголовке)

// File writing
ofstream mFile;
WAVFILEHEADER mFileHeader;
int16_t * mPcmBuffer;
int32_t mPcmBufferPosition;
int32_t mPcmBufferSize;
uint32_t mPcmTotalSize;
bool mRecording;

Вот код, который готовит файл:

// Start recording audio
void CaptureApp::startRecording()
{

    // Set flag
    mRecording = true;

    // Set size values
    mPcmBufferPosition = 0;
    mPcmTotalSize = 0;

    // Open file for streaming
    mFile.open("c:\my.wav", ios::binary|ios::trunc);

}

Вот код, который получает буфер. Это предполагает, что входящие данные верны - это должно быть, но я не исключаю, что это не так.

// Append file buffer to output WAV
void CaptureApp::writeData()
{

    // Update header with new PCM length
    mPcmBufferPosition *= sizeof(int16_t);
    mPcmTotalSize += mPcmBufferPosition;
    mFileHeader.bytes = mPcmTotalSize + sizeof(WAVFILEHEADER);
    mFileHeader.pcmbytes = mPcmTotalSize;
    mFile.seekp(0);
    mFile.write(reinterpret_cast<char *>(&mFileHeader), sizeof(mFileHeader));

    // Append PCM data
    if (mPcmBufferPosition > 0)
    {
        mFile.seekp(mPcmTotalSize - mPcmBufferPosition + sizeof(WAVFILEHEADER));
        mFile.write(reinterpret_cast<char *>(&mPcmBuffer), mPcmBufferPosition);
    }

    // Reset file buffer position
    mPcmBufferPosition = 0;

}

И это код, который закрывает файл:

// Stop recording
void CaptureApp::stopRecording()
{

    // Save remaining data
    if (mPcmBufferSize > 0) 
        writeData();

    // Close file
    if (mFile.is_open())
    {
        mFile.flush();
        mFile.close();
    }

    // Turn off recording flag
    mRecording = false;

}

Если здесь есть что-то, что может привести к добавлению в файл неверных данных, пожалуйста, дайте мне знать. Если нет, я проверю трижды входные данные (в обратном вызове). Эти данные должны быть хорошими, потому что они работают, если я скопирую их в больший буфер (например, две минуты), а затем сохраню.

Ответы [ 4 ]

2 голосов
/ 11 февраля 2011

Мне просто интересно, как

void CaptureApp::writeData()
{
    mPcmBufferPosition *= sizeof(int16_t); // mPcmBufferPosition = 0, so 0*2 = 0;

// (...)
    mPcmBufferPosition = 0;

}

работает (кстати, sizeof int16_t всегда 2). Вы устанавливаете mPcmBufferPosition в другом месте?

void CaptureApp::writeData()
{

    // Update header with new PCM length
    long pos = mFile.tellp();
    mPcmBufferBytesToWrite *= 2;
    mPcmTotalSize += mPcmBufferBytesToWrite;
    mFileHeader.bytes = mPcmTotalSize + sizeof(WAVFILEHEADER);
    mFileHeader.pcmbytes = mPcmTotalSize;

    mFile.seekp(0);
    mFile.write(reinterpret_cast<char *>(&mFileHeader), sizeof(mFileHeader));
    mFile.seekp(pos);

    // Append PCM data
    if (mPcmBufferBytesToWrite > 0)    
        mFile.write(reinterpret_cast<char *>(mPcmBuffer), mPcmBufferBytesToWrite);
}

Также mPcmBuffer - указатель, поэтому не знаю, почему вы используете & в записи.

1 голос
/ 11 февраля 2011

Наиболее вероятная причина - вы пишете с адреса указателя на ваш буфер, а не с самого буфера.Откажитесь от «&» в финальном mFile.write.(Он может содержать некоторые хорошие данные, если ваш буфер расположен рядом, и вы случайно захватили его, но это просто удача, что ваша запись может перекрывать ваш буфер)Оказавшись в такой ситуации, вы можете подумать, как можно протестировать этот код изолированно от кода записи: установите буфер, в котором находятся значения 0..255, а затем установите «размер куска» в 16и посмотрите, записывает ли он непрерывную последовательность 0..255 через 16 отдельных операций записи.Это быстро проверит, работает ли ваш буферный код или нет.

0 голосов
/ 11 февраля 2011

Стреляй, извини - у меня была поздняя ночь на работе, и я сегодня немного ухожу. Я забыл показать вам реальный обратный звонок. Вот оно:

// Called when buffer is full
void CaptureApp::onData(float * data, int32_t & size)
{

    // Check recording flag and buffer size
    if (mRecording && size <= BUFFER_LENGTH)
    {

        // Save the PCM data to file and reset the array if we 
        // don't have room for this buffer
        if (mPcmBufferPosition + size >= mPcmBufferSize) 
            writeData();

        // Copy PCM data to file buffer
        copy(mAudioInput.getData(), mAudioInput.getData() + size, mPcmBuffer + mPcmBufferPosition);

        // Update PCM position
        mPcmBufferPosition += size;

    }

}

Попробую все советы и доклад.

0 голосов
/ 11 февраля 2011

Я не буду отлаживать ваш код, но постараюсь дать вам контрольный список вещей, которые вы можете попытаться проверить и определить, где ошибка:

  • всегда иметь под рукой референтный рекордер или проигрыватель.Это может быть что-то простое, например, Windows Sound Recorder, Audacity или Adobe Audition.У вас есть рекордер / проигрыватель, который вы ОПРЕДЕЛЕНЫ, который будет правильно записывать и воспроизводить файлы.
  • запишите файл с вашим приложением и попробуйте воспроизвести его с помощью эталонного проигрывателя.Работаете?
  • попробуйте записать файл с помощью референсного рекордера и воспроизвести его на своем плеере.Работает?
  • когда вы записываете данные SOUND в файл WAV на вашем рекордере, запишите их в один дополнительный файл.Откройте этот файл в режиме RAW с помощью проигрывателя (Windows Sound Recorder здесь будет недостаточно).Он воспроизводится правильно?
  • при воспроизведении файла на плеере и записи на звуковую карту, записать вывод в файл RAW, чтобы проверить, правильно ли вы воспроизводите данные или у вас есть проблемы со звуковыми картами.Играет ли он правильно?

Попробуйте все это, и у вас будет гораздо лучшее представление о том, где что-то пошло не так.

...