Playing audio in Windows = Core Audio API?

DSP, Plug-in and Host development discussion.
unkow
KVRer
25 posts since 29 Sep, 2021

Post Tue Oct 26, 2021 7:38 pm

I'm getting back to programming some C after a few years of layover. Back in the day I used to program mostly on Unix (I did program a 2-operator FM VST synth in 2010-ish, the code is long lost).

Anyway, my next project will be on Windows. The plan is to start by writing a standalone program to read WAV files, mess them around a bit, then play the result. If I get that to work, I may be programming a VST next. We'll see.

For the playback bit, what Windows library would one use? The all-knowing Google is sending me to the Win32 Core Audio API -- but, I mean, *Win32*??? Surely there's something newer in the big tangle of spaghetti code that are the Windows libraries?

PeterP_swe
KVRist
120 posts since 13 Aug, 2017 from Gothenburg

Post Wed Oct 27, 2021 12:37 am

If you want to stay with native Windows APIs then best choices would be WASAPI or XAudio2.

Benutzername
KVRist
488 posts since 23 Jan, 2008 from Hamburg, Germany

Post Wed Oct 27, 2021 2:52 am

If you just want to hear the the result then a simple ShellExecute() with the output wav file name as parameter will open the default media player and play the file.

If you like to stream the audio data to your sound card yourself then I'd use a portable audio API like PortAudio (C/C++) or RTAudio (C++). These are much easier to master and they hide a lot of the quirks of the different platforms. They also automatically support all low latency APIs like ASIO, WASAPI, CoreAudio, Jack or ALSA if present.

If you want to be very fancy and plan for the future then I'd suggest that you look into JUCE. It's a popular audio framework that supports stand alone applications and several plugin formats. Setup is pretty easy too but it's C++, not C.

mystran
KVRAF
6591 posts since 12 Feb, 2006 from Helsinki, Finland

Post Wed Oct 27, 2021 4:55 am

PeterP_swe wrote:
Wed Oct 27, 2021 12:37 am
If you want to stay with native Windows APIs then best choices would be WASAPI or XAudio2.
The "best" choice is one thing, but if you just want to get some audio going with the minimum amount of trouble, there truly is nothing that beats good old DirectSound. It might not exactly be the latest or the greatest or the one that's recommended for new development, but the minimum implementation of "just give me a buffer I can write my output to" takes about 10 lines of code and just works on every Windows since NT4 or something.
Preferred pronouns would be "it/it" because according to this country, I'm a piece of human trash.

unkow
KVRer

Topic Starter

25 posts since 29 Sep, 2021

Post Thu Oct 28, 2021 1:38 pm

Thanks for the replies everybody! Much appreciated.

j_e_g
KVRist
74 posts since 4 Mar, 2010

Post Fri Oct 29, 2021 12:45 pm

Use WASAPI, and consult the complete C example I posted in the thread viewtopic.php?f=33&t=460177

xoxos
Mr Entertainment
12341 posts since 30 Apr, 2002 from i might peeramid

Post Sun Oct 31, 2021 8:45 am

isn't there some kind of reference for the/a way to implement an output stream without using a resource?

i seem to remember the routine for this in win32 took a dozen lines or so and it was transmitted to me by a user example rather than a ms resource and i pretty much didn't muck with it once established..

but can't someone assist the person without more dependencies to wear around their neck? is that just what we all do nowadays?

i have to admit, one thing i was never able to do was open a line in and line out on win32 simultaneously for processing. but i made extensive use of the output stream...
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

User avatar
BertKoor
KVRAF
13212 posts since 8 Mar, 2005 from Utrecht, Holland

Post Mon Nov 01, 2021 5:32 am

xoxos wrote:
Sun Oct 31, 2021 8:45 am
one thing i was never able to do was open a line in and line out on win32 simultaneously for processing.
Back in the days (like 2 decades ago) that was a not uncommon hardware limitation.

I recall I had two SoundBlasters installed, one of 'em was a AWE-64, the other a cheaper & newer SB-Live blabla something. One of them (don't know which anymore) just was physically unable to do full duplex. It could either record or play back. Most built-in Realtek/AC'97 soundchips had that limitation as well.

If your card could do this (worked for me with CoolEditPro for example) but not with self-written software, then it was indeed a software problem.

But this all is a long time ago, probably not so relevant anymore today.
We are the KVR collective. Resistance is futile. You will be assimilated. Image
My MusicCalc is served over https!!

mystran
KVRAF
6591 posts since 12 Feb, 2006 from Helsinki, Finland

Post Mon Nov 01, 2021 10:44 pm

BertKoor wrote:
Mon Nov 01, 2021 5:32 am
xoxos wrote:
Sun Oct 31, 2021 8:45 am
one thing i was never able to do was open a line in and line out on win32 simultaneously for processing.
Back in the days (like 2 decades ago) that was a not uncommon hardware limitation.
Well, these days that should not be the case. Even integrated crap does multi-channel input and output at the same time. :D
Preferred pronouns would be "it/it" because according to this country, I'm a piece of human trash.

xoxos
Mr Entertainment
12341 posts since 30 Apr, 2002 from i might peeramid

Post Sat Nov 06, 2021 9:18 am

it took me bloody forever to even find the reference for this
https://docs.microsoft.com/en-us/window ... -reference

(what audio dsp person thinks of "waveform" when seeking to populate a buffer?)

the documentation is impenetrable for anyone seeking to accomplish it without previous experience. based on my familiarity with "win32 programming on a 32 bit system" i do hope that at some point i'll be able to be back using the contemporary windows API for generating a sound wave by populating a buffer and using a per sample audio process to write a sine wave, which anyone would be able to understand and adapt to their realtime synthesis intentions.


i'd like to say now that this is a bastard, and microsoft is a bastard, and people who talk about it ... all the pointless "kick you in the balls" replies to desperate would be audio programmers unable to find a platform asking in all the forums, how do i open an audio stream out so i can make a noise?

there are people actively posting on these forums who would rather cut you with a knife and put their balls in it than see you do something ever.

answers to old petitioners.. "simply, you can't program a sinewave with c++ and put it to the speaker." "that sine wave is too low frequency, so you can't do it." trying to break the people. shameful as hell.

if i die and don't return (and haha) this seemingly helpful response is from 2016, still sorting out the parts that don't work in the ms compiler of today (is it 32? is it 64? maybe it will let you know) but it seems to document the appropriate API calls.
https://stackoverflow.com/questions/367 ... nd-waveout

but no i'm disgusted. i remember what i went through doing this for win32 on 32 bit os. ugh world ugh. distastefully shitty human social reality. show the native API process with a simple tangible example. make the documentation easy to find. show a simple example of the APIs in use for something as crucial to utility as audio.

but it is cool that my old win32 apps work on this 64 bit system :)
berzerker.zip
You do not have the required permissions to view the files attached to this post.
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

xoxos
Mr Entertainment
12341 posts since 30 Apr, 2002 from i might peeramid

Post Sat Nov 06, 2021 1:00 pm

here :)
windoze.zip
this makes a windows app you can drag around, resize, add your stuff to, which outputs a sine wave you should be able to hear continuously until you close the app.
You do not have the required permissions to view the files attached to this post.
Last edited by xoxos on Sat Nov 06, 2021 1:26 pm, edited 1 time in total.
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

xoxos
Mr Entertainment
12341 posts since 30 Apr, 2002 from i might peeramid

Post Sat Nov 06, 2021 1:25 pm

here's a description for the case of download not available -

i used ms visual studio community 2019 which says it's version number 16 something. create new windows project i called windoze, produces a .cpp file and a couple of headers. looks a lot like old windows mostly so good for me.

you may want to build after each change to see if your current whatever puts up with it.

in windoze.h add:

Code: Select all

float amp = 16384.f;
float phase = 0.f;
float tau = 6.283185307179586476925286766559f;
now go to windoze.cpp or equivalent. my files spawned with two include statements at the top. add just under this (are these all necessary even?)

Code: Select all

#include <Windows.h>
#include <math.h>
#include <mmsystem.h>
#include <stdio.h>
#pragma comment(lib, "Winmm.lib")
#define OUT_BUFFER_SIZE		16384
#include "audio.h"
before going on let's create a header file called the audio.h

Code: Select all

#pragma once

float denormal = 1.0e-18f;

void FillAudioBuffer(PBYTE pBuffer) {

	for (int buffercount = 0; buffercount < OUT_BUFFER_SIZE; buffercount += 4) {
		register float outl, outr;


		phase += .125f;
		while (phase > tau) phase -= tau;

		outl = outr = sin(phase) * .5f * amp;

		int aui = (int)outl;	if (aui < -32767) aui = -32767;	else if (aui > 32767) aui = 32767;
		int auj = (int)outr;	if (auj < -32767) auj = -32767;	else if (auj > 32767) auj = 32767;
		unsigned char so0 = aui, so1 = aui >> 8, so2 = auj, so3 = auj >> 8;
		pBuffer[buffercount] = so0;	pBuffer[buffercount + 1] = so1;	pBuffer[buffercount + 2] = so2;	pBuffer[buffercount + 3] = so3;
	}
	denormal = -denormal;

}
here's the audio process where you make stuff happen to the signal. the stuff at the bottom breaks the value into whatever crazy bit format the stream takes. denormal doesn't do anything here but it's like logs piled up for a dsp fire.

the rest of the stuff to add takes place in WinProc. we are adding some extra options on the switch (including a WM_CREATE because the file spawned without one.) we are also declaring variables in WinProc but before the switch statement so they have the scope of all the functions.

add this before the switch statement.

Code: Select all

    static BOOL bShutOff, bClosing;
    static HWAVEOUT hWaveOut; /* device handle */
    static PBYTE pBuffer1, pBuffer2;
    static PWAVEHDR pWaveHdr1, pWaveHdr2;
    static WAVEFORMATEX waveformat;
i think you'll see WM_COMMAND as the first switch option. squeeze this in before it:

Code: Select all

    case WM_CREATE:
        {
            if (hWaveOut == NULL) {
                pWaveHdr1 = (PWAVEHDR)malloc(sizeof(WAVEHDR));
                pWaveHdr2 = (PWAVEHDR)malloc(sizeof(WAVEHDR));
                pBuffer1 = (PBYTE)malloc(OUT_BUFFER_SIZE);
                pBuffer2 = (PBYTE)malloc(OUT_BUFFER_SIZE);
                if (!pWaveHdr1 || !pWaveHdr2 || !pBuffer1 || !pBuffer2) {
                    if (!pWaveHdr1) free(pWaveHdr1);
                    if (!pWaveHdr2) free(pWaveHdr2);
                    if (!pBuffer1)  free(pBuffer1);
                    if (!pBuffer2)  free(pBuffer2);
                    fprintf(stderr, "Error allocating memory!\n");
                    return TRUE;
                }
                bShutOff = FALSE;

                //   MMRESULT result;  /* for waveOut return values */
                waveformat.nSamplesPerSec = 44100; /* sample rate */
                waveformat.wBitsPerSample = 16;   /* sample size */
                waveformat.nChannels = 2;   /* channels  */
                waveformat.cbSize = 0; /* size of _extra_ info */
                waveformat.wFormatTag = WAVE_FORMAT_PCM;
                waveformat.nBlockAlign = (waveformat.wBitsPerSample >> 3) * waveformat.nChannels;
                waveformat.nAvgBytesPerSec = waveformat.nBlockAlign * waveformat.nSamplesPerSec;
                if (waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveformat, (DWORD)hWnd, 0, CALLBACK_WINDOW) != MMSYSERR_NOERROR) {
                    free(pWaveHdr1);	free(pWaveHdr2);	free(pBuffer1);	free(pBuffer2);
                    hWaveOut = NULL;
                    fprintf(stderr, "unable to open WAVE_MAPPER device\n");
                    ExitProcess(1);
                }
                pWaveHdr1->lpData = (LPSTR)pBuffer1;
                pWaveHdr1->dwBufferLength = OUT_BUFFER_SIZE;
                pWaveHdr1->dwBytesRecorded = 0;
                pWaveHdr1->dwUser = 0;
                pWaveHdr1->dwFlags = 0;
                pWaveHdr1->dwLoops = 1;
                pWaveHdr1->lpNext = NULL;
                pWaveHdr1->reserved = 0;
                waveOutPrepareHeader(hWaveOut, pWaveHdr1, sizeof(WAVEHDR));

                pWaveHdr2->lpData = (LPSTR)pBuffer2;
                pWaveHdr2->dwBufferLength = OUT_BUFFER_SIZE;
                pWaveHdr2->dwBytesRecorded = 0;
                pWaveHdr2->dwUser = 0;
                pWaveHdr2->dwFlags = 0;
                pWaveHdr2->dwLoops = 1;
                pWaveHdr2->lpNext = NULL;
                pWaveHdr2->reserved = 0;
                waveOutPrepareHeader(hWaveOut, pWaveHdr2, sizeof(WAVEHDR));
            }
        }
        break;
    case WM_LBUTTONDOWN:	break;
in WM_DESTROY add bShutOff = TRUE;
i don't know if it's necessary but i'll explain below.

after WM_DESTROY add:

Code: Select all

    case MM_WOM_OPEN:
        		FillAudioBuffer(pBuffer1);	waveOutWrite(hWaveOut, pWaveHdr1, sizeof (WAVEHDR));
        		FillAudioBuffer(pBuffer2);	waveOutWrite(hWaveOut, pWaveHdr2, sizeof (WAVEHDR));
        return TRUE;
    case MM_WOM_DONE:
        		if (bShutOff) {waveOutClose(hWaveOut);	return TRUE;}
        		FillAudioBuffer( (PBYTE) ((PWAVEHDR) lParam)->lpData);
        		waveOutWrite(hWaveOut, (PWAVEHDR) lParam, sizeof (WAVEHDR));
        return TRUE;
    case MM_WOM_CLOSE:
        		waveOutUnprepareHeader(hWaveOut, pWaveHdr1, sizeof (WAVEHDR));
        		waveOutUnprepareHeader(hWaveOut, pWaveHdr2, sizeof (WAVEHDR));
        free(pWaveHdr1);	free(pWaveHdr2);	free(pBuffer1);	free(pBuffer2);
        hWaveOut = NULL;
        return TRUE;
and we outie.

i shuffled this method over from my old win32 style which i mostly inherited from a dev who i shall not name because i don't remember how i changed it and what kind of foolishness occured. (i remember how you tried and tried to help me with that mic in as well..). so i think most of the code is valid and pertinent, but of course it may be faaaaaar off course for how contemporary microsoft would go about it because i don't swim with the fishes.

but it makes a nice 150 something k compile which is developing as i remember so not a lot of fluff. clean and effective, i don't know how this even happens.

if you're new to windows programming, to make a realtime interactive interface, in teh old days i am qualified to speak of, i would create a timer, when WM_TIMER is called, InvalidateRgn to force a WM_PAINT event, and put all of my param updates in a process called from there, which update global variables doing stuff in the audio routine in audio.h. i would expect it's as effective and simple today before i actually do anything more.
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

bitwise
KVRist
155 posts since 18 Mar, 2012

Post Sat Nov 06, 2021 1:36 pm

xoxos wrote:
Sat Nov 06, 2021 9:18 am
i do hope that at some point i'll be able to be back using the contemporary windows API for generating a sound wave by populating a buffer and using a per sample audio process to write a sine wave, which anyone would be able to understand and adapt to their realtime synthesis intentions.

You're looking for miniaudio.h

https://miniaud.io/docs/examples/simple ... _sine.html
https://github.com/mackron/miniaudio

xoxos
Mr Entertainment
12341 posts since 30 Apr, 2002 from i might peeramid

Post Sat Nov 06, 2021 1:40 pm

*closes 17 windows* :)

i should add, going through this step by step, i think the main obstacles carrying old win32 code over were in the two header declarations:
pWaveHdr1->lpData = (LPSTR)pBuffer1;
i had to add the LPSTR cast

in MM_WOM_DONE i had to add the PBYTE cast
FillAudioBuffer( (PBYTE) ((PWAVEHDR) lParam)->lpData);

i had this stuff as a switch in WinProc

Code: Select all

	case WM_SYSCOMMAND:
		switch (wParam) {
			case SC_CLOSE:
				if (hWaveOut != NULL) {
					bShutOff = TRUE;	bClosing = TRUE;
//					waveOutReset (hWaveOut);
				}
				else DestroyWindow(hwnd);
				return TRUE;
		}
		break;
before removing it completely, the app emitted no sound and did not respond (eg. to trying to close it).
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

xoxos
Mr Entertainment
12341 posts since 30 Apr, 2002 from i might peeramid

Post Sun Nov 07, 2021 2:52 pm

here's a ms link dated 01/06/2021 that says use win32:
The Win32 API (also called the Windows API) is the original platform for native C/C++ Windows applications that require direct access to Windows and hardware. It provides a first-class development experience without depending on a managed runtime environment like .NET and WinRT (for UWP apps for Windows 10). This makes the Win32 API the platform of choice for applications that need the highest level of performance and direct access to system hardware.
https://docs.microsoft.com/en-us/window ... rogramming

nice to be aware of.
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Return to “DSP and Plug-in Development”