www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - ATTN: Andrej Mitrovic: Port Audio

reply Johnson <Johnson Johnson.com> writes:
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.defaultH
ghOutputLatency)~", "~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");
			}
		
		}
	}
}
Aug 26
parent reply Johnson <Johnson Johnson.com> writes:
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!
Aug 26
parent Johnson <Johnson Johnson.com> writes:
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.
Aug 26