C++: Convert float[] to unsigned char* or BYTE* - c++

I'm developing a project where I need to convert PCM 16-bits 2 channels sound into a IEEE Float 32-bits 2 channels.
To do this I'm using the following code:
void CAudioConverter::ConvI16ToF32(BYTE* pcmFrom, BYTE* floatTo, int length)
{
short* src = reinterpret_cast<short*>(pcmFrom);
float* dst = reinterpret_cast<float*>(floatTo);
for (int n = 0; n < length; n++)
{
dst[n] = static_cast<float>(src[n]) / 32768.0f;
}
}
I have initialized the variable __pcm32_bytesPerFrame with:
WAVEFORMATEX* closestFormat;
ws->default_pb_dev->GetMixFormat(&closestFormat);
__pcm32_bytesPerFrame = closestFormat->nAvgBytesPerSec * (prm->samples_per_frame * 1000 / (prm->clock_rate * closestFormat->nChannels)) / 1000;
strm->pb_max_frame_count is:
hr = ws->default_pb_dev->GetBufferSize(&ws->pb_max_frame_count);
I have a while loop in a dedicated thread the does something like:
hr = strm->default_pb_dev->GetCurrentPadding(&padding);
incoming_frame = __pcm32_bytesPerFrame / 4;
frame_to_render = strm->pb_max_frame_count - padding;
if (frame_to_render >= incoming_frame)
{
frame_to_render = incoming_frame;
} else {
/* Don't get new frame because there's no space */
frame_to_render = 0;
}
if (frame_to_render > 0)
{
pjmedia_frame frame;
hr = strm->pb_client->GetBuffer(frame_to_render, &cur_pb_buf);
if (FAILED(hr)) {
continue;
}
void* destBuffer = (void*)malloc(strm->bytes_per_frame*frame_to_render*sizeof(pj_uint16_t));
if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
/* PCM mode */
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
frame.size = strm->bytes_per_frame;
frame.timestamp.u64 = strm->pb_timestamp.u64;
frame.bit_info = 0;
frame.buf = destBuffer;
}
status = (*strm->pb_cb)(strm->user_data, &frame);
CAudioConverter* conv = new CAudioConverter();
conv->ConvI16ToF32((BYTE*)destBuffer, cur_pb_buf, frame_to_render);
hr = strm->pb_client->ReleaseBuffer(frame_to_render, 0);
(...)
But, to send the sound to the WASAPI capture buffer I need a BYTE*.
How can I fill my 'floatTo' argument?
Any ideas?
Thanks

What about this:
void CAudioConverter::ConvI16ToF32(BYTE* pcmFrom, BYTE* floatTo, int length)
{
short* src = reinterpret_cast<short*>(pcmFrom);
float* dst = reinterpret_cast<float*>(floatTo);
for (int n = 0; n < length; n++)
{
dst[n] = static_cast<float>(src[n]) / 32768.0f;
}
}
Additionally make sure length indicates the number of elments in pcmFrom and floatTo, and not the number of bytes allocated. In you case pcmFrom should have allocated length*2 bytes and floatTo needs room for length*4 bytes.

Related

libe57 read file 65536 points

I compiled the libe57 in order to use it in my project. I did it and now I'm trying to use it. (I'm on windows 10 x64)
Basing on this tutorial, I did this
int
main(int argc, char** argv)
{
char sFile[] = "PTX/file.e57";
_bstr_t bsFile = sFile; //converts Unicode to UTF-8
e57::Reader eReader((char*)bsFile);
e57::E57Root rootHeader;
eReader.GetE57Root(rootHeader);
const char* fileGuid = rootHeader.guid.c_str();
e57::DateTime fileGPSTime = rootHeader.creationDateTime;
int data3DCount = eReader.GetData3DCount();
int scanIndex = 0;
e57::Data3D scanHeader;
eReader.ReadData3D(scanIndex, scanHeader);
_bstr_t bstrName = scanHeader.name.c_str();
_bstr_t bstrGuid = scanHeader.guid.c_str();
_bstr_t bstrDesc = scanHeader.description.c_str();
int64_t nColumn = 0;
int64_t nRow = 0;
int64_t nPointsSize = 0; //Number of points
int64_t nGroupsSize = 0; //Number of groups
int64_t nCountSize = 0; //Number of points per group
bool bColumnIndex = false; //indicates that idElementName is "columnIndex"
eReader.GetData3DSizes(scanIndex, nRow, nColumn, nPointsSize, nGroupsSize, nCountSize, bColumnIndex);
int64_t nSize = nRow;
if (nSize == 0) nSize = 1024; // choose a chunk size
int8_t * isInvalidData = NULL;
if (scanHeader.pointFields.cartesianInvalidStateField)
isInvalidData = new int8_t[nSize];
double * xData = NULL;
if (scanHeader.pointFields.cartesianXField)
xData = new double[nSize];
double * yData = NULL;
if (scanHeader.pointFields.cartesianYField)
yData = new double[nSize];
double * zData = NULL;
if (scanHeader.pointFields.cartesianZField)
zData = new double[nSize];
double * intData = NULL;
bool bIntensity = false;
double intRange = 0;
double intOffset = 0;
if (scanHeader.pointFields.intensityField)
{
bIntensity = true;
intData = new double[nSize];
intRange = scanHeader.intensityLimits.intensityMaximum - scanHeader.intensityLimits.intensityMinimum;
intOffset = scanHeader.intensityLimits.intensityMinimum;
}
uint16_t * redData = NULL;
uint16_t * greenData = NULL;
uint16_t * blueData = NULL;
bool bColor = false;
int32_t colorRedRange = 1;
int32_t colorRedOffset = 0;
int32_t colorGreenRange = 1;
int32_t colorGreenOffset = 0;
int32_t colorBlueRange = 1;
int32_t colorBlueOffset = 0;
if (scanHeader.pointFields.colorRedField)
{
bColor = true;
redData = new uint16_t[nSize];
greenData = new uint16_t[nSize];
blueData = new uint16_t[nSize];
colorRedRange = scanHeader.colorLimits.colorRedMaximum - scanHeader.colorLimits.colorRedMinimum;
colorRedOffset = scanHeader.colorLimits.colorRedMinimum;
colorGreenRange = scanHeader.colorLimits.colorGreenMaximum - scanHeader.colorLimits.colorGreenMinimum;
colorGreenOffset = scanHeader.colorLimits.colorGreenMinimum;
colorBlueRange = scanHeader.colorLimits.colorBlueMaximum - scanHeader.colorLimits.colorBlueMinimum;
colorBlueOffset = scanHeader.colorLimits.colorBlueMinimum;
}
int64_t * idElementValue = NULL;
int64_t * startPointIndex = NULL;
int64_t * pointCount = NULL;
if (nGroupsSize > 0)
{
idElementValue = new int64_t[nGroupsSize];
startPointIndex = new int64_t[nGroupsSize];
pointCount = new int64_t[nGroupsSize];
if (!eReader.ReadData3DGroupsData(scanIndex, nGroupsSize, idElementValue,
startPointIndex, pointCount))
nGroupsSize = 0;
}
int32_t * rowIndex = NULL;
int32_t * columnIndex = NULL;
if (scanHeader.pointFields.rowIndexField)
rowIndex = new int32_t[nSize];
if (scanHeader.pointFields.columnIndexField)
columnIndex = new int32_t[nRow];
e57::CompressedVectorReader dataReader = eReader.SetUpData3DPointsData(
scanIndex, //!< data block index given by the NewData3D
nRow, //!< size of each of the buffers given
xData, //!< pointer to a buffer with the x data
yData, //!< pointer to a buffer with the y data
zData, //!< pointer to a buffer with the z data
isInvalidData, //!< pointer to a buffer with the valid indication
intData, //!< pointer to a buffer with the lidar return intesity
NULL,
redData, //!< pointer to a buffer with the color red data
greenData, //!< pointer to a buffer with the color green data
blueData, //!< pointer to a buffer with the color blue data
NULL,
NULL,
NULL,
NULL,
NULL,
rowIndex, //!< pointer to a buffer with the rowIndex
columnIndex //!< pointer to a buffer with the columnIndex
);
int64_t count = 0;
unsigned size = 0;
int col = 0;
int row = 0;
int cpt = 0;
while (size = dataReader.read())
{
cpt++;
}
std::cout << cpt << std::endl;
dataReader.close();
if (isInvalidData) delete isInvalidData;
if (xData) delete xData;
if (yData) delete yData;
if (zData) delete zData;
if (intData) delete intData;
if (redData) delete redData;
if (greenData) delete greenData;
if (blueData) delete blueData;
}
The problem is that everytime, the reader says that there are 65536 rows and 1 cols, meaning that my pointcloud contain always 65536 points. I tried with several files that have more than 200k points each, but the result are always the same, it writes in my XYZ file 65536 points, and no more.
EDIT 2: shorter example : the problem is still the same
#include "E57Foundation.h"
#include "E57Simple.h"
#include "comutil.h"
#include <iostream>
#include <vector>
int
main(int argc, char** argv)
{
e57::Reader eReader("PTX/file.e57");
int scanIndex = 0; //picking the first scan
e57::Data3D scanHeader; //read scan's header information
eReader.ReadData3D(scanIndex, scanHeader);
_bstr_t scanGuid = scanHeader.guid.c_str(); //get guid
int64_t nColumn = 0; //Number of Columns in a structure scan (from "indexBounds" if structure data)
int64_t nRow = 0; //Number of Rows in a structure scan
int64_t nPointsSize = 0; //Number of points
int64_t nGroupsSize = 0; //Number of groups (from "groupingByLine" if present)
int64_t nCountsSize = 0; //Number of points per group
bool bColumnIndex = false;
eReader.GetData3DSizes(scanIndex, nRow, nColumn, nPointsSize, nGroupsSize, nCountsSize, bColumnIndex);
int64_t nSize = (nRow > 0) ? nRow : 1024; //Pick a size for buffers
double *xData = new double[nSize];
double *yData = new double[nSize];
double *zData = new double[nSize];
e57::CompressedVectorReader dataReader = eReader.SetUpData3DPointsData(
scanIndex, //!< scan data index
nSize, //!< size of each of the buffers given
xData, //!< pointer to a buffer with the x data
yData, //!< pointer to a buffer with the y data
zData); //!< pointer to a buffer with the z data
//still a size of 65536
dataReader.close();
delete xData;
delete yData;
delete zData;
}

WriteJPGBuffer method using libjpeg libraries instead of ijl15.lib

We have following method implemented using ijl15.lib API.
We want to use libjpeg libraries instead of ijl. How should I implement WriteJPGBuffer using libjpeg libraries? We are aware of LoadJPG and SaveJPG from file. However i want to write and read the jpg image in buffer using libjpeg libraries. Any inputs will be very helpul. Thank you in advance.
unsigned char WriteJPGBuffer(unsigned int &size)
{
size = 0;
int jErr;
JPEG_CORE_PROPERTIES jpgProps;
bool colorsSwapped;
if (!jpgSupported)
return NULL;
jErr = ijlInit(&jpgProps);
if (jErr != IJL_OK)
return NULL;
jpgProps.DIBWidth = m_width;
jpgProps.DIBHeight = -m_height;
jpgProps.DIBBytes = (unsigned char *)m_pData;
jpgProps.DIBPadBytes = 0 ;
jpgProps.DIBChannels = 4;
jpgProps.DIBColor = IJL_RGB;
jpgProps.JPGFile = NULL;
jpgProps.JPGWidth = m_width;
jpgProps.JPGHeight = m_height;
jpgProps.JPGChannels = 3;
jpgProps.JPGColor = IJL_YCBCR;
jpgProps.JPGSubsampling = IJL_411;
jpgProps.jquality = jpgQuality;
unsigned int iSize = m_width*m_height*3;
unsigned char * pBuffer = new unsigned char[iSize];
jpgProps.JPGSizeBytes = iSize;
jpgProps.JPGBytes = pBuffer;
jpgProps.jprops.jpeg_comment_size = (unsigned short)m_strCommentAdobe.length;
jpgProps.jprops.jpeg_comment = (char*)m_strCommentAdobe;
colorsSwapped = SetInternalFormat(RGB);
jErr = ijlWrite(&jpgProps, IJL_JBUFF_WRITEWHOLEIMAGE);
if (colorsSwapped)
SetInternalFormat(BGR);
if (jErr != IJL_OK)
{
ijlFree(&jpgProps);
return NULL;
}
size = jpgProps.JPGSizeBytes;
ijlFree(&jpgProps);
return jpgProps.JPGBytes;
}
Thanks for your inputs. I have implemented the solution below through RND. In below implementation, we have image data stored in the class member variable in the form of RGBQUAD which i am converting into unsigned char* first using ConversionfromGLRGBQUADToUnsignedChar function and then writing it jpeg buffer.
void ConversionfromGLRGBQUADToUnsignedChar(unsigned char* dataInCharFromGLRGBQUAD)
{
int spot,spotDst;
for (int y = 0;y < m_height;y++)
{
for (int x = 0;x<m_width;x++)
{
spot = y * m_width + x;
spotDst = spot * 3;
dataInCharFromGLRGBQUAD[spotDst] = m_pData[spot].red;
dataInCharFromGLRGBQUAD[spotDst + 1] = m_pData[spot].green;
dataInCharFromGLRGBQUAD[spotDst + 2] = m_pData[spot].blue;
}
}
}
unsigned char * WriteJPGBuffer(unsigned int &size)
{
size = 0;
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer[1];
unsigned char* dataInCharFromGLRGBQUAD;
bool colorsSwapped;
int row_stride;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
unsigned long sizeOfJPGBuffer = 0;
jpeg_mem_dest(&cinfo, &m_pDIBData, &sizeOfJPGBuffer);
cinfo.image_width = m_width;
cinfo.image_height = m_height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
cinfo.jpeg_color_space = JCS_YCbCr;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, jpgQuality, true);
jpeg_start_compress(&cinfo, true);
colorsSwapped = SetInternalFormat(RGB);
FlipVert();
dataInCharFromGLRGBQUAD = new unsigned char[m_width*m_height*3];
ConversionfromGLRGBQUADToUnsignedChar(dataInCharFromGLRGBQUAD);
row_stride = cinfo.image_width * cinfo.input_components;
while (cinfo.next_scanline < cinfo.image_height)
{
row_pointer[0] = &dataInCharFromGLRGBQUAD[cinfo.next_scanline * row_stride];
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
if (colorsSwapped)
SetInternalFormat(BGR);
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
size = sizeOfJPGBuffer;
delete[] dataInCharFromGLRGBQUAD;
return m_pDIBData;
}

SDL Audio Pitch - Playback Rate

My goal is to connect the RPM of an engine to the pitch of an sound. I am using SDL as my audio Backend.
So my idea was to sample from the wave buffer quicker than normal. So by trail and error I am now able to pitch my engine sound "step by step".
Question #1
If I change this part from:
audioBuff += 1 + pitch * 2;
to
audioBuff += 2
I get just noise. Why? Does this have to do with stereo channels?
Question #2
How can I make this a linear pitch? Currently it's a "stepping" pitch.
Here is the full code:
#include "SDL2/SDL.h"
#include <iostream>
void audioCallback(void* userdata, Uint8 *stream, int len);
Uint8 *audioBuff = nullptr;
Uint8 *audioBuffEnd = nullptr;
Uint32 audioLen = 0;
bool quit = false;
Uint16 pitch = 0;
int main()
{
if(SDL_Init(SDL_INIT_AUDIO) < 0)
return -1;
Uint32 wavLen = 0;
Uint8 *wavBuff = nullptr;
SDL_AudioSpec wavSpec;
if(SDL_LoadWAV("test.wav", &wavSpec, &wavBuff, &wavLen) == nullptr)
{
return 1;
}
wavSpec.callback = audioCallback;
wavSpec.userdata = nullptr;
wavSpec.format = AUDIO_S16;
wavSpec.samples = 2048;
audioBuff = wavBuff;
audioBuffEnd = &wavBuff[wavLen];
audioLen = wavLen;
if( SDL_OpenAudio(&wavSpec, NULL) < 0)
{
fprintf(stderr, "Could not open audio: %s\n", SDL_GetError());
return 1;
}
SDL_PauseAudio(0);
while(!quit)
{
SDL_Delay(500);
pitch ++;
}
SDL_CloseAudio();
SDL_FreeWAV(wavBuff);
return 0;
}
Uint32 sampleIndex = 0;
void audioCallback(void* userdata, Uint8 *stream, int len)
{
Uint32 length = (Uint32)len;
length = (length > audioLen ? audioLen : length);
for(Uint32 i = 0; i < length; i++)
{
if(audioBuff > audioBuffEnd)
{
quit = true;
return;
}
// why pitch * 2?
// how to get a smooth pitch?
stream[i] = audioBuff[0];
audioBuff += 1 + pitch * 2;
fprintf(stdout, "pitch: %u\n", pitch);
}
}
You're setting the audio format to AUDIO_S16, which is "Signed 16-bit little-endian samples". Each sample is two bytes, with the first byte being the LSB. When you read the data in audioCallback, you're reading it as bytes (8 bits), then passing those bytes back to something expecting 16 bits. You're getting noise because of this, and when you use audioBuff +=2; you're always reading the LSB of the audio sample, which essentially is noise when used that way.
You should consistently use either 16 bit or 8 bit samples.

how to fill the "data field" of wavfile

Hi i am trying to record from a board and i have successfully record 4 seconds. Problem is when i try to record for more time, i got an error telling me that there not enough memory. my target is to record a 5 minutes file. Until now i have create a buffer named snIn[256] where are the samples. i send it to a big buffer of [16K * 4sec] and when it is full, i create the wav file.
#include "SAI_InOut.hpp"
#include "F746_GUI.hpp"
#include "Delay.hpp"
#include "WaveformDisplay.hpp"
#include "SDFileSystem.h"
#include "wavfile.h"
using namespace Mikami;
#define RES_STR_SIZE 0x20
#define WAVFILE_SAMPLES_PER_SECOND 16000
#define REC_TIME 4
//Create an SDFileSystem object
SDFileSystem sd("sd");
bool flag = 1;
int count = 0;
char *res_buf;
int rp = 0;
const int NUM_SAMPLES = WAVFILE_SAMPLES_PER_SECOND * REC_TIME;
Array<int16_t> my_buffer(NUM_SAMPLES);
int j = 0;
static const char *target_filename = "/sd/rectest.wav";
const int SEG_SIZE = 256;
int sent_array = 0;
int rec(const char *filename, Array<int16_t> my_buffer)
{
j = 0;
flag = 0;
sent_array = 0;
WavFileResult result;
wavfile_info_t info;
wavfile_data_t data;
WAVFILE_INFO_AUDIO_FORMAT(&info) = 1;
WAVFILE_INFO_NUM_CHANNELS(&info) = 1;
WAVFILE_INFO_SAMPLE_RATE(&info) = WAVFILE_SAMPLES_PER_SECOND;
WAVFILE_INFO_BITS_PER_SAMPLE(&info) = 16;
WAVFILE_INFO_BYTE_RATE(&info) = WAVFILE_INFO_NUM_CHANNELS(&info) * WAVFILE_INFO_SAMPLE_RATE(&info) * (WAVFILE_INFO_BITS_PER_SAMPLE(&info) / 8);
WAVFILE_INFO_BLOCK_ALIGN(&info) = 2;
WAVFILE *wf = wavfile_open(filename, WavFileModeWrite, &result);
if (result != WavFileResultOK) {
wavfile_result_string(result, res_buf, RES_STR_SIZE);
printf("%s", res_buf);
return result;
} else printf ("Open file success \r\n");
rp = 0;
WAVFILE_DATA_NUM_CHANNELS(&data) = 1;
result = wavfile_write_info(wf, &info);
if (result != WavFileResultOK) {
wavfile_result_string(result, res_buf, RES_STR_SIZE);
printf("%s", res_buf);
return result; } else printf ("Write info success \r\n");
while ( rp < NUM_SAMPLES ) {
WAVFILE_DATA_CHANNEL_DATA(&data, 0) = my_buffer[rp];
result = wavfile_write_data(wf, &data);
rp += 1;
}
if (result != WavFileResultOK) {
wavfile_result_string(result, res_buf, RES_STR_SIZE);
printf("%s", res_buf);
return result; } else printf ("Write Data file success \r\n");
result = wavfile_close(wf);
if (result != WavFileResultOK) {
wavfile_result_string(result, res_buf , RES_STR_SIZE);
printf("%s", res_buf);
return result; } else printf ("Close file success \r\n");
//UnMount the filesystem
sd.unmount();
printf("Success rec !\r\n");
return 0;
}
int main()
{
//Mount the filesystem
sd.mount();
const float MAX_DELAY = 0.5f; // 最大遅延,単位:秒
const int FS = I2S_AUDIOFREQ_16K; // 標本化周波数: 16 kHz
const uint32_t MAX_ARRAY_SIZE = (uint32_t)(MAX_DELAY*FS);
SaiIO mySai(SaiIO::BOTH, 256, FS, INPUT_DEVICE_DIGITAL_MICROPHONE_2);
Label myLabel(185, 10, "Delay System", Label::CENTER, Font16);
// ButtonGroup: "ON", "OFF"
const uint16_t BG_LEFT = 370;
const uint16_t BG_WIDTH = 100;
const uint16_t BG_HEIGHT = 45;
ButtonGroup onOff(BG_LEFT, 40, BG_WIDTH/2, BG_HEIGHT,
2, (string[]){"ON", "OFF"}, 0, 0, 2, 1);
const uint16_t SB_LEFT = BG_LEFT - 320;
const uint16_t SB_WIDTH = 270;
const uint16_t SB_Y0 = 240;
char str[20];
sprintf(str, " %3.1f [s]", MAX_DELAY);
SeekBar barDelay(SB_LEFT, SB_Y0, SB_WIDTH,
0, MAX_ARRAY_SIZE, 0, "0", "", str);
NumericLabel<float> labelDelay(SB_LEFT+SB_WIDTH/2, SB_Y0-40, "DELEY: %4.2f", 0, Label::CENTER);
DelaySystem delaySystem(MAX_ARRAY_SIZE);
WaveformDisplay displayIn(*GuiBase::GetLcdPtr(), SB_LEFT+7, 70, 256, 9,LCD_COLOR_WHITE, LCD_COLOR_CYAN,GuiBase::ENUM_BACK);
Label inLabel(SB_LEFT-30, 65, "IN");
WaveformDisplay displayOut(*GuiBase::GetLcdPtr(), SB_LEFT+7, 130, 256, 9,LCD_COLOR_WHITE, LCD_COLOR_CYAN,GuiBase::ENUM_BACK);
Label outLabel(SB_LEFT-30, 125, "OUT");
int runStop = 1;
Array<int16_t> snIn(mySai.GetLength());
Array<int16_t> snOut(mySai.GetLength());
mySai.RecordIn();
mySai.PlayOut();
mySai.PauseOut();
while (true)
{
// On/OFF
int num;
if (onOff.GetTouchedNumber(num))
if (runStop != num)
{
if (num == 0) mySai.ResumeOut();
else mySai.PauseOut();
runStop = num;
}
if (mySai.IsCompleted())
{
for (int n=0; n<mySai.GetLength() ; n++)
{
int16_t xL, xR;
mySai.Input(xL,xR);
int16_t xn = xL + xR;
snIn[n] = xn;
my_buffer[j] = xn;
j++;
if (j == NUM_SAMPLES && flag == 1) {
rec (target_filename , my_buffer); }
int16_t yn = delaySystem.Execute(xn);
mySai.Output(yn, yn);
snOut[n] = yn;
}
mySai.Reset();
displayIn.Execute(snIn);
}
}
}
I thought about a possible solution, to fill directly the "data field" of the wavefile with the snIn[256] buffer (instead of using my_buffer) again and again and at the end close the wavfile. Please let me know what you think about that and other solutions
things to note: 1) while a write operation is being performed, more data is still coming in.
At the very least I would double buffer that data, so can be writing one buffer while the other one fills.
Usually this means using an interrupt to collect the samples (into which ever buffer is currently being filed.)
the foreground program waits for the current buffer to be 'full', then initiates write operation.,
then waits again for a buffer to be 'full'
The interrupt function tracks which buffer is being filled and the current index into that buffer. When a buffer is full, set a 'global' status to let the foreground program know which buffer is ready to be written.
The foreground program writes the buffer, then resets the status for that buffer.

Direct Sound: How do I read captured data from a small buffer?

I'm trying to capture waveforms of floating point PCM data from a microphone. The application is only asking for a small number of samples each cycle (For 20'000Hz and a frame size of 0.003s, it would ask for 60 samples)
I would like to set the buffer size depending on how many ms the app is interested in but it seems that dwBufferBytes has to be a certain size. Instead, I set it to nAvgBytesPerSec and only lock/copy 60 samples each time (even though much more data would be available to read)
Is this a valid approach or is there a different way to throttle the sound driver? Is there a way to reduce the size of the buffer to only give me as much data as the app is requesting? I don't want to get a ton of sound
data if the application only wants 60 values.
Using this approach, I certainly will run into problems if the buffer catches up with my (slow) read cursor.
unsigned short channelNum = 2;
unsigned short bitsPerSample = 32;
unsigned long sampleRate = 20000;
unsigned short blockAlign = (channelNum * bitsPerSample) / 8;
unsigned long avgBytesPerSec = sampleRate * blockAlign;
WAVEFORMATEX wfx = { WAVE_FORMAT_IEEE_FLOAT, channelNum, sampleRate, avgBytesPerSec, blockAlign, bitsPerSample, 0 };
unsigned int mSampleBufferSize = 60; // 1400
DSCBUFFERDESC bufferDesc;
bufferDesc.dwSize = sizeof(DSCBUFFERDESC);
bufferDesc.dwFlags = 0;
bufferDesc.dwBufferBytes = wfx.nAvgBytesPerSec;
bufferDesc.dwReserved = 0;
bufferDesc.lpwfxFormat = &wfx;
bufferDesc.dwFXCount = 0;
bufferDesc.lpDSCFXDesc = NULL;
IDirectSoundCaptureBuffer *buffer = 0;
bool bufferRunning = false;
if (directSound && capture)
{
hr = capture->CreateCaptureBuffer(&bufferDesc, &buffer, NULL);
if (FAILED(hr))
std::cout << "SampleThread() -- Error creating DirectSoundCaptureBuffer " << endl;
else
{
hr = buffer->Start(DSCBSTART_LOOPING);
if (SUCCEEDED(hr)) {
bufferRunning = true;
}
}
}
void* primaryBuffer = NULL;
unsigned long primaryBufferSizeBytes = 0;
void* secondaryBuffer = NULL;
unsigned long secondaryBufferSize = 0;
bool mStopExecution = false;
unsigned long lastReadPosition = 0;
if (directSound && capture && buffer)
{
while (!mStopExecution)
{
DWORD readPos;
WORD remainingSize = 0;
DWORD capturePos;
hr = buffer->GetCurrentPosition(&capturePos, &readPos);
if (FAILED(hr))
{
cout << "SampleThread() -- Error GetCurrentPosition" << endl;
return 0;
}
buffer->Lock(lastReadPos, mSampleBufferSize, &primaryBuffer, &primaryBufferSizeBytes, &secondaryBuffer, &secondaryBufferSize, NULL);
memcpy(mBuffer, (float*)primaryBuffer, primaryBufferSizeBytes / sizeof(float));
// .... copy secondary buffer
hr = buffer->Unlock(primaryBuffer, primaryBufferSizeBytes, secondaryBuffer, secondaryBufferSize);
lastReadPosition = (lastReadPosition + mSampleBufferSize) % bufferDesc.dwBufferBytes;
}
}