unit Unit1;
{$mode delphi}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
PortAudio, CTypes;
{ TForm1 }
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ private declarations }
public
procedure Error;
end;
const
SampleRate = 44100;
FramesPerBuffer = 64;
TwoPi = 2*Pi;
// How long you want to play the test sine:
NumSecs = 1;
// Wavetable size. Influences your pitch:
TableSize = 200;
type
{ A type which holds a wavetable, two integers keeping track of
at which offset in the wavetable each channel is currently
playing (= phase), and a message: }
PaTestData = record
Sine : array[0..TableSize] of CFloat;
LeftPhase : CInt32;
RightPhase : CInt32;
AMessage : PChar;
end;
PPaTestData = ^PaTestData;
var
Form1: TForm1;
OutputParameters : PaStreamParameters;
{ This pointer points to a pointer which will be returned by
Pa_OpenStream: }
Stream : PPaStream;
Err : PaError;
Data : PaTestData;
DataPointer : PPaTestData;
j : CInt32;
implementation
{$R *.lfm}
{ TForm1 }
function PaTestCallback( inputBuffer : pointer; OutputBuffer : pointer;
framesPerBuffer : culong; timeInfo : PPaStreamCallbackTimeInfo;
statusFlags : PaStreamCallbackFlags; UserData : pointer ) : CInt32;
cdecl;
var
OutBuffer : PCFloat;
i : culong;
LocalDataPointer : PPaTestData;
begin
OutBuffer := PCFloat(OutputBuffer);
LocalDataPointer := PPaTestData(UserData);
// Fill the buffer...
for i := 0 to (FramesPerBuffer-1) do begin
OutBuffer^ := LocalDataPointer^.Sine[LocalDataPointer^.LeftPhase];
Inc(OutBuffer);
OutBuffer^ := LocalDataPointer^.Sine[LocalDataPointer^.RightPhase];
Inc(OutBuffer);
Inc(LocalDataPointer^.LeftPhase);
if (LocalDataPointer^.LeftPhase >= TableSize ) then
LocalDataPointer^.LeftPhase :=
(LocalDataPointer^.LeftPhase - TableSize);
Inc(LocalDataPointer^.RightPhase);
Inc(LocalDataPointer^.RightPhase);
if ( LocalDataPointer^.RightPhase >= TableSize ) then
LocalDataPointer^.RightPhase :=
(LocalDataPointer^.RightPhase - TableSize);
end;
PaTestCallback := CInt32(0);
end;
{ This is called when playback is finished.
Remember: ALWAYS USE CDECL or your pointers will be messed up!
Pointers to this function must be castable to PPaStreamFinishedCallback: }
procedure StreamFinished( UserData : pointer ); cdecl;
var
LocalDataPointer : PPaTestData;
begin
LocalDataPointer := PPaTestData(UserData);
// Memo1.lines.add('Stream Completed: '+ LocalDataPointer^.AMessage);
end;
procedure TForm1.Error;
begin
Pa_Terminate();
Memo1.lines.add('An error occured while using the portaudio Stream');
Memo1.lines.add('Error number: '+ IntToStr(Err));
Memo1.lines.add('Error message: '+ Pa_GetErrorText( Err ) );
halt;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.lines.add('PortAudio Test: Output Sine wave. SR = ' +
IntToStr(SampleRate) + ', Buffer size = ' + IntToStr(FramesPerBuffer));
DataPointer := @Data;
// Fill a Sine wavetable (Float Data -1 .. +1)
for j := 0 to TableSize-1 do begin
Data.Sine[j] := Sin( j*TwoPi/TableSize);
end;
Data.LeftPhase := 0;
Data.RightPhase := 0;
Err := Pa_Initialize;
if not Err = 0 then Error;
OutputParameters.Device := Pa_GetDefaultOutputDevice;
OutputParameters.ChannelCount := CInt32(2);
OutputParameters.SampleFormat := paFloat32;
OutputParameters.SuggestedLatency :=
(Pa_GetDeviceInfo(OutputParameters.device)^.defaultHighOutputLatency)*1;
OutputParameters.HostApiSpecificStreamInfo := nil;
Memo1.lines.add('Latency '+FloatToStr(
Pa_GetDeviceInfo(OutputParameters.device)^.defaultHighOutputLatency));
Err := Pa_OpenStream(@Stream, nil, @OutputParameters, SampleRate,
FramesPerBuffer, paClipOff, PPaStreamCallback(@PaTestCallback),
DataPointer);
if not Err = 0 then Error;
Data.AMessage := 'No Message'#0;
Err := Pa_SetStreamFinishedCallback(Stream,
PPaStreamFinishedCallback(@StreamFinished));
if not Err = 0 then Error;
Memo1.lines.add('Press <RETURN> to attempt start playing a Sine for '+
IntToStr(NumSecs)+' sec.');
Err := Pa_StartStream( Stream );
if not Err = 0 then Error;
Memo1.lines.add('Play for '+IntToStr(NumSecs)+ ' seconds.');
Pa_Sleep( NumSecs * 1000 );
Err := Pa_StopStream( Stream );
if not Err = 0 then Error;
Err := Pa_CloseStream( Stream );
if not Err = 0 then Error;
Pa_Terminate;
Memo1.lines.add('Test finished.');
end;
end.