program portaudio_demo;
{$mode objfpc}{$H+}
{$PACKRECORDS C}
uses
ctypes, Math, SysUtils;
const
NUM_CHANNELS = 2;
SAMPLE_RATE = 44100;
FRAMES_PER_BUFF = 256;
PI2 = 6.283185307179586476925286766559;
type
PaError = cint;
PaStreamParameters = record
device: cint;
channelCount: cint;
sampleFormat: culong;
suggestedLatency: double;
hostApiSpecificStreamInfo: Pointer;
end;
PPaStreamParameters = ^PaStreamParameters;
PaStream = Pointer;
PPaStream = ^PaStream;
PaStreamFlags = culong;
OscillatorData = record
phase: double;
phaseIncrement: double;
end;
POscillatorData = ^OscillatorData;
{ ====================================================================
PORTAUDIO FUNCTION DECLARATIONS (DUAL MULTI-OS LAYOUT)
==================================================================== }
{$IFDEF WINDOWS}
function Pa_Initialize: PaError; cdecl; external name 'Pa_Initialize';
function Pa_Terminate: PaError; cdecl; external name 'Pa_Terminate';
function Pa_GetDefaultOutputDevice: cint; cdecl; external name 'Pa_GetDefaultOutputDevice';
function Pa_OpenStream(stream: PPaStream; inputParameters: PPaStreamParameters; outputParameters: PPaStreamParameters; sampleRate: double; framesPerBuffer: culong; streamFlags: PaStreamFlags; streamCallback: Pointer; userData: Pointer): PaError; cdecl; external name 'Pa_OpenStream';
function Pa_StartStream(stream: PaStream): PaError; cdecl; external name 'Pa_StartStream';
function Pa_StopStream(stream: PaStream): PaError; cdecl; external name 'Pa_StopStream';
function Pa_CloseStream(stream: PaStream): PaError; cdecl; external name 'Pa_CloseStream';
procedure Pa_Sleep(msec: clong); cdecl; external name 'Pa_Sleep';
{$ELSE}
function Pa_Initialize: PaError; cdecl; external 'portaudio' name 'Pa_Initialize';
function Pa_Terminate: PaError; cdecl; external 'portaudio' name 'Pa_Terminate';
function Pa_GetDefaultOutputDevice: cint; cdecl; external 'portaudio' name 'Pa_GetDefaultOutputDevice';
function Pa_OpenStream(stream: PPaStream; inputParameters: PPaStreamParameters; outputParameters: PPaStreamParameters; sampleRate: double; framesPerBuffer: culong; streamFlags: PaStreamFlags; streamCallback: Pointer; userData: Pointer): PaError; cdecl; external 'portaudio' name 'Pa_OpenStream';
function Pa_StartStream(stream: PaStream): PaError; cdecl; external 'portaudio' name 'Pa_StartStream';
function Pa_StopStream(stream: PaStream): PaError; cdecl; external 'portaudio' name 'Pa_StopStream';
function Pa_CloseStream(stream: PaStream): PaError; cdecl; external 'portaudio' name 'Pa_CloseStream';
procedure Pa_Sleep(msec: clong); cdecl; external 'portaudio' name 'Pa_Sleep';
{$ENDIF}
{ ====================================================================
STATIC LINKING REGION (ZERO C++ ARCHIVES REQUIRED)
==================================================================== }
{$LinkLib libportaudio.a}
{$IFDEF WINDOWS}
{$LinkLib libgcc.a}
{$LinkLib libmingwex.a}
{$LinkLib libmsvcrt.a}
{$LinkLib libole32.a}
{$LinkLib libwinmm.a}
{$LinkLib libsetupapi.a}
{$LinkLib libadvapi.a}
{$LinkLib libuuid.a}
{$LinkLib libkernel32.a}
{$LinkLib libuser32.a}
{$ENDIF}
{ ====================================================================
LIGHTWEIGHT EMBEDDED C++ RUNTIME EMULATION STUBS
==================================================================== }
{$IFDEF WINDOWS}
procedure C_free(p: Pointer); cdecl; external 'msvcrt' name 'free';
function C_malloc(size: ptruint): Pointer; cdecl; external 'msvcrt' name 'malloc';
{ Note: public modifier paired with alias name string directly satisfies internal ld lookups }
function operator_new(size: ptruint): Pointer; cdecl; [public, alias: '_Znwy'];
begin
Result := C_malloc(size);
end;
function operator_new_arr(size: ptruint): Pointer; cdecl; [public, alias: '_Znay'];
begin
Result := C_malloc(size);
end;
procedure operator_delete(p: Pointer); cdecl; [public, alias: '_ZdlPvy'];
begin
C_free(p);
end;
procedure operator_delete_arr(p: Pointer); cdecl; [public, alias: '_ZdaPv'];
begin
C_free(p);
end;
procedure __gxx_personality_seh0; cdecl; [public, alias: '__gxx_personality_seh0']; begin end;
procedure __cxa_begin_catch; cdecl; [public, alias: '__cxa_begin_catch']; begin end;
procedure __cxa_end_catch; cdecl; [public, alias: '__cxa_end_catch']; begin end;
procedure _Unwind_Resume; cdecl; [public, alias: '_Unwind_Resume']; begin end;
procedure _ZNSt9bad_allocD1Ev; cdecl; [public, alias: '_ZNSt9bad_allocD1Ev']; begin end;
var
_ZTVN10__cxxabiv117__class_type_infoE: array[0..3] of Pointer; cvar; public;
_ZTVN10__cxxabiv120__si_class_type_infoE: array[0..3] of Pointer; cvar; public;
_ZTVSt9bad_alloc: array[0..3] of Pointer; cvar; public;
{$ENDIF}
{ ====================================================================
REAL-TIME AUDIO CALLBACK
==================================================================== }
function AudioCallback(inputBuffer: Pointer; outputBuffer: Pointer;
framesPerBuffer: culong; timeInfo: Pointer;
statusFlags: culong; userData: Pointer): cint; cdecl;
var
outPtr: PSingle;
data: POscillatorData;
i: culong;
sample: Single;
begin
outPtr := PSingle(outputBuffer);
data := POscillatorData(userData);
for i := 0 to framesPerBuffer - 1 do
begin
sample := Single(Sin(data^.phase));
outPtr^ := sample; Inc(outPtr);
outPtr^ := sample; Inc(outPtr);
data^.phase := data^.phase + data^.phaseIncrement;
if data^.phase >= PI2 then data^.phase := data^.phase - PI2;
end;
Result := 0;
end;
{ ====================================================================
MAIN APPLICATION EXECUTIVE
==================================================================== }
var
err: PaError;
stream: PaStream;
outputParams: PaStreamParameters;
sineData: OscillatorData;
begin
WriteLn('--- uos Clean Static PortAudio Test ---');
sineData.phase := 0.0;
sineData.phaseIncrement := (440.0 * PI2) / SAMPLE_RATE;
err := Pa_Initialize;
if err <> 0 then begin WriteLn('Initialization Failure!'); Exit; end;
outputParams.device := Pa_GetDefaultOutputDevice;
if outputParams.device = -1 then begin WriteLn('No default audio device found!'); Pa_Terminate; Exit; end;
outputParams.channelCount := NUM_CHANNELS;
outputParams.sampleFormat := $00000001;
outputParams.suggestedLatency := 0.050;
outputParams.hostApiSpecificStreamInfo := nil;
err := Pa_OpenStream(@stream, nil, @outputParams, SAMPLE_RATE, FRAMES_PER_BUFF, 0, @AudioCallback, @sineData);
if err <> 0 then begin WriteLn('Failed to open audio stream!'); Pa_Terminate; Exit; end;
Pa_StartStream(stream);
Pa_Sleep(4000);
Pa_StopStream(stream); Pa_CloseStream(stream); Pa_Terminate;
WriteLn('Done!');
end.