Thread overview
ATTN: Andrej Mitrovic: Port Audio
Aug 27, 2017
Johnson
Aug 27, 2017
Johnson
Aug 27, 2017
Johnson
August 27, 2017
You wrote a thread a while back about your callbacks not being called and you had a fix.

http://www.digitalmars.com/d/archives/digitalmars/D/learn/Anyone_using_Portaudio_22343.html

I'm trying to get portAudio to work on my machine and it seems everything passes yet my callbacks are not being called.


I do not know if it is the same issue you had or what. Could you describe your fixes, if you remember? You said it was alias issues, my source uses alias, so maybe I have the updated one.

Also, using paPrimeOutputBuffersUsingStreamCallback causes an access violation ;/

Here are some dll's I'm using

https://github.com/spatialaudio/portaudio-binaries



I'm including the two files needed to compile what I have in case you(or anyone else) decide to help figure this out:

Make sure to change the dll location in the DLL_PortAudio struct and set the appropriate audio interface to use(I have it set to 12, but have tried every value on my system with no luck).


module mPortAudio;
import std.stdio, std.conv;


enum PaError : int
{	
    NoError = 0,
    NotInitialized = -10000,
    UnanticipatedHostError,
    InvalidChannelCount,
    InvalidSampleRate,
    InvalidDevice,
    InvalidFlag,
    SampleFormatNotSupported,
    BadIODeviceCombination,
    InsufficientMemory,
    BufferTooBig,
    BufferTooSmall,
    NullCallback,
    BadStreamPtr,
    TimedOut,
    InternalError,
    DeviceUnavailable,
    IncompatibleHostApiSpecificStreamInfo,
    StreamIsStopped,
    StreamIsNotStopped,
    InputOverflowed,
    OutputUnderflowed,
    HostApiNotFound,
    InvalidHostApi,
    CanNotReadFromACallbackStream,
    CanNotWriteToACallbackStream,
    CanNotReadFromAnOutputOnlyStream,
    CanNotWriteToAnInputOnlyStream,
    IncompatibleStreamHostApi,
    BadBufferPtr,
	FormatIsSupported = 0,
};

enum PaSampleFormat : ulong
{
	Float32      = 0x00000001,
	Int32        = 0x00000002,
	Int24        = 0x00000004,
	Int16        = 0x00000008,
	Int8         = 0x00000010,
	UInt8        = 0x00000020,
	CustomFormat = 0x00010000,
	NonInterleaved = 0x80000000
}

enum PaHostApiTypeId : int
{
	InDevelopment=0,
	DirectSound=1,
	MME=2,
	ASIO=3,
	SoundManager=4,
	CoreAudio=5,
	OSS=7,
	ALSA=8,
	AL=9,
	BeOS=10,
	WDMKS=11,
	JACK=12,
	WASAPI=13,
	AudioScienceHPI=14
};


enum PaStreamCallbackResult : int
{
    paContinue=0,   /**< Signal that the stream should continue invoking the callback and processing audio. */
    paComplete=1,   /**< Signal that the stream should stop invoking the callback and finish once all output samples have played. */
    paAbort=2       /**< Signal that the stream should stop invoking the callback and finish as soon as possible. */
};





enum PaStreamFlags : ulong
{
	NoFlag = 0,
	ClipOff = 0x00000001,
	DitherOff = 0x00000002,
	NeverDropInput = 0x00000004,
	PrimeOutputBuffersUsingStreamCallback = 0x00000008,
	PlatformSpecificFlags = 0xFFFF0000,
}

alias void PaStream;
enum paFramesPerBufferUnspecified = 0;
enum PaStreamCallbackFlags : ulong
{
	InputUnderflow = 0x00000001,
	InputOverflow = 0x00000002,
	OutputUnderflow = 0x00000004,
	OutputOverflow = 0x00000008,
	PrimingOutput = 0x00000010,
}



alias extern(C) int function(const(void) *input, void *output, ulong frameCount, const(PaStreamCallbackTimeInfo)* timeInfo, PaStreamCallbackFlags statusFlags, void *userData ) PaStreamCallback;
alias void function( void *userData ) PaStreamFinishedCallback;

alias int PaDeviceIndex;
enum paNoDevice = cast(PaDeviceIndex)-1;
enum paUseHostApiSpecificDeviceSpecification =  cast(PaDeviceIndex)-2;
alias int PaHostApiIndex;
alias double PaTime;


struct PaHostApiInfo
{
    int structVersion;
    PaHostApiTypeId type;
    const(char) *name;
    int deviceCount;
    PaDeviceIndex defaultInputDevice;
    PaDeviceIndex defaultOutputDevice;
};

struct PaDeviceInfo
{
    int structVersion;
    const(char) *name;
    PaHostApiIndex hostApi;
    int maxInputChannels;
    int maxOutputChannels;
    PaTime defaultLowInputLatency;
    PaTime defaultLowOutputLatency;
    PaTime defaultHighInputLatency;
    PaTime defaultHighOutputLatency;
    double defaultSampleRate;
};

struct PaStreamCallbackTimeInfo
{
    PaTime inputBufferAdcTime;
    PaTime currentTime;
    PaTime outputBufferDacTime;
};

struct PaStreamInfo
{
    int structVersion;
    PaTime inputLatency;
    PaTime outputLatency;
    double sampleRate;
};


struct PaHostErrorInfo
{
    PaHostApiTypeId hostApiType;
    long errorCode;
    const(char) *errorText;
};

struct PaStreamParameters
{
    PaDeviceIndex device;
    int channelCount;
    PaSampleFormat sampleFormat;
    PaTime suggestedLatency;
    void *hostApiSpecificStreamInfo;
};
























struct DLL_PortAudio
{
	@("DLLImport") public static extern(Windows)
	{
		@(r"D:\D\DLLs\portaudio_x86.dll")
		{
			int function() Pa_GetVersion;
			const(char)* function() Pa_GetVersionText;
			const(char) *function(PaError errorCode) Pa_GetErrorText;
			PaError function() Pa_Initialize;
			PaError function() Pa_Terminate;
			PaHostApiIndex function() Pa_GetHostApiCount;
			PaHostApiIndex function() Pa_GetDefaultHostApi;
			const(PaHostApiInfo)* function(PaHostApiIndex hostApi) Pa_GetHostApiInfo;
			PaHostApiIndex function(PaHostApiTypeId type) Pa_HostApiTypeIdToHostApiIndex;
			PaDeviceIndex function(PaHostApiIndex hostApi, int hostApiDeviceIndex) Pa_HostApiDeviceIndexToDeviceIndex;
			const(PaHostErrorInfo)* function() Pa_GetLastHostErrorInfo;
			PaDeviceIndex function() Pa_GetDeviceCount;
			PaDeviceIndex function() Pa_GetDefaultInputDevice;
			PaDeviceIndex function() Pa_GetDefaultOutputDevice;
			const(PaDeviceInfo)* function(PaDeviceIndex device) Pa_GetDeviceInfo;
			PaError function(PaStream *stream, PaStreamFinishedCallback streamFinishedCallback) Pa_SetStreamFinishedCallback;
			PaError function(PaStream *stream) Pa_StartStream;
			PaError function(PaStream *stream) Pa_StopStream;
			PaError function(PaStream *stream) Pa_AbortStream;
			PaError function(PaStream *stream) Pa_IsStreamStopped;
			PaError function(PaStream *stream) Pa_IsStreamActive;
			PaError function(PaStream* stream, void *buffer, ulong frames) Pa_ReadStream;
			const(PaStreamInfo)* function(PaStream *stream) Pa_GetStreamInfo;
			PaTime function(PaStream *stream) Pa_GetStreamTime;
			double function(PaStream* stream) Pa_GetStreamCpuLoad;
			PaError function(PaStream* stream, const void *buffer, ulong frames) Pa_WriteStream;
			long function(PaStream* stream) Pa_GetStreamReadAvailable;
			long function(PaStream* stream) Pa_GetStreamWriteAvailable;
			PaError function(PaSampleFormat format) Pa_GetSampleSize;
			void function(long msec) Pa_Sleep;
			PaError function(PaStream** stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, double sampleRate, ulong framesPerBuffer, PaStreamFlags streamFlags, PaStreamCallback streamCallback, void *userData) Pa_OpenStream;
			PaError function(PaStream** stream, int numInputChannels, int numOutputChannels, PaSampleFormat sampleFormat, double sampleRate, ulong framesPerBuffer, PaStreamCallback streamCallback, void *userData) Pa_OpenDefaultStream;
			PaError function(PaStream *stream) Pa_CloseStream;
			PaError function(const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, double sampleRate) Pa_IsFormatSupported;
		}
	}
}



// Fixes static functions and function pointers to point to their specified DLL's
static this()
{
	version(Windows)
	{
		import DLLImport;
		ImportDLLs!DLL_PortAudio;
	}
}











struct Phase
{
    float left=0, right=0;
}





bool callbackCalled = false;
extern(C) int sawtooth(const(void)* inputBuffer, void* outputBuffer, ulong framesPerBuffer, const(PaStreamCallbackTimeInfo)* timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
	callbackCalled = true;
    auto phase = cast(Phase*)userData;
    auto pout = cast(float*)outputBuffer;
    enum vol = 1f;

    foreach(i; 0 .. framesPerBuffer)
    {
        *pout++ = vol * phase.left;
        *pout++ = vol * phase.right;

        phase.left += 0.01f;
        if (phase.left >= 1.0f) phase.left -= 2.0f;

        phase.right += 0.03f;
        if (phase.right >= 1.0f) phase.right -= 2.0f;
    }
    return 1;
}

int main()
{
	import core.stdc.stdio, std.stdio, std.conv;
	with(DLL_PortAudio)
	{
		enum NUM_SECONDS = 5;

		PaStream* stream;
		PaError err;
		Phase phase_data;


		
		if ((err = Pa_Initialize()) == PaError.NoError)
		{
			writeln("---------------------------------------------------------------------------");

			auto numDevices = Pa_GetDeviceCount();
			if(numDevices < 0)
			{
				printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices);
				err = cast(PaError)numDevices;
				goto Lerror;
			}


			for(auto i = 0; i < numDevices; i++ )
			{	
				auto deviceInfo = Pa_GetDeviceInfo(i);
				auto deviceApiInfo = Pa_GetHostApiInfo(deviceInfo.hostApi);
				write("Device "~to!string(i)~" > "~to!string(deviceInfo.name)~", "~to!string(deviceInfo.maxInputChannels)~", "~to!string(deviceInfo.maxOutputChannels)~", "~to!string(deviceInfo.defaultLowOutputLatency)~","~to!string(deviceInfo.defaultHighOutputLatency)~", "~to!string(deviceInfo.defaultSampleRate)~", "~to!string(deviceInfo.hostApi));
				
				write(", "~to!string(deviceApiInfo.name)~", "~to!string(deviceApiInfo.type));
				writeln();
				
			}

			double sampleRate = 44100;
			PaStreamParameters* input; // = new PaStreamParameters();
			if (input != null)
			{
				input.device = 12;
				auto id = Pa_GetDeviceInfo(input.device);
				input.hostApiSpecificStreamInfo = null;
				input.channelCount = id.maxInputChannels;
				input.sampleFormat = PaSampleFormat.Float32;
				input.suggestedLatency = (id.defaultLowInputLatency + id.defaultHighInputLatency)/2;
				sampleRate = id.defaultSampleRate;
			}

			PaStreamParameters* output = new PaStreamParameters();
			if (output != null)
			{
				output.device = 12;
				auto od = Pa_GetDeviceInfo(output.device);
				output.hostApiSpecificStreamInfo = null;
				output.channelCount = od.maxOutputChannels;
				output.sampleFormat = PaSampleFormat.Float32;
				output.suggestedLatency = (od.defaultLowOutputLatency + od.defaultHighOutputLatency)/2;
				sampleRate = od.defaultSampleRate;
			} else
			{
				writeln("ERROR: Invalid output stream Parameters");
				return -1;
			}


			Pa_SetStreamFinishedCallback(&stream, (data) { writeln("WFWFWEFWEFWEF"); });
			writeln("Opening Device!");
						
			if ((err = Pa_OpenStream(&stream, input, output, sampleRate, cast(ulong)paFramesPerBufferUnspecified, cast(PaStreamFlags)(PaStreamFlags.NoFlag + 0*PaStreamFlags.PrimeOutputBuffersUsingStreamCallback), &sawtooth, &phase_data)) != PaError.NoError) goto Lerror;

			writeln("Starting Device!");
			if ((err = Pa_StartStream(stream)) != PaError.NoError) goto Lerror;

			Pa_Sleep(NUM_SECONDS * 1000);

			writeln("Stopping Device!");
			if ((err = Pa_StopStream(stream)) != PaError.NoError) goto Lerror;
			if ((err = Pa_CloseStream(stream)) != PaError.NoError) goto Lerror;
			if ((err = Pa_Terminate()) != PaError.NoError) goto Lerror;

			if (!callbackCalled) writeln("Callback was not called!");
			writeln("DONE!");
			getchar();
			return 0;
		} else
		{
			Lerror:
				writeln(to!string(Pa_GetErrorText(err)));
				getchar();
				return 1;
		}
	}
	return 0;
}


















module DLLImport;

void ImportDLLs(T)()
{
	version(Windows)
	{
		import core.sys.windows.windows, std.conv, std.meta, std.traits;
		HINSTANCE[string] DLLs;
	
		foreach(fname; __traits(allMembers, T))
		{	
			mixin("enum isf = isFunction!(T."~fname~");");
			mixin("enum isfp = isFunctionPointer!(T."~fname~");");
			mixin("enum attrs = __traits(getAttributes, T."~fname~");");		
			static if ((isf || isfp) && attrs.length == 2 && attrs[0] == "DLLImport")
			{
				auto dllName = attrs[1];
				if (dllName !in DLLs)
					DLLs[dllName] = LoadLibrary(to!wstring(dllName~"\0").ptr);

				auto dll = DLLs[dllName];
				if (dll == null)
					assert(0, "Cannot load DLL `"~dllName~"'");

				auto func = GetProcAddress(dll, fname);

				mixin("import "~moduleName!(T)~";");
				static if (isf)
					mixin("auto p = cast(void**)&"~T.stringof~"."~fname~"; *p = cast(typeof(p))func;");
				else static if (isfp)
					mixin(""~T.stringof~"."~fname~" = cast(typeof("~T.stringof~"."~fname~"))func;");	
				else
					static assert("DLLImport Error");
			}
		
		}
	}
}

August 27, 2017
On Sunday, 27 August 2017 at 02:00:22 UTC, Johnson wrote:
> You wrote a thread a while back about your callbacks not being called and you had a fix.
>
> [...]

After going through the code a bit, seems there are some bugs with &stream. Only OpenStream seems to take a ** so the other functions are getting passed junk.

At least, I got sound! Scared the shit out of me too!
August 27, 2017
On Sunday, 27 August 2017 at 02:23:32 UTC, Johnson wrote:
> On Sunday, 27 August 2017 at 02:00:22 UTC, Johnson wrote:
>> You wrote a thread a while back about your callbacks not being called and you had a fix.
>>
>> [...]
>
> After going through the code a bit, seems there are some bugs with &stream. Only OpenStream seems to take a ** so the other functions are getting passed junk.
>
> At least, I got sound! Scared the shit out of me too!

I should mention that the callback is still not being called, but I used the sawtooth to modify a buffer and used Pa_WriteStream. Pa_SetStreamFinishedCallback is working.

PaStreamFlags.PrimeOutputBuffersUsingStreamCallback still causes an access violation.

Seems like the callback address is not being correctly transmitted to PA.