C++ - XAudio2 - Cracking sound when trying to play a continuous sine sound - c++

Edit: Today I found out that I only encounter this problem when I use my headphones with a cord. It's not the headphones that's the problem, because the same headphones can also be used wireless, and then the problem is gone, similar to when I use my other wireless headphones. I would prefer working with a cord though, as it has a smaller delay. So I hope someone can help me with this mystery having provided this additional information.
I have this code to play a sine wave sound. I'm constantly hearing clicking sounds when I try to play this. I'm pretty sure it's because the playback of the buffers is not going perfectly, because when you change the value of l to a larger value (for instance 44100) the clicks are further apart. I think I have followed as accurate as possible the explanation of how to use the callbacks on the Microsoft website. I create three source voices that take turns playing: one is playing while the next is ready and the one after that is being made. I use a total time (tt) to put into the sin() function, so the first byte of the next buffer should align perfectly with the last byte of the current one.
Does anyone know what's going wrong?
P.S.: The many similar questions did not answer my question. In short: I'm not modifying the playing buffer (I don't think so at least); there should be no discontinuity at the border of one buffer to another; I'm not adjusting the frequency either during playback. So I don't think this is a duplicate.
#include <xaudio2.h>
#include <iostream>
#define PI 3.14159265358979323846f
#define l 4410 //0.1 seconds
IXAudio2MasteringVoice* pMasterVoice;
IXAudio2* pXAudio2;
IXAudio2SourceVoice* pSourceVoice[3];
XAUDIO2_BUFFER buffer;
WAVEFORMATEX wfx;
XAUDIO2_VOICE_STATE state;
BYTE pDataBuffer[2*l];
BYTE bytw[2];
int pow16[2];
float w[l];
int i, p;
float tt, ampl;
class VoiceCallback : public IXAudio2VoiceCallback {
public:
HANDLE hBufferEndEvent;
VoiceCallback() : hBufferEndEvent(CreateEvent(NULL, FALSE, FALSE, NULL)) {}
~VoiceCallback() { CloseHandle(hBufferEndEvent); }
//Called when the voice has just finished playing a contiguous audio stream.
void STDMETHODCALLTYPE OnStreamEnd() {SetEvent(hBufferEndEvent);}
//Unused methods are stubs
void STDMETHODCALLTYPE OnVoiceProcessingPassEnd() {}
void STDMETHODCALLTYPE OnVoiceProcessingPassStart(UINT32 SamplesRequired) {}
void STDMETHODCALLTYPE OnBufferEnd(void * pBufferContext) {}
void STDMETHODCALLTYPE OnBufferStart(void * pBufferContext) {}
void STDMETHODCALLTYPE OnLoopEnd(void * pBufferContext) {}
void STDMETHODCALLTYPE OnVoiceError(void * pBufferContext, HRESULT Error) {}
};
VoiceCallback voiceCallback[3];
int main() {
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
pXAudio2 = nullptr;
XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
pMasterVoice = nullptr;
pXAudio2->CreateMasteringVoice(&pMasterVoice);
tt = 0, p = 660, ampl = 2000;
pow16[0] = 16;
pow16[1] = 4096;
wfx = {0};
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = (WORD)1; //mono
wfx.nSamplesPerSec = (DWORD)44100; //samplerate
wfx.wBitsPerSample = (WORD)16; //16 bit (signed)
wfx.nBlockAlign = (WORD)2; //2 bytes per sample
wfx.nAvgBytesPerSec = (DWORD)88200; //samplerate*blockalign
wfx.cbSize = (WORD)0;
i = 0;
while (true) {
for (int t = 0; t < l; t++) {
tt = (float)(t + i*l); //total time
w[t] = sin(2.f*PI*tt/p)*ampl;
int intw = (int)w[t];
if (intw < 0) {
intw += 65535;
}
bytw[0] = 0; bytw[1] = 0;
for (int k = 1; k >= 0; k--) {
//turn integer into a little endian byte array
bytw[k] += (BYTE)(16*(intw/pow16[k]));
intw -= bytw[k]*(pow16[k]/16);
bytw[k] += (BYTE)(intw/(pow16[k]/16));
intw -= (intw/(pow16[k]/16))*pow16[k]/16;
}
pDataBuffer[2*t] = bytw[0];
pDataBuffer[2*t + 1] = bytw[1];
}
buffer.AudioBytes = 2*l; //number of bytes per buffer
buffer.pAudioData = pDataBuffer;
buffer.Flags = XAUDIO2_END_OF_STREAM;
if (i > 2) {
pSourceVoice[i%3]->DestroyVoice();
}
pSourceVoice[i%3] = nullptr;
pXAudio2->CreateSourceVoice(&pSourceVoice[i%3], &wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &voiceCallback[i%3], NULL, NULL);
pSourceVoice[i%3]->SubmitSourceBuffer(&buffer);
if (i > 1) {
//wait until the current one is done playing
while (pSourceVoice[(i - 2)%3]->GetState(&state), state.BuffersQueued > 0) {
WaitForSingleObjectEx(voiceCallback[(i - 2)%3].hBufferEndEvent, INFINITE, TRUE);
}
}
if (i > 0) {
//play the next one while you're writing the one after that in the next iteration
pSourceVoice[(i - 1)%3]->Start(0);
}
i++;
}
}

If you want the sound to be 'looping', then submit multiple data-packets to the same source voice -or- set a loop value so it automatically restarts the existing audio packet. If you allow a source voice to run out of data, then you are going to hear the break in between depending upon the latency of your audio output system.
Furthermore, creating and destroy source voices is a relatively expensive operation, so doing it in a loop like this is not particular efficient.
See DirectX Tool Kit for Audio for a complete example of XAudio2 usage, as well as the latest version of the XAudio2 samples from the legacy DirectX SDK on GitHub.

Related

Method of getting current CPU utilization doesn't work reliably on Windows 10

I've been using the following method of getting current CPU utilization for the entire computer:
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
//Use a simple infinite loop to display the concept
for(;; ::Sleep(500))
{
COORD coord = {0, 0};
SetConsoleCursorPosition(hOut, coord);
int nCpuUse = get_cpu_utilization_method_01();
_tprintf(L"CPU Usage: %d\t", nCpuUse);
}
int get_cpu_utilization_method_01()
{
int nRes = -1;
FILETIME ftIdle, ftKrnl, ftUsr;
if(GetSystemTimes(&ftIdle, &ftKrnl, &ftUsr))
{
static BOOL bUsedOnce = FALSE;
static ULONGLONG uOldIdle = 0;
static ULONGLONG uOldKrnl = 0;
static ULONGLONG uOldUsr = 0;
ULONGLONG uIdle = ((ULONGLONG)ftIdle.dwHighDateTime << 32) | ftIdle.dwLowDateTime;
ULONGLONG uKrnl = ((ULONGLONG)ftKrnl.dwHighDateTime << 32) | ftKrnl.dwLowDateTime;
ULONGLONG uUsr = ((ULONGLONG)ftUsr.dwHighDateTime << 32) | ftUsr.dwLowDateTime;
//Only if we have previous values
if(bUsedOnce)
{
ULONGLONG uDiffIdle = uIdle - uOldIdle;
ULONGLONG uDiffKrnl = uKrnl - uOldKrnl;
ULONGLONG uDiffUsr = uUsr - uOldUsr;
if(uDiffKrnl + uDiffUsr)
{
//Calculate percentage
nRes = (int)((uDiffKrnl + uDiffUsr - uDiffIdle) * 100 / (uDiffKrnl + uDiffUsr));
//Check value and correct it (Just in case)
if(nRes < 0)
nRes = 0;
else if(nRes > 100)
nRes = 100;
}
}
//Remember previous data
bUsedOnce = TRUE;
uOldIdle = uIdle;
uOldKrnl = uKrnl;
uOldUsr = uUsr;
}
return nRes;
}
But since Windows 10 I'm getting reports from the users of my software that the reading provided by this method can be wrong. Some people told me that it can report values as low as half of what Windows Task Manager was showing.
I've been running my own tests for a while and it seemed to be somewhat close to the Task Manager's reading on my laptop, but when I run it on my desktop with Intel Core i7-4770 CPU # 3.40GHz with 4 physical and 8 logical cores, the reading seems to be consistently too low:
Note that I had Windows 7 installed on the same hardware (now running Windows 10) and it worked pretty reliably on Windows 7 then.
So any ideas how to make it work under Windows 10?
Try this:
#include "TCHAR.h"
#include "pdh.h"
static PDH_HQUERY cpuQuery;
static PDH_HCOUNTER cpuTotal;
void init(){
PdhOpenQuery(NULL, NULL, &cpuQuery);
PdhAddCounter(cpuQuery, L"\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal);
PdhCollectQueryData(cpuQuery);
}
double getCurrentValue(){
PDH_FMT_COUNTERVALUE counterVal;
PdhCollectQueryData(cpuQuery);
PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal);
return counterVal.doubleValue;
}

RtAudio - Playing samples from wav file

I am currently trying to learn audio programming. My goal is to open a wav file, extract everything and play the samples with RtAudio.
I made a WaveLoader class which let's me extract the samples and meta data. I used this guide to do that and I checked that everything is correct with 010 editor. Here is a snapshot of 010 editor showing the structure and data.
And this is how i store the raw samples inside WaveLoader class:
data = new short[wave_data.payloadSize]; // - Allocates memory size of chunk size
if (!fread(data, 1, wave_data.payloadSize, sound_file))
{
throw ("Could not read wav data");
}
If i print out each sample I get : 1, -3, 4, -5 ... which seems ok.
The problem is that I am not sure how I can play them. This is what I've done:
/*
* Using PortAudio to play samples
*/
bool Player::Play()
{
ShowDevices();
rt.showWarnings(true);
RtAudio::StreamParameters oParameters; //, iParameters;
oParameters.deviceId = rt.getDefaultOutputDevice();
oParameters.firstChannel = 0;
oParameters.nChannels = mAudio.channels;
//iParameters.deviceId = rt.getDefaultInputDevice();
//iParameters.nChannels = 2;
unsigned int sampleRate = mAudio.sampleRate;
// Use a buffer of 512, we need to feed callback with 512 bytes everytime!
unsigned int nBufferFrames = 512;
RtAudio::StreamOptions options;
options.flags = RTAUDIO_SCHEDULE_REALTIME;
options.flags = RTAUDIO_NONINTERLEAVED;
//&parameters, NULL, RTAUDIO_FLOAT64,sampleRate, &bufferFrames, &mCallback, (void *)&rawData
try {
rt.openStream(&oParameters, NULL, RTAUDIO_SINT16, sampleRate, &nBufferFrames, &mCallback, (void*) &mAudio);
rt.startStream();
}
catch (RtAudioError& e) {
std::cout << e.getMessage() << std::endl;
return false;
}
return true;
}
/*
* RtAudio Callback
*
*/
int mCallback(void * outputBuffer, void * inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void * userData)
{
unsigned int i = 0;
short *out = static_cast<short*>(outputBuffer);
auto *data = static_cast<Player::AUDIO_DATA*>(userData);
// if i is more than our data size, we are done!
if (i > data->dataSize) return 1;
// First time callback is called data->ptr is 0, this means that the offset is 0
// Second time data->ptr is 1, this means offset = nBufferFrames (512) * 1 = 512
unsigned int offset = nBufferFrames * data->ptr++;
printf("Offset: %i\n", offset);
// First time callback is called offset is 0, we are starting from 0 and looping nBufferFrames (512) times, this gives us 512 bytes
// Second time, the offset is 1, we are starting from 512 bytes and looping to 512 + 512 = 1024
for (i = offset; i < offset + nBufferFrames; ++i)
{
short sample = data->rawData[i]; // Get raw sample from our struct
*out++ = sample; // Pass to output buffer for playback
printf("Current sample value: %i\n", sample); // this is showing 1, -3, 4, -5 check 010 editor
}
printf("Current time: %f\n", streamTime);
return 0;
}
Inside callback function, when I print out sample values I get exactly like 010 editor? Why isnt rtaudio playing them. What is wrong here? Do I need to normalize sample values to between -1 and 1?
Edit:
The wav file I am trying to play:
Chunksize: 16
Format: 1
Channel: 1
SampleRate: 48000
ByteRate: 96000
BlockAlign: 2
BitPerSample: 16
Size of raw samples total: 2217044 bytes
For some reason it works when I pass input parameters to the openStream()
RtAudio::StreamParameters oParameters, iParameters;
oParameters.deviceId = rt.getDefaultOutputDevice();
oParameters.firstChannel = 0;
//oParameters.nChannels = mAudio.channels;
oParameters.nChannels = mAudio.channels;
iParameters.deviceId = rt.getDefaultInputDevice();
iParameters.nChannels = 1;
unsigned int sampleRate = mAudio.sampleRate;
// Use a buffer of 512, we need to feed callback with 512 bytes everytime!
unsigned int nBufferFrames = 512;
RtAudio::StreamOptions options;
options.flags = RTAUDIO_SCHEDULE_REALTIME;
options.flags = RTAUDIO_NONINTERLEAVED;
//&parameters, NULL, RTAUDIO_FLOAT64,sampleRate, &bufferFrames, &mCallback, (void *)&rawData
try {
rt.openStream(&oParameters, &iParameters, RTAUDIO_SINT16, sampleRate, &nBufferFrames, &mCallback, (void*) &mAudio);
rt.startStream();
}
catch (RtAudioError& e) {
std::cout << e.getMessage() << std::endl;
return false;
}
return true;
It was so random when I was trying to playback my mic. I left input parameters and my wav file was suddenly playing. Is this is a bug?

Detect USB hardware keylogger

I need to determine is there hardware keylogger that was plugged to PC with USB keyboard. It needs to be done via software method, from user-land. However wiki says that it is impossible to detect HKL using soft, there are several methods exists. The best and I think only one overiew that present in net relating that theme is "Detecting Hardware Keyloggers, by Fabian Mihailowitsch - youtube".
Using this overview I am developing a tool to detect USB hardware keyloggers. The sources for detecting PS/2 keyloggers was already shared by author and available here. So my task is to make it worked for USB only.
As suggested I am using libusb library to interfere with USB devices in system.
So, there are methods I had choosen in order to detect HKL:
Find USB keyboard that bugged by HKL. Note that HKL is usually
invisible from device list in system or returned by libusb.
Detect Keyghost HKL by: Interrupt read from USB HID device, send usb reset (libusb_reset_device), read interrupt again. If data returned on last read is not nulls then keylogger detected. It is described on page 45 of Mihailowitsch's presentation
Time measurement. The idea is measure time of send/receive packets using control transfer for original keyboard for thousands times. In case HKL has been plugged, program will measure time again and then compare the time with the original value. For HKL it have to be much(or not so much) greater.
Algorithm is:
Send an output report to Keyboard(as Control transfer) (HID_REPORT_TYPE_OUTPUT 0x02 )
Wait for ACKed packet
Repeat Loop (10.000 times)
Measure time
Below is my code according to steps of detection.
1. Find USB keyboard
libusb_device * UsbKeyboard::GetSpecifiedDevice(PredicateType pred)
{
if (_usbDevices == nullptr) return nullptr;
int i = 0;
libusb_device *dev = nullptr;
while ((dev = _usbDevices[i++]) != NULL)
{
struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if (r >= 0)
{
if (pred(desc))
return dev;
}
}
return nullptr;
}
libusb_device * UsbKeyboard::FindKeyboard()
{
return GetSpecifiedDevice([&](libusb_device_descriptor &desc) {
bool isKeyboard = false;
auto dev_handle = libusb_open_device_with_vid_pid(_context, desc.idVendor, desc.idProduct);
if (dev_handle != nullptr)
{
unsigned char buf[255] = "";
// product description contains 'Keyboard', usually string is 'USB Keyboard'
if (libusb_get_string_descriptor_ascii(dev_handle, desc.iProduct, buf, sizeof(buf)) >= 0)
isKeyboard = strstr((char*)buf, "Keyboard") != nullptr;
libusb_close(dev_handle);
}
return isKeyboard;
});
}
Here we're iterating through all USB devices in system and checks their Product string. In my system this string for keyboard is 'USB keyboard' (obviously).
Is it stable way to detect keyboard through Product string? Is there other ways?
2. Detect Keyghost HKL using Interrupt read
int UsbKeyboard::DetectKeyghost(libusb_device *kbdev)
{
int r, i;
int transferred;
unsigned char answer[PACKET_INT_LEN];
unsigned char question[PACKET_INT_LEN];
for (i = 0; i < PACKET_INT_LEN; i++) question[i] = 0x40 + i;
libusb_device_handle *devh = nullptr;
if ((r = libusb_open(kbdev, &devh)) < 0)
{
ShowError("Error open device", r);
return r;
}
r = libusb_set_configuration(devh, 1);
if (r < 0)
{
ShowError("libusb_set_configuration error ", r);
goto out;
}
printf("Successfully set usb configuration 1\n");
r = libusb_claim_interface(devh, 0);
if (r < 0)
{
ShowError("libusb_claim_interface error ", r);
goto out;
}
r = libusb_interrupt_transfer(devh, 0x81 , answer, PACKET_INT_LEN,
&transferred, TIMEOUT);
if (r < 0)
{
ShowError("Interrupt read error ", r);
goto out;
}
if (transferred < PACKET_INT_LEN)
{
ShowError("Interrupt transfer short read %", r);
goto out;
}
for (i = 0; i < PACKET_INT_LEN; i++) {
if (i % 8 == 0)
printf("\n");
printf("%02x, %02x; ", question[i], answer[i]);
}
printf("\n");
out:
libusb_close(devh);
return 0;
}
I've got such error on libusb_interrupt_transfer:
libusb: error [hid_submit_bulk_transfer] HID transfer failed: [5] Access denied
Interrupt read error - Input/Output Error (LIBUSB_ERROR_IO) (GetLastError() - 1168)
No clue why 'access denied', then IO error, and GetLastError() returns 1168, which means - Element not found (What element?). Looking for help here.
Time measurement. Send output report and wait for ACK packet.
int UsbKeyboard::SendOutputReport(libusb_device *kbdev)
{
const int PACKET_INT_LEN = 1;
int r, i;
unsigned char answer[PACKET_INT_LEN];
unsigned char question[PACKET_INT_LEN];
for (i = 0; i < PACKET_INT_LEN; i++) question[i] = 0x30 + i;
for (i = 1; i < PACKET_INT_LEN; i++) answer[i] = 0;
libusb_device_handle *devh = nullptr;
if ((r = libusb_open(kbdev, &devh)) < 0)
{
ShowError("Error open device", r);
return r;
}
r = libusb_set_configuration(devh, 1);
if (r < 0)
{
ShowError("libusb_set_configuration error ", r);
goto out;
}
printf("Successfully set usb configuration 1\n");
r = libusb_claim_interface(devh, 0);
if (r < 0)
{
ShowError("libusb_claim_interface error ", r);
goto out;
}
printf("Successfully claim interface\n");
r = libusb_control_transfer(devh, CTRL_OUT, HID_SET_REPORT, (HID_REPORT_TYPE_OUTPUT << 8) | 0x00, 0, question, PACKET_INT_LEN, TIMEOUT);
if (r < 0) {
ShowError("Control Out error ", r);
goto out;
}
r = libusb_control_transfer(devh, CTRL_IN, HID_GET_REPORT, (HID_REPORT_TYPE_INPUT << 8) | 0x00, 0, answer, PACKET_INT_LEN, TIMEOUT);
if (r < 0) {
ShowError("Control In error ", r);
goto out;
}
out:
libusb_close(devh);
return 0;
}
Error the same as for read interrupt:
Control Out error - Input/Output Error (LIBUSB_ERROR_IO) (GetLastError() - 1168
)
How to fix please? Also how to wait for ACK packet?
Thank you.
UPDATE:
I've spent a day on searching and debbuging. So currently my problem is only to
send Output report via libusb_control_transfer. The 2nd method with interrupt read is unnecessary to implement because of Windows denies access to read from USB device using ReadFile.
It is only libusb stuff left, here is the code I wanted to make work (from 3rd example):
// sending Output report (LED)
// ...
unsigned char buf[65];
buf[0] = 1; // First byte is report number
buf[1] = 0x80;
r = libusb_control_transfer(devh, CTRL_OUT,
HID_SET_REPORT/*0x9*/, (HID_REPORT_TYPE_OUTPUT/*0x2*/ << 8) | 0x00,
0, buf, (uint16_t)2, 1000);
...
The error I've got:
[ 0.309018] [00001c0c] libusb: debug [_hid_set_report] Failed to Write HID Output Report: [1] Incorrect function
Control Out error - Input/Output Error (LIBUSB_ERROR_IO) (GetLastError() - 1168)
This error occures right after DeviceIoControl call in libusb internals.
What means "Incorrect function" there?

Converting a short[] from SoundTouch audio library for playback

I'm attempting to use the SoundTouch C++ library for audio speed and pitch changes in an Android app. I have successfully pushed a Java byte[] array (from a .wav) through JNI, returned it, and played it back with an AudioTrack.
The next step is attempting to push a sample byte[] through the SoundTouch pipeline. I have dissected the source of the SoundStretch console program included with the library and have attempted to adapt it. I am using a stereo, 16-bit source for testing purposes.
With my current temporary setup I am ignoring the RIFF header and converting it along with the .wav data because the Java AudioTrack object does not need to read the header, it just plays raw PCM. Playing the raw byte[] without sending through SoundTouch just results in a small click where the header is.
After sending through the SoundTouch pipeline, I am playing back white noise where the beginning of the audio is supposed to be. I assume I am having a problem at the end of my write() function, where I am casting short's to signed chars. Here, the console app is writing to a file, instead of pushing to a vector:
int res = (int)fwrite(temp, 1, numBytes, fptr);
I have read the documentation for fwrite but I don't know enough about bit twiddling or audio processing to know what to do here to correctly get this information in a char[] instead of writing to a file. I know I am loosing information with the cast, but I am unsure of how to correct it.
In case anyone is extra motivated, the SoundStretch source can be found here: http://www.surina.net/soundtouch/sourcecode.html
extern "C" DLL_PUBLIC jbyteArray
Java_net_surina_soundtouch_SoundTouch_getMutatedBytes
(JNIEnv *env, jobject thiz, jbyteArray input, jint length)
{
const int BUFF_SIZE = 2048000;
SoundTouch soundTouch;
jboolean isCopy;
jbyte* ar = env->GetByteArrayElements(input, &isCopy);
signed char* cBufferIn = (signed char*)ar;
SAMPLETYPE* fBufferIn = new SAMPLETYPE[length];
vector<signed char> fBufferOut;
//converts the chars to floats per the SoundTouch console app.
convertInput16(cBufferIn, fBufferIn, length);
//channels, sampling rate, speed, pitch change
setup(&soundTouch, 2, 44100, 1.0, 0);
//transform floats from fBufferIn to fBufferout
process(&soundTouch, fBufferIn, fBufferOut, BUFF_SIZE);
signed char* res = &fBufferOut[0];
jbyteArray result = env->NewByteArray(length);
env->SetByteArrayRegion(result, 0, fBufferOut.size(), res);
LOGV("fBufferOut Size: %d", fBufferOut.size());
delete[] fBufferIn;
return result;
}
process():
static void process(SoundTouch* soundTouch, SAMPLETYPE* fBufferIn, vector<signed char>& fBufferOut, int BUFF_SIZE)
{
int nSamples = BUFF_SIZE / 2; //2 bytes per sample, using 16 bit sample for testing
int buffSizeSamples = BUFF_SIZE / 2; //2 channel stereo
soundTouch->putSamples(fBufferIn, nSamples);
do
{
nSamples = soundTouch->receiveSamples(fBufferIn, buffSizeSamples);
write(fBufferIn, fBufferOut, nSamples / 2); //2 channels
} while (nSamples != 0);
soundTouch->flush();
do
{
nSamples = soundTouch->receiveSamples(fBufferIn, buffSizeSamples);
write(fBufferIn, fBufferOut, nSamples / 2);
LOGV("NUMBER OF SAMPLES: %d", nSamples);
} while (nSamples != 0);
}
write():
static void write(const float *bufferIn, vector<signed char>& bufferOut, int numElems)
{
int numBytes;
int bytesPerSample;
if (numElems == 0) return;
bytesPerSample = 16 / 8; //16 bit test sample / bits in a byte
numBytes = numElems * bytesPerSample;
short *temp = (short*)getConvBuffer(numBytes);
switch (bytesPerSample)
{
case 2: //16 bit encoding per the SoundStretch console app
{
short *temp2 = (short *)temp;
for (int i = 0; i < numElems; i++)
{
short value = (short)saturate(bufferIn[i] * 32768.0f, -32768.0f, 32767.0f); //magic to me
temp2[i] = value; //works for little endian only.
}
break;
}
default:
assert(false);
}
for (int i = 0; i < numElems; ++i)
{
bufferOut.push_back((signed char)temp[i]); //I think my problem is here.
}
delete[] temp;
//bytesWritten += numBytes;
}
I just needed to get all the bits in char[]:
for (int i = 0; i < numElems; ++i)
{
bufferOut.push_back(temp[i] & 0xff);
bufferOut.push_back((temp[i] >> 8) & 0xff);
}

WaveOutWrite callback creates choppy audio

I have four buffers that I am using for audio playback in a synthesizer. I submit two buffers initially, and then in the callback routine I write data into the next buffer and then submit that buffer.
When I generate each buffer I'm just putting a sine wave into it whose period is a multiple of the buffer length.
When I execute I hear brief pauses between each buffer. I've increased the buffer size to 16K samples at 44100 Hz so I can clearly hear that the whole buffer is playing, but there is an interruption between each.
What I think is happening is that the callback function is only called when ALL buffers that have been written are complete. I need the synthesis to stay ahead of the playback so I need a callback when each buffer is completed.
How do people usually solve this problem?
Update: I've been asked to add code. Here's what I have:
First I connect to the WaveOut device:
// Always grab the mapped wav device.
UINT deviceId = WAVE_MAPPER;
// This is an excelent tutorial:
// http://planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=4422&lngWId=3
WAVEFORMATEX wfx;
wfx.nSamplesPerSec = 44100;
wfx.wBitsPerSample = 16;
wfx.nChannels = 1;
wfx.cbSize = 0;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
_waveChangeEventHandle = CreateMutex(NULL,false,NULL);
MMRESULT res;
res = waveOutOpen(&_wo, deviceId, &wfx, (DWORD_PTR)WavCallback,
(DWORD_PTR)this, CALLBACK_FUNCTION);
I initialize the four frames I'll be using:
for (int i=0; i<_numFrames; ++i)
{
WAVEHDR *header = _outputFrames+i;
ZeroMemory(header, sizeof(WAVEHDR));
// Block size is in bytes. We have 2 bytes per sample.
header->dwBufferLength = _codeSpec->OutputNumSamples*2;
header->lpData = (LPSTR)malloc(2 * _codeSpec->OutputNumSamples);
ZeroMemory(header->lpData, 2*_codeSpec->OutputNumSamples);
res = waveOutPrepareHeader(_wo, header, sizeof(WAVEHDR));
if (res != MMSYSERR_NOERROR)
{
printf("Error preparing header: %d\n", res - MMSYSERR_BASE);
}
}
SubmitBuffer();
SubmitBuffer();
Here is the SubmitBuffer code:
void Vodec::SubmitBuffer()
{
WAVEHDR *header = _outputFrames+_curFrame;
MMRESULT res;
res = waveOutWrite(_wo, header, sizeof(WAVEHDR));
if (res != MMSYSERR_NOERROR)
{
if (res = WAVERR_STILLPLAYING)
{
printf("Cannot write when still playing.");
}
else
{
printf("Error calling waveOutWrite: %d\n", res-WAVERR_BASE);
}
}
_curFrame = (_curFrame+1)&0x3;
if (_pointQueue != NULL)
{
RenderQueue();
_nextFrame = (_nextFrame + 1) & 0x3;
}
}
And here is my callback code:
void CALLBACK Vodec::WavCallback(HWAVEOUT hWaveOut,
UINT uMsg,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2 )
{
// Only listen for end of block messages.
if(uMsg != WOM_DONE) return;
Vodec *instance = (Vodec *)dwInstance;
instance->SubmitBuffer();
}
The RenderQueue code is pretty simple - just copies a piece of a template buffer into the output buffer:
void Vodec::RenderQueue()
{
double white = _pointQueue->White;
white = 10.0; // For now just override with a constant value
int numSamples = _codeSpec->OutputNumSamples;
signed short int *data = (signed short int *)_outputFrames[_nextFrame].lpData;
for (int i=0; i<numSamples; ++i)
{
Sample x = white * _noise->Samples[i];
data[i] = (signed short int)(x);
}
_sampleOffset += numSamples;
if (_sampleOffset >= _pointQueue->DurationInSamples)
{
_sampleOffset = 0;
_pointQueue = _pointQueue->next;
}
}
UPDATE: Mostly solved the issue. I need to increment _nextFrame along with _curFrame (not conditionally). The playback buffer was getting ahead of the writing buffer.
However, when I decrease the playback buffer to 1024 samples, it gets choppy again. At 2048 samples it is clear. This happens for both Debug and Release builds.
1024 samples is just about 23ms of audio data. wav is pretty high level API from Windows Vista onwards. If you want low-latency audio playback, you should use CoreAudio. You can get latencies down to 10 ms in shared mode and 3 ms in exclusive mode. Also, the audio depends upon the processes currently running on your system. In other words, it depends on how frequently your audio thread can run to get data. You should also look at MultiMedia Class Scheduler Service and AvSetMmThreadCharacteristics function.