Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.8k views
in Technique[技术] by (71.8m points)

c - Stream sine wave to XAudio2

I am trying to write a very simple sine wave generator that plays out through XAudio2.

Currently there is sound playing, and if I call Win32XAudioInit() and then Win32PlayTone() a tone will play, and the tone will change on subsequent calls to Win32PlayTone(), however there is a noticeable click almost every time the tone changes.

I know there are a few reasons that could cause this:

  1. I am not keeping track of the phase-offset, which means new waves would be misaligned.
  2. I am simply updating the Memory that the buffer is pointing to without regard to what is playing.

Regarding #2, I am not sure if XAudio wants me to create a new XAUDIO2_BUFFER and resubmit that every time I change the tone, or if I am supposed to somehow keep track of where the 'playhead' is (for lack of a better term) and only update bytes that have already been played.

I know if #2 is a problem, I won't be able to hear if I fixed it I am still plagued by problem #1. I have read through XAudio2 - Play generated sine, when changing frequency clicking sound and I think I could figure out the sin wave problem if I knew XAudio2 was set up correctly.

Any thoughts would be helpful, thanks!

struct win32_audio_buffer
{
    real32 Memory[44100 * 1];  // samples per buffer (44100) * channels 1
    int BytesPerBuffer;
    XAUDIO2_BUFFER XBuffer;
    IXAudio2 *XEngine;
    IXAudio2SourceVoice *SourceVoice;
    WAVEFORMATEX WaveFormat;
};

// NOTE XAUDIO2
internal HRESULT
Win32XAudioInit(win32_audio_buffer *AudioBuffer)
{
    // Initialize a COM:
    HRESULT HRes;
    HRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if(FAILED(HRes)) { return(HRes); }
    
    // Init XAUDIO Engine
    AudioBuffer->XEngine = {};
    if (FAILED(HRes = XAudio2Create(&AudioBuffer->XEngine, 0, XAUDIO2_DEFAULT_PROCESSOR)))
    { return HRes; }
    
    // MASTER VOICE
    IXAudio2MasteringVoice* XAudioMasterVoice = nullptr;
    if (FAILED(HRes = AudioBuffer->XEngine->CreateMasteringVoice(&XAudioMasterVoice)))
    { return HRes; }
    
    AudioBuffer->WaveFormat = {};
    
    //int32 SamplesPerBuffer = 4410;
    int SampleHz = 44100;
    WORD Channels = 1;
    WORD BitsPerChannel = 32; // 4 byte samples
    int32 BufferSize = Channels * BitsPerChannel * SampleHz;
    AudioBuffer->BytesPerBuffer = SampleHz * Channels;
    
    AudioBuffer->WaveFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; // or could use WAVE_FORMAT_PCM WAVE_FORMAT_IEEE_FLOAT
    AudioBuffer->WaveFormat.nChannels = Channels;
    AudioBuffer->WaveFormat.nSamplesPerSec = SampleHz;
    AudioBuffer->WaveFormat.wBitsPerSample = BitsPerChannel; // 32
    AudioBuffer->WaveFormat.nBlockAlign = (Channels * BitsPerChannel) / 8;
    AudioBuffer->WaveFormat.nAvgBytesPerSec = SampleHz * Channels * BitsPerChannel / 8;
    AudioBuffer->WaveFormat.cbSize = 0;    // set to zero for PCM or IEEE float
    
    AudioBuffer->XBuffer.Flags = 0;
    AudioBuffer->XBuffer.AudioBytes = SampleHz * Channels * BitsPerChannel / 8;
    AudioBuffer->XBuffer.PlayBegin = 0;
    AudioBuffer->XBuffer.PlayLength = 0;
    AudioBuffer->XBuffer.LoopBegin = 0;
    AudioBuffer->XBuffer.LoopLength = 0;
    AudioBuffer->XBuffer.LoopCount = XAUDIO2_LOOP_INFINITE;
    AudioBuffer->XBuffer.pContext = NULL;
    AudioBuffer->XBuffer.pAudioData = (BYTE *)&AudioBuffer->Memory;
    
    if(FAILED(HRes = AudioBuffer->XEngine->CreateSourceVoice(&AudioBuffer->SourceVoice, (WAVEFORMATEX*)&AudioBuffer->WaveFormat))) 
    { return HRes; }
    
    if(FAILED(HRes = AudioBuffer->SourceVoice->Start(0)))
    { return HRes; }
    
    if(FAILED(HRes = AudioBuffer->SourceVoice->SubmitSourceBuffer(&AudioBuffer->XBuffer)))
    { return HRes; }
    
    return(S_OK);
}

internal HRESULT
Win32PlayTone(win32_audio_buffer *Buffer, int32 Hz)
{
    real32 PI2 = (real32)6.28318; //530718;
    
    for(int i = 0;
        i < Buffer->BytesPerBuffer;
        i++)
    {
        real32 CurrentSample = sinf(i * PI2 / 44100 * Hz);
        Buffer->Memory[i] = CurrentSample;
    }
    
    return(S_OK);
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

XAudio2 is entirely asynchronous, so you should not change the memory pointed to by a playing packet until the packet is completed or you will get clicks as you describe.

Also, when the current packet completes, you want to have another one already queued up if you want the sound to be continuous.

See these resources for learning how to program XAudio2:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...