glBeginQuery GL_OUT_OF_MEMORY error - c++

I'm using glQuery for getting info about FPS count in my application:
CollectDataBegin();
/*all drawing operations with OpenGL*/
CollectDataEnd();
Where:
void RenderingInfo::CollectDataBegin()
{
//FPS begin
available = 0;
GLenum eError;
// UPDATE 1 START
if (!bQueryGenerated){
glGenQueries(1, queries);
bQueryGenerated = true;
}
// UPDATE 1 END
//GL_NO_ERROR from glGetError();
glBeginQuery(GL_TIME_ELAPSED, queries[0]);
//GL_OUT_OF_MEMORY error from glGetError();
//FPS end
}
void RenderingInfo::CollectDataEnd()
{
//FPS begin
glEndQuery(GL_TIME_ELAPSED);
iFramesCount++;
if (iFramesCount == 20)
{
iFramesCount = 0;
while (!available) {
glGetQueryObjectiv(queries[0], GL_QUERY_RESULT_AVAILABLE, &available);
}
glGetQueryObjectui64v(queries[0], GL_QUERY_RESULT, &timeElapsed);
float jeb = static_cast<float>(timeElapsed) / std::pow(10, 9);
xRenderStats.fFPS = static_cast<float>(1.0 / jeb);
sFPS = std::to_string(xRenderStats.fFPS);
// UPDATE 1 START
if (bQueryGenerated){
glDeleteQueries(1, queries);
bQueryGenerated = false;
}
// UPDATE 1 END
}
}
Private members of RenderingInfo class:
GLuint queries[] = {0};
GLint available = 0;
GLuint64 timeElapsed;
int iFramesCount = 0;
bool bQueryGenerated = false; //UPADTE 1
I wrote and tested this code using Nvidia GeForce GTX760 with newest drivers and I had no problems at all.
But after switching to my integrated Intel HD Graphics 4600 I'm recieving GL_OUT_OF_MEMORY after calling glBeginQuery(). Interesting thing is that I'm not getting this error right away but after making some calls to glBeginQuery().
I wasn't been able to find any posts related to this matter so I'm asking for Your help is solving this issue.
UPDATE 1:
I modified my code accoring to #Ike advices, but I'm still recieving an GL_OUT_OF_MEMORY error.

After removing the code associated with glQuery my app no longer produces GL_OUT_OF_MEMORY errors. Since I was using this funcionality for counting time in which single frame is rendered, I replaced it with more reliable method:
void RenderingInfo::CollectDataBegin()
{
//FPS begin
ctTimeBegin = clock();
//FPS end
}
void RenderingInfo::CollectDataEnd()
{
//FPS begin
ctTimeEnd = clock();
dElapsedTime += (static_cast<double>((ctTimeEnd - ctTimeBegin))/CLOCKS_PER_SEC);
iFramesCount++;
if (iFramesCount == 20)
{
if ((dElapsedTime / iFramesCount) < (1.0 / CLOCKS_PER_SEC)){
xRenderStats.fFPS = 60.0f;
}
else{
xRenderStats.fFPS = static_cast<float>(iFramesCount / dElapsedTime);
}
sFPS = std::to_string(xRenderStats.fFPS);
dElapsedTime = 0.0;
iFramesCount = 0;
}
//FPS end
}
Private RenderingInfo members:
int iFramesCount =0;
clock_t ctTimeBegin = 0;
clock_t ctTimeEnd = 0;
double dElapsedTime = 0.0;
This is not the answer for a question why I received GL_OUT_OF_MEMORY, but it's for showing a possible way out when someone will end up with the similar problem.

Related

What's the difference between initializing a vector in Class Header or Class constructor body?

I encountered a strange behavior in my C++ program that I don't understand and I don't know how to search for more information. So I ask for advice here hoping someone might know.
I have a class Interface that has a 2 dimensional vector that I initialize in the header :
class Interface {
public:
// code...
const unsigned short int SIZE_X_ = 64;
const unsigned short int SIZE_Y_ = 32;
std::vector<std::vector<bool>> screen_memory_ =
std::vector<std::vector<bool>>(SIZE_X_, std::vector<bool>(SIZE_Y_, false));
// code...
};
Here I expect that I have a SIZE_X_ x SIZE_Y_ vector filled with false booleans.
Later in my program I loop at a fixed rate like so :
void Emulator::loop() {
const milliseconds intervalPeriodMillis{static_cast<int>((1. / FREQ) * 1000)};
//Initialize the chrono timepoint & duration objects we'll be //using over & over inside our sleep loop
system_clock::time_point currentStartTime{system_clock::now()};
system_clock::time_point nextStartTime{currentStartTime};
while (!stop) {
currentStartTime = system_clock::now();
nextStartTime = currentStartTime + intervalPeriodMillis;
// ---- Stuff happens here ----
registers_->trigger_timers();
interface_->toogle_buzzer();
interface_->poll_events();
interface_->get_keys();
romParser_->step();
romParser_->decode();
// ---- ------------------ ----
stop = stop || interface_->requests_close();
std::this_thread::sleep_until(nextStartTime);
}
}
But then during the execution I get a segmentation fault
[1] 7585 segmentation fault (core dumped) ./CHIP8 coin.ch8
I checked with the debugger and some part of the screen_memory_ cannot be accessed anymore. And it seems to happen at random time.
But when I put the initialization of the vector in the constructor body like so :
Interface::Interface(const std::shared_ptr<reg::RegisterManager> & registers, bool hidden)
: registers_(registers) {
// code ...
screen_memory_ =
std::vector<std::vector<bool>>(SIZE_X_, std::vector<bool>(SIZE_Y_, false));
// code ...
}
The segmentation fault doesn't happen anymore. So the solution is just to initialize the vector in the constructor body.
But why ? what is happening there ?
I don't understand what I did wrong, I'm sure someone knows.
Thanks for your help !
[Edit] I found the source of the bug (Or at least what to change so it doesnt give me a segfault anymore).
In my class Interface I use the SDL and SDL_audio libraries to create the display and the buzzer sound. Have a special look where I set the callback want_.callback, the callback Interface::forward_audio_callback and Interface::audio_callback. Here's the code :
// (c) 2021 Maxandre Ogeret
// Licensed under MIT License
#include "Interface.h"
Interface::Interface(const std::shared_ptr<reg::RegisterManager> & registers, bool hidden)
: registers_(registers) {
if (SDL_Init(SDL_INIT_AUDIO != 0) || SDL_Init(SDL_INIT_VIDEO) != 0) {
throw std::runtime_error("Unable to initialize rendering engine.");
}
want_.freq = SAMPLE_RATE;
want_.format = AUDIO_S16SYS;
want_.channels = 1;
want_.samples = 2048;
want_.callback = Interface::forward_audio_callback;
want_.userdata = &sound_userdata_;
if (SDL_OpenAudio(&want_, &have_) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to open audio: %s", SDL_GetError());
}
if (want_.format != have_.format) {
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to get the desired AudioSpec");
}
window = SDL_CreateWindow("CHIP8", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
SIZE_X_ * SIZE_MULTIPLIER_, SIZE_Y_ * SIZE_MULTIPLIER_,
hidden ? SDL_WINDOW_HIDDEN : 0);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
bpp_ = SDL_GetWindowSurface(window)->format->BytesPerPixel;
SDL_Delay(1000);
// screen_memory_ = std::vector<std::vector<bool>>(SIZE_X_, std::vector<bool>(SIZE_Y_, false));
}
Interface::~Interface() {
SDL_CloseAudio();
SDL_DestroyWindow(window);
SDL_Quit();
}
// code ...
void Interface::audio_callback(void * user_data, Uint8 * raw_buffer, int bytes) {
audio_buffer_ = reinterpret_cast<Sint16 *>(raw_buffer);
sample_length_ = bytes / 2;
int & sample_nr(*(int *) user_data);
for (int i = 0; i < sample_length_; i++, sample_nr++) {
double time = (double) sample_nr / (double) SAMPLE_RATE;
audio_buffer_[i] = static_cast<Sint16>(
AMPLITUDE * (2 * (2 * floor(220.0f * time) - floor(2 * 220.0f * time)) + 1));
}
}
void Interface::forward_audio_callback(void * user_data, Uint8 * raw_buffer, int bytes) {
static_cast<Interface *>(user_data)->audio_callback(user_data, raw_buffer, bytes);
}
}
In the function Interface::audio_callback, replacing the class variable assignation :
sample_length_ = bytes / 2;
By an int creation and assignation :
int sample_length = bytes / 2;
which gives :
void Interface::audio_callback(void * user_data, Uint8 * raw_buffer, int bytes) {
audio_buffer_ = reinterpret_cast<Sint16 *>(raw_buffer);
int sample_length = bytes / 2;
int &sample_nr(*(int*)user_data);
for(int i = 0; i < sample_length; i++, sample_nr++)
{
double time = (double)sample_nr / (double)SAMPLE_RATE;
audio_buffer_[i] = (Sint16)(AMPLITUDE * sin(2.0f * M_PI * 441.0f * time)); // render 441 HZ sine wave
}
}
The class variable sample_length_ is defined and initialized as private in the header like so :
int sample_length_ = 0;
So I had an idea and I created the variable sample_length_ as public and it works ! So the problem was definitely a scope problem of the class variable sample_length_. But it doesn't explain why the segfault disappeared when I moved the init of some other variable in the class constructor... Did I hit some undefined behavior with my callback ?
Thanks for reading me !

Clamp framerate in Windows

I have a simple loop
LARGE_INTEGER ticks_per_second;
::QueryPerformanceFrequency(&ticks_per_second);
MSG msg = { 0 };
while (true)
{
if (msg.message == WM_QUIT)
exit(0);
if (::PeekMessageW(&msg, NULL, 0U, 0U, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessageW(&msg);
continue;
}
static double last_time_s = 0;
LARGE_INTEGER cur_time_li;
::QueryPerformanceCounter(&cur_time_li);
double cur_time_s = (double)cur_time_li.QuadPart / (double)ticks_per_second.QuadPart;
double diff_s = cur_time_s - last_time_s;
double rate_s = 1 / 30.0f;
uint32_t slept_ms = 0;
if (diff_s < rate_s)
{
slept_ms = (uint32_t)((rate_s - diff_s) * 1000.0);
::Sleep(slept_ms);
}
update();
::printf("updated %f %u\n", diff_s, slept_ms);
last_time_s = cur_time_s;
}
And want update() to be called 30 times per second, but not more often
With this code it goes wrong, in console I getting something like this:
updated 0.031747 1
updated 0.001997 31
updated 0.031912 1
updated 0.001931 31
updated 0.031442 1
updated 0.002084 31
Which is seems to be correct only for first update, second one called too fast, and I can't understand why
I understand that update, PeekMessageW and etc. also wasting time, but even if I create a while (true) loop and comment update() out, it's still printing similar result
I using DirectX 11 with vsync turned off for rendering (rendering inside update function):
g_pSwapChain->Present(0, 0);
How do I fix code to make update() stable called 30 times in one second?
I don't think casting to double is good idea.I would run something like this:
static LARGE_INTEGER last_time_s = { 0 };
::QueryPerformanceCounter(&cur_time_li);
time_diff_microsec.QuadPart = cur_time_li.QuadPart - last_time_s.QuadPart;
// To avoid precision lost, convert to seconds *before* dividing by ticks-per-second.
time_diff_microsec.QuadPart *= 1000000;
time_diff_microsec.QuadPart /= ticks_per_second.QuadPart;
double rate_s = 1 / 30.0f;
uint32_t slept_ms = 0;
if (time_diff_microsec.QuadPart >= rate_s)// if (diff_s < rate_s)
{
// slept_ms = (uint32_t)(rate_s - time_diff_microsec.LowPart);// *1000.0);
// ::Sleep(slept_ms);
//}
//update();
::printf("updated %lld %u\n", time_diff_microsec.QuadPart, slept_ms);
}
last_time_s.QuadPart = time_diff_microsec.QuadPart/ 1000000;
}
Just brief "sketch". Not verified that calculations are correct though.

Waiting-time of thread switches systematicly between 0 and 30000 microseconds for the same task

I'm writing a little Console-Game-Engine and for better performance I wanted 2 threads (or more but 2 for this task) using two buffers. One thread is drawing the next frame in the first buffer while the other thread is reading the current frame from the second buffer. Then the buffers get swapped.
Of cause I can only swap them if both threads finished their task and the drawing/writing thread happened to be the one waiting. But the time it is waiting systematicly switches more or less between two values, here a few of the messurements I made (in microseconds):
0, 36968, 0, 36260, 0, 35762, 0, 38069, 0, 36584, 0, 36503
It's pretty obvious that this is not a coincidence but I wasn't able to figure out what the problem was as this is the first time I'm using threads.
Here the code, ask for more if you need it, I think it's too much to post it all:
header-file (Manager currently only adds a pointer to my WinAppBase-class):
class SwapChain : Manager
{
WORD *pScreenBuffer1, *pScreenBuffer2, *pWritePtr, *pReadPtr, *pTemp;
bool isRunning, writingFinished, readingFinished, initialized;
std::mutex lockWriting, lockReading;
std::condition_variable cvWriting, cvReading;
DWORD charsWritten;
COORD startPosition;
int screenBufferWidth;
// THREADS (USES NORMAL THREAD AS SECOND THREAD)
void ReadingThread();
// THIS FUNCTION IS ONLY FOR INTERN USE
void SwapBuffers();
public:
// USE THESE TO CONTROL WHEN THE BUFFERS GET SWAPPED
void BeginDraw();
void EndDraw();
// PUT PIXEL | INLINED FOR BETTER PERFORMANCE
inline void PutPixel(short xPos, short yPos, WORD color)
{
this->pWritePtr[(xPos * 2) + yPos * screenBufferWidth] = color;
this->pWritePtr[(xPos * 2) + yPos * screenBufferWidth + 1] = color;
}
// GENERAL CONTROL OVER SWAP CHAIN
void Initialize();
void Run();
void Stop();
// CONSTRUCTORS
SwapChain(WinAppBase * pAppBase);
virtual ~SwapChain();
};
Cpp-file
SwapChain::SwapChain(WinAppBase * pAppBase)
:
Manager(pAppBase)
{
this->isRunning = false;
this->initialized = false;
this->pReadPtr = NULL;
this->pScreenBuffer1 = NULL;
this->pScreenBuffer2 = NULL;
this->pWritePtr = NULL;
this->pTemp = NULL;
this->charsWritten = 0;
this->startPosition = { 0, 0 };
this->readingFinished = 0;
this->writingFinished = 0;
this->screenBufferWidth = this->pAppBase->screenBufferInfo.dwSize.X;
}
SwapChain::~SwapChain()
{
this->Stop();
if (_CrtIsValidHeapPointer(pReadPtr))
delete[] pReadPtr;
if (_CrtIsValidHeapPointer(pScreenBuffer1))
delete[] pScreenBuffer1;
if (_CrtIsValidHeapPointer(pScreenBuffer2))
delete[] pScreenBuffer2;
if (_CrtIsValidHeapPointer(pWritePtr))
delete[] pWritePtr;
}
void SwapChain::ReadingThread()
{
while (this->isRunning)
{
this->readingFinished = 0;
WriteConsoleOutputAttribute(
this->pAppBase->consoleCursor,
this->pReadPtr,
this->pAppBase->screenBufferSize,
this->startPosition,
&this->charsWritten
);
memset(this->pReadPtr, 0, this->pAppBase->screenBufferSize);
this->readingFinished = true;
this->cvWriting.notify_all();
if (!this->writingFinished)
{
std::unique_lock<std::mutex> lock(this->lockReading);
this->cvReading.wait(lock);
}
}
}
void SwapChain::SwapBuffers()
{
this->pTemp = this->pReadPtr;
this->pReadPtr = this->pWritePtr;
this->pWritePtr = this->pTemp;
this->pTemp = NULL;
}
void SwapChain::BeginDraw()
{
this->writingFinished = false;
}
void SwapChain::EndDraw()
{
TimePoint tpx1, tpx2;
tpx1 = Clock::now();
if (!this->readingFinished)
{
std::unique_lock<std::mutex> lock2(this->lockWriting);
this->cvWriting.wait(lock2);
}
tpx2 = Clock::now();
POST_DEBUG_MESSAGE(std::chrono::duration_cast<std::chrono::microseconds>(tpx2 - tpx1).count(), "EndDraw wating time");
SwapBuffers();
this->writingFinished = true;
this->cvReading.notify_all();
}
void SwapChain::Initialize()
{
if (this->initialized)
{
POST_DEBUG_MESSAGE(Result::CUSTOM, "multiple initialization");
return;
}
this->pScreenBuffer1 = (WORD *)malloc(sizeof(WORD) * this->pAppBase->screenBufferSize);
this->pScreenBuffer2 = (WORD *)malloc(sizeof(WORD) * this->pAppBase->screenBufferSize);
for (int i = 0; i < this->pAppBase->screenBufferSize; i++)
{
this->pScreenBuffer1[i] = 0x0000;
}
for (int i = 0; i < this->pAppBase->screenBufferSize; i++)
{
this->pScreenBuffer2[i] = 0x0000;
}
this->pWritePtr = pScreenBuffer1;
this->pReadPtr = pScreenBuffer2;
this->initialized = true;
}
void SwapChain::Run()
{
this->isRunning = true;
std::thread t1(&SwapChain::ReadingThread, this);
t1.detach();
}
void SwapChain::Stop()
{
this->isRunning = false;
}
This is where I run the SwapChain-class from:
void Application::Run()
{
this->engine.graphicsmanager.swapChain.Initialize();
Sprite<16, 16> sprite(&this->engine);
sprite.LoadSprite("engine/resources/TestData.xml", "root.test.sprites.baum");
this->engine.graphicsmanager.swapChain.Run();
int a, b, c;
for (int i = 0; i < 60; i++)
{
this->engine.graphicsmanager.swapChain.BeginDraw();
for (c = 0; c < 20; c++)
{
for (a = 0; a < 19; a++)
{
for (b = 0; b < 10; b++)
{
sprite.Print(a * 16, b * 16);
}
}
}
this->engine.graphicsmanager.swapChain.EndDraw();
}
this->engine.graphicsmanager.swapChain.Stop();
_getch();
}
The for-loops above simply draw the sprite 20 times from the top-left corner to the bottom-right corner of the console - the buffers don't get swapped during that, and that again for a total of 60 times (so the buffers get swapped 60 times).
sprite.Print uses the PutPixel function of SwapChain.
Here the WinAppBase (which consits more or less of global-like variables)
class WinAppBase
{
public:
// SCREENBUFFER
CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
long screenBufferSize;
// CONSOLE
DWORD consoleMode;
HWND consoleWindow;
HANDLE consoleCursor;
HANDLE consoleInputHandle;
HANDLE consoleHandle;
CONSOLE_CURSOR_INFO consoleCursorInfo;
RECT consoleRect;
COORD consoleSize;
// FONT
CONSOLE_FONT_INFOEX fontInfo;
// MEMORY
char * pUserAccessDataPath;
public:
void reload();
WinAppBase();
virtual ~WinAppBase();
};
There are no errors, simply this alternating waitng time.
Maybe you'd like to start by looking if I did the synchronisation of the threads correctly? I'm not exactly sure how to use a mutex or condition-variables so it might comes from that.
Apart from that it is working fine, the sprites are shown as they should.
The clock you are using may have limited resolution. Here is a random example of a clock provided by Microsoft with 15 ms (15000 microsecond) resolution: Why are .NET timers limited to 15 ms resolution?
If one thread is often waiting for the other, it is entirely possible (assuming the above clock resolution) that it sometimes waits two clockticks and sometimes none. Maybe your clock only has 30 ms resolution. We really can't tell from the code. Do you get more precise measurements elsewhere with this clock?
There are also other systems in play such as the OS scheduler or whatever controls your std::threads. That one is (hopefully) much more granular, but how all these interactions play out doesn't have to be obvious or intuitive.

Audio distorted with VST plugin

I had to plug into a pre-existing software, managing ASIO audio streams, a simple VST host. Despite of lack of some documentation, I managed to do so however once I load the plugin I get a badly distorted audio signal back.
The VST I'm using works properly (with other VST Hosts) so it's probably some kind of bug in the code I made, however when I disable the "PROCESS" from the plugin (my stream goes through the plugin, it simply does not get processed) it gets back as I sent without any noise or distortion on it.
One thing I'm slightly concerned about is the type of the data used as the ASIO driver fills an __int32 buffer while the plugins wants some float buffer.
That's really depressing as I reviewed zillions of times my code and it seems to be fine.
Here is the code of the class I'm using; please note that some numbers are temporarily hard-coded to help debugging.
VSTPlugIn::VSTPlugIn(const char* fullDirectoryName, const char* ID)
: plugin(NULL)
, blocksize(128) // TODO
, sampleRate(44100.0F) // TODO
, hostID(ID)
{
this->LoadPlugin(fullDirectoryName);
this->ConfigurePluginCallbacks();
this->StartPlugin();
out = new float*[2];
for (int i = 0; i < 2; ++i)
{
out[i] = new float[128];
memset(out[i], 0, 128);
}
}
void VSTPlugIn::LoadPlugin(const char* path)
{
HMODULE modulePtr = LoadLibrary(path);
if(modulePtr == NULL)
{
printf("Failed trying to load VST from '%s', error %d\n", path, GetLastError());
plugin = NULL;
}
// vst 2.4 export name
vstPluginFuncPtr mainEntryPoint = (vstPluginFuncPtr)GetProcAddress(modulePtr, "VSTPluginMain");
// if "VSTPluginMain" was not found, search for "main" (backwards compatibility mode)
if(!mainEntryPoint)
{
mainEntryPoint = (vstPluginFuncPtr)GetProcAddress(modulePtr, "main");
}
// Instantiate the plugin
plugin = mainEntryPoint(hostCallback);
}
void VSTPlugIn::ConfigurePluginCallbacks()
{
// Check plugin's magic number
// If incorrect, then the file either was not loaded properly, is not a
// real VST plugin, or is otherwise corrupt.
if(plugin->magic != kEffectMagic)
{
printf("Plugin's magic number is bad. Plugin will be discarded\n");
plugin = NULL;
}
// Create dispatcher handle
this->dispatcher = (dispatcherFuncPtr)(plugin->dispatcher);
// Set up plugin callback functions
plugin->getParameter = (getParameterFuncPtr)plugin->getParameter;
plugin->processReplacing = (processFuncPtr)plugin->processReplacing;
plugin->setParameter = (setParameterFuncPtr)plugin->setParameter;
}
void VSTPlugIn::StartPlugin()
{
// Set some default properties
dispatcher(plugin, effOpen, 0, 0, NULL, 0);
dispatcher(plugin, effSetSampleRate, 0, 0, NULL, sampleRate);
dispatcher(plugin, effSetBlockSize, 0, blocksize, NULL, 0.0f);
this->ResumePlugin();
}
void VSTPlugIn::ResumePlugin()
{
dispatcher(plugin, effMainsChanged, 0, 1, NULL, 0.0f);
}
void VSTPlugIn::SuspendPlugin()
{
dispatcher(plugin, effMainsChanged, 0, 0, NULL, 0.0f);
}
void VSTPlugIn::ProcessAudio(float** inputs, float** outputs, long numFrames)
{
plugin->processReplacing(plugin, inputs, out, 128);
memcpy(outputs, out, sizeof(float) * 128);
}
EDIT: Here's the code I use to interface my sw with the VST Host
// Copying the outer buffer in the inner container
for(unsigned i = 0; i < bufferLenght; i++)
{
float f;
f = ((float) buff[i]) / (float) std::numeric_limits<int>::max()
if( f > 1 ) f = 1;
if( f < -1 ) f = -1;
samples[0][i] = f;
}
// DO JOB
for(auto it = inserts.begin(); it != inserts.end(); ++it)
{
(*it)->ProcessAudio(samples, samples, bufferLenght);
}
// Copying the result back into the buffer
for(unsigned i = 0; i < bufferLenght; i++)
{
float f = samples[0][i];
int intval;
f = f * std::numeric_limits<int>::max();
if( f > std::numeric_limits<int>::max() ) f = std::numeric_limits<int>::max();
if( f < std::numeric_limits<int>::min() ) f = std::numeric_limits<int>::min();
intval = (int) f;
buff[i] = intval;
}
where "buff" is defined as "__int32* buff"
I'm guessing that when you call f = std::numeric_limits<int>::max() (and the related min() case on the line below), this might cause overflow. Have you tried f = std::numeric_limits<int>::max() - 1?
Same goes for the code snippit above with f = ((float) buff[i]) / (float) std::numeric_limits<int>::max()... I'd also subtract one there to avoid a potential overflow later on.

Pops / clicks when stopping and starting DirectX sound synth in C++ / MFC

I have made a soft synthesizer in Visual Studio 2012 with C++, MFC and DirectX. Despite having added code to rapidly fade out the sound I am experiencing popping / clicking when stopping playback (also when starting).
I copied the DirectX code from this project: http://www.codeproject.com/Articles/7474/Sound-Generator-How-to-create-alien-sounds-using-m
I'm not sure if I'm allowed to cut and paste all the code from the Code Project. Basically I use the Player class from that project as is, the instance of this class is called m_player in my code. The Stop member function in that class calls the Stop function of LPDIRECTSOUNDBUFFER:
void Player::Stop()
{
DWORD status;
if (m_lpDSBuffer == NULL)
return;
HRESULT hres = m_lpDSBuffer->GetStatus(&status);
if (FAILED(hres))
EXCEP(DirectSoundErr::GetErrDesc(hres), "Player::Stop GetStatus");
if ((status & DSBSTATUS_PLAYING) == DSBSTATUS_PLAYING)
{
hres = m_lpDSBuffer->Stop();
if (FAILED(hres))
EXCEP(DirectSoundErr::GetErrDesc(hres), "Player::Stop Stop");
}
}
Here is the notification code (with some supporting code) in my project that fills the sound buffer. Note that the rend function always returns a double between -1 to 1, m_ev_smps = 441, m_n_evs = 3 and m_ev_sz = 882. subInit is called from OnInitDialog:
#define FD_STEP 0.0005
#define SC_NOT_PLYD 0
#define SC_PLYNG 1
#define SC_FD_OUT 2
#define SC_FD_IN 3
#define SC_STPNG 4
#define SC_STPD 5
bool CMainDlg::subInit()
// initialises various variables and the sound player
{
Player *pPlayer;
SOUNDFORMAT format;
std::vector<DWORD> events;
int t, buf_sz;
try
{
pPlayer = new Player();
pPlayer->SetHWnd(m_hWnd);
m_player = pPlayer;
m_player->Init();
format.NbBitsPerSample = 16;
format.NbChannels = 1;
format.SamplingRate = 44100;
m_ev_smps = 441;
m_n_evs = 3;
m_smps = new short[m_ev_smps];
m_smp_scale = (int)pow(2, format.NbBitsPerSample - 1);
m_max_tm = (int)((double)m_ev_smps / (double)(format.SamplingRate * 1000));
m_ev_sz = m_ev_smps * format.NbBitsPerSample/8;
buf_sz = m_ev_sz * m_n_evs;
m_player->CreateSoundBuffer(format, buf_sz, 0);
m_player->SetSoundEventListener(this);
for(t = 0; t < m_n_evs; t++)
events.push_back((int)((t + 1)*m_ev_sz - m_ev_sz * 0.95));
m_player->CreateEventReadNotification(events);
m_status = SC_NOT_PLYD;
}
catch(MATExceptions &e)
{
MessageBox(e.getAllExceptionStr().c_str(), "Error initializing the sound player");
EndDialog(IDCANCEL);
return FALSE;
}
return TRUE;
}
void CMainDlg::Stop()
// stop playing
{
m_player->Stop();
m_status = SC_STPD;
}
void CMainDlg::OnBnClickedStop()
// causes fade out
{
m_status = SC_FD_OUT;
}
void CMainDlg::OnSoundPlayerNotify(int ev_num)
// render some sound samples and check for errors
{
ScopeGuardMutex guard(&m_mutex);
int s, end, begin, elapsed;
if (m_status != SC_STPNG)
{
begin = GetTickCount();
try
{
for(s = 0; s < m_ev_smps; s++)
{
m_smps[s] = (int)(m_synth->rend() * 32768 * m_fade);
if (m_status == SC_FD_IN)
{
m_fade += FD_STEP;
if (m_fade > 1)
{
m_fade = 1;
m_status = SC_PLYNG;
}
}
else if (m_status == SC_FD_OUT)
{
m_fade -= FD_STEP;
if (m_fade < 0)
{
m_fade = 0;
m_status = SC_STPNG;
}
}
}
}
catch(MATExceptions &e)
{
OutputDebugString(e.getAllExceptionStr().c_str());
}
try
{
m_player->Write(((ev_num + 1) % m_n_evs)*m_ev_sz, (unsigned char*)m_smps, m_ev_sz);
}
catch(MATExceptions &e)
{
OutputDebugString(e.getAllExceptionStr().c_str());
}
end = GetTickCount();
elapsed = end - begin;
if(elapsed > m_max_tm)
m_warn_msg.Format(_T("Warning! compute time: %dms"), elapsed);
else
m_warn_msg.Format(_T("compute time: %dms"), elapsed);
}
if (m_status == SC_STPNG)
Stop();
}
It seems like the buffer is not always sounding out when the stop button is clicked. I don't have any specific code for waiting for the sound buffer to finish playing before the DirectX Stop is called. Other than that the sound playback is working just fine, so at least I am initialising the player correctly and notification code is working in that respect.
Try replacing 32768 with 32767. Not by any means sure this is your issue, but it could overflow the positive short int range (assuming your audio is 16-bit) and cause a "pop".
I got rid of the pops / clicks when stopping playback, by filling the buffer with zeros after the fade out. However I still get pops when re-starting playback, despite filling with zeros and then fading back in (it is frustrating).