Creating a Ring Buffer

本文介绍了一个在C++中实现的循环缓冲区类,它是任何音频引擎的基础组成部分。由于音频流通常太大而无法一次性加载到内存中,它们会以小块加载。不同计算机系统和声音硬件的速度差异很大,无法保证每次读取相同数量的数据且在精确的时间间隔内。因此,需要一种方法在需要时获取所需的数据。循环缓冲区提供了一种解决方案,它允许您使用特定大小和时间的工作集,无需担心内存分配、释放和重新分配的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Creating a Ring Buffer

This is an example of a ring buffer class in C++. A ring buffer is one of the basic building blocks of any audio engine.

Since audio streams tend to be far too large to load into memory all at once, they are typically loaded a little at a time. The speed of different computer systems and sound hardware varies considerably and there is no guarantee that a system will need to read the same amount of data every time and at precise intervals. Because of this, there needs to be a way for the sound engine to grab what it needs when it needs it.

That is why we need a ring buffer. A ring buffer is a good solution because it gives you a specific size and length of time that you can work with - you don't have to worry about with allocating, deallocating, and reallocating memory. You have one single buffer that loops around to the beginning once you've reached the end, so as long as you keep track of where you are in the buffer when reading and writing, things will run a lot more smoothly.

This code is not specific to any platform and has been compiled into programs on Linux, MacOS, and Windows. We're assuming you know how to operate a compiler and know what to do with C++ code when you see it.

Here is the header file for our ringbuffer, RingBuffer.h:


class RingBuffer
{
public:
     RingBuffer( int sizeBytes );
     ~RingBuffer();
     int Read( unsigned char* dataPtr, int numBytes );
     int Write( unsigned char *dataPtr, int numBytes );
     bool Empty( void );
     int GetSize( );
     int GetWriteAvail( );
     int GetReadAvail( );
private:
     unsigned char * _data;
     int _size;
     int _readPtr;
     int _writePtr;
     int _writeBytesAvail;
};

For this particular buffer, we're setting it up so you can only read as many bytes as you've written.

We have your basic read and write functions, along with data members to keep track of the actual data, size or the data, read position, write position, and how full the buffer is.

One thing we have not done is decide how many bytes there are in an audio frame, and in fact there's nothing specific about this buffer that would require that it be used for audio. We have also not done any locking, so any thread-safety issues will need to be handled by the programmer.


#include "RingBuffer.h"

RingBuffer::RingBuffer( int sizeBytes )
{
     _data = new unsigned char[sizeBytes];
     memset( _data, 0, sizeBytes );
     _size = sizeBytes;
     _readPtr = 0;
     _writePtr = 0;
     _writeBytesAvail = sizeBytes;
}

RingBuffer::~RingBuffer( )
{
     delete[] _data;
}

// Set all data to 0 and flag buffer as empty.
bool RingBuffer::Empty( void )
{
     memset( _data, 0, _size );
     _readPtr = 0;
     _writePtr = 0;
     _writeBytesAvail = _size;
     return true;
}

int RingBuffer::Read( unsigned char *dataPtr, int numBytes )
{
     // If there's nothing to read or no data available, then we can't read anything.
     if( dataPtr == 0 || numBytes <= 0 || _writeBytesAvail == _size )
     {
         return 0;
     }

     int readBytesAvail = _size - _writeBytesAvail;

     // Cap our read at the number of bytes available to be read.
     if( numBytes > readBytesAvail )
     {
         numBytes = readBytesAvail;
     }

     // Simultaneously keep track of how many bytes we've read and our position in the outgoing buffer
     if(numBytes > _size - _readPtr)
     {
         int len = _size-_readPtr;
         memcpy(dataPtr,_data+_readPtr,len);
         memcpy(dataPtr+len, _data, numBytes-len);
     }
     else
     {
         memcpy(dataPtr, _data+_readPtr, numBytes);
     }

     _readPtr = (_readPtr + numBytes) % _size;
     _writeBytesAvail += numBytes;

     return numBytes;
}

// Write to the ring buffer. Do not overwrite data that has not yet
// been read.
int RingBuffer::Write( unsigned char *dataPtr, int numBytes )
{
     // If there's nothing to write or no room available, we can't write anything.
     if( dataPtr == 0 || numBytes <= 0 || _writeBytesAvail == 0 )
     {
         return 0;
     }

     // Cap our write at the number of bytes available to be written.
     if( numBytes > _writeBytesAvail )
     {
         numBytes = _writeBytesAvail;
     }

     // Simultaneously keep track of how many bytes we've written and our position in the incoming buffer
     if(numBytes > _size - _writePtr)
     {
         int len = _size-_writePtr;
         memcpy(_data+_writePtr,dataPtr,len);
         memcpy(_data, dataPtr+len, numBytes-len);
     }
     else
     {
         memcpy(_data+_writePtr, dataPtr, numBytes);
     }

     _writePtr = (_writePtr + numBytes) % _size;
     _writeBytesAvail -= numBytes;

     return numBytes;
}

int RingBuffer::GetSize( void )
{
     return _size;
}

int RingBuffer::GetWriteAvail( void )
{
     return _writeBytesAvail;
}

int RingBuffer::GetReadAvail( void )
{
     return _size - _writeBytesAvail;
}

As you can see, this code is not very complicated, so if you should want to extend or modify this class it should be easy to do so. For example, you may want to change it so that you don't have to continue to write data in order to read it (for looping sound effects), or you may want to modify it to work in samples rather than bytes (typically a signed short or float data type.)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值