使用 ofstream 写入二进制数据时出现问题

发布于 2024-10-17 09:35:21 字数 2197 浏览 2 评论 0原文

大家好,我正在编写一个应用程序,它将麦克风输入记录到 WAV 文件中。以前,我编写了这个来填充指定大小的缓冲区,并且效果很好。现在,我希望能够录制任意长度。这就是我想要做的:

  • 设置 32 个小型音频缓冲区(循环缓冲)
  • 使用 ofstream 启动一个 WAV 文件 -- 将 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;

}

如果这里有任何内容看起来会导致错误数据附加到文件中,请告诉我。如果没有,我将三次检查输入数据(在回调中)。该数据应该很好,因为如果我将其复制到更大的缓冲区(例如,两分钟)然后将其保存出来,它就可以工作。

Hey all, I'm writing an application which records microphone input to a WAV file. Previously, I had written this to fill a buffer of a specified size and that worked fine. Now, I'd like to be able to record to an arbitrary length. Here's what I'm trying to do:

  • Set up 32 small audio buffers (circular buffering)
  • Start a WAV file with ofstream -- write the header with PCM length set to 0
  • Add a buffer to input
  • When a buffer completes, append its data to the WAV file and update the header; recycle the buffer
  • When the user hits "stop", write the remaining buffers to file and close

It kind of works in that the files are coming out to the correct length (header and file size and are correct). However, the data is wonky as hell. I can make out a semblance of what I said -- and the timing is correct -- but there's this repetitive block of distortion. It basically sounds like only half the data is getting into the file.

Here are some variables the code uses (in header)

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

Here is the code that prepares the file:

// 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);

}

Here's the code that receives the buffer. This assumes the incoming data is correct -- it should be, but I haven't ruled out that it isn't.

// 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;

}

And this is the code that closes the file:

// 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;

}

If there's anything here that looks like it would result in bad data getting appended to the file, please let me know. If not, I'll triple check the input data (in the callback). This data should be good, because it works if I copy it to a larger buffer (eg, two minutes) and then save that out.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

萝莉病 2024-10-24 09:35:21

我只是想知道,如何

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 是一个指针,所以不知道为什么在 write 中使用 &

I am just wondering, how

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

// (...)
    mPcmBufferPosition = 0;

}

works (btw. sizeof int16_t is always 2). Are you setting mPcmBufferPosition somewhere else?

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);
}

Also mPcmBuffer is a pointer, so don't know why you use & in write.

何其悲哀 2024-10-24 09:35:21

最可能的原因是您从缓冲区指针的地址写入,而不是从缓冲区本身写入。抛弃“&”在最后的mFile.write中。 (如果你的缓冲区分配在附近,并且你碰巧抓住了其中的一块,那么它可能有一些好的数据,但这只是运气,你的写入碰巧与缓冲区重叠)

一般来说,如果你发现自己处于这种情况,您可以尝试考虑如何独立于记录代码来测试此代码:设置一个包含值 0..255 的缓冲区,然后将“块大小”设置为 16 并查看它是否写出跨 16 个独立写入操作的 0..255 连续序列。这将快速验证您的缓冲代码是否正常工作。

The most likely reason is you're writing from the address of the pointer to your buffer, not from the buffer itself. Ditch the "&" in the final mFile.write. (It may have some good data in it if your buffer is allocated nearby and you happen to grab a chunk of it, but that's just luck that your write hapens to overlap your buffer)

In general, if you find yourself in this sort of situation, you could try to think how you can test this code in isolation from the recording code: Set up a buffer that has the values 0..255 in it, and then set your "chunk size" to 16 and see if it writes out a continuous sequence of 0..255 across 16 separate write operations. That will quickly verify if your buffering code is working or not.

撧情箌佬 2024-10-24 09:35:21

我不会调试您的代码,但会尝试为您提供您可以尝试检查并确定错误所在的事项的清单:

  • 始终手边有参考记录器或播放器。它可以是像 Windows 录音机、Audacity 或 Adob​​e Audition 这样简单的东西。拥有一个您确信能够正确录制和播放文件的录音机/播放器。
  • 使用您的应用程序录制文件并尝试使用参考播放器播放它。在职的?
  • 尝试使用参考录音机录制该文件,然后用播放器播放。在职的?
  • 当您将声音数据写入录音机中的 WAV 文件时,请将其写入一个额外的文件。使用播放器以 RAW 模式打开该文件(Windows 录音机在这里还不够)。播放正确吗?
  • 在播放器中播放文件并写入声卡时,将输出写入 RAW 文件,以查看是否正确播放数据或者是否存在声卡问题。播放正确吗?

尝试所有这些,您就会更好地了解哪里出了问题。

I won't debug your code, but will try to give you checklist of the things you can try to check and determine where's the error:

  • always have referent recorder or player handy. It can be something as simple as Windows Sound Recorder, or Audacity, or Adobe Audition. Have a recorder/player that you are CERTAIN that will record and play files correctly.
  • record the file with your app and try to play it with reference player. Working?
  • try to record the file with reference recorder, and play it with your player. Working?
  • when you write SOUND data to the WAV file in your recorder, write it to one extra file. Open that file in RAW mode with the player (Windows Sound Recorder won't be enough here). Does it play correctly?
  • when playing the file in your player, and writing to the soundcard, write output to the RAW file, to see if you are playing the data correctly at all or you have soundcars issues. Does it play correctly?

Try all this, and you'll have much better idea of where something went wrong.

踏月而来 2024-10-24 09:35:21

好吧,抱歉——工​​作到很晚,今天有点休息。我忘了向你们展示实际的回调。就是这样:

// 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;

    }

}

将尝试你们的建议和报告。

Shoot, sorry -- had a late night of work and am a bit off today. I forgot to show y'all the actual callback. This is it:

// 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;

    }

}

Will try y'alls advice and report.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文