aboutsummaryrefslogtreecommitdiffstats
path: root/Game/Code/Classes/UAudio_portaudio.pas
diff options
context:
space:
mode:
Diffstat (limited to 'Game/Code/Classes/UAudio_portaudio.pas')
-rw-r--r--Game/Code/Classes/UAudio_portaudio.pas863
1 files changed, 429 insertions, 434 deletions
diff --git a/Game/Code/Classes/UAudio_portaudio.pas b/Game/Code/Classes/UAudio_portaudio.pas
index 9ed2107d..941d90d1 100644
--- a/Game/Code/Classes/UAudio_portaudio.pas
+++ b/Game/Code/Classes/UAudio_portaudio.pas
@@ -1,434 +1,429 @@
-unit UAudio_Portaudio;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-
-uses Classes,
- {$IFDEF win32}
- windows,
- {$ENDIF}
- Messages,
- SysUtils,
- {$IFNDEF FPC}
- Forms,
- {$ENDIF}
- portaudio,
- {$IFDEF UsePortmixer}
- portmixer,
- {$ENDIF}
- ULog,
- UMusic;
-
-implementation
-
-uses
- {$IFDEF LAZARUS}
- lclintf,
- {$ENDIF}
- URecord,
- UIni,
- UMain,
- UCommon,
- UThemes;
-{
-type
- TPaHostApiIndex = PaHostApiIndex;
- TPaDeviceIndex = PaDeviceIndex;
- PPaStream = ^PaStreamPtr;
- PPaStreamCallbackTimeInfo = ^PaStreamCallbackTimeInfo;
- TPaStreamCallbackFlags = PaStreamCallbackFlags;
- TPaHostApiTypeId = PaHostApiTypeId;
- PPaHostApiInfo = ^PaHostApiInfo;
- PPaDeviceInfo = ^PaDeviceInfo;
- TPaError = PaError;
- TPaStreamParameters = PaStreamParameters;
-}
-type
- TAudio_Portaudio = class( TInterfacedObject, IAudioInput )
- private
- function GetPreferredApiIndex(): TPaHostApiIndex;
- public
- function GetName: String;
- procedure InitializeRecord;
-
- procedure CaptureStart;
- procedure CaptureStop;
-
- procedure CaptureCard(Card: byte; CaptureSoundLeft, CaptureSoundRight: TSound);
- procedure StopCard(Card: byte);
- end;
-
- TPortaudioSoundCard = class(TGenericSoundCard)
- RecordStream: PPaStream;
- DeviceIndex: TPaDeviceIndex;
- end;
-
-function MicrophoneCallback(input: Pointer; output: Pointer; frameCount: Longword;
- timeInfo: PPaStreamCallbackTimeInfo; statusFlags: TPaStreamCallbackFlags;
- inputDevice: Pointer): Integer; cdecl; forward;
-
-var
- singleton_MusicPortaudio : IAudioInput;
-
-const
- sampleRate: Double = 44100.;
-
-{* the default API used by Portaudio is the least common denominator
- * and might lack efficiency. ApiPreferenceOrder defines the order of
- * preferred APIs to use. The first API-type in the list is tried first. If it's
- * not available the next is tried, ...
- * If none of the preferred APIs was found the default API is used.
- * Pascal doesn't permit zero-length static arrays, so you can use paDefaultApi
- * as an array's only member if you do not have any preferences.
- * paDefaultApi also terminates a preferences list but this is optional.
- *}
-const
- paDefaultApi = -1;
-var
- ApiPreferenceOrder:
-{$IF Defined(WIN32)}
- // Note1: Portmixer has no mixer support for paASIO and paWASAPI at the moment
- // Note2: Windows Default-API is MME
- //array[0..0] of TPaHostApiTypeId = ( paDirectSound, paMME );
- array[0..0] of TPaHostApiTypeId = ( paDirectSound );
-{$ELSEIF Defined(LINUX)}
- // Note1: Portmixer has no mixer support for paJACK at the moment
- // Note2: Not tested, but ALSA might be better than OSS.
- array[0..1] of TPaHostApiTypeId = ( paALSA, paOSS );
-{$ELSEIF Defined(DARWIN)}
- // Note: Not tested.
- //array[0..0] of TPaHostApiTypeId = ( paCoreAudio );
- array[0..0] of TPaHostApiTypeId = ( paDefaultApi );
-{$ELSE}
- array[0..0] of TPaHostApiTypeId = ( paDefaultApi );
-{$IFEND}
-
-function TAudio_Portaudio.GetName: String;
-begin
- result := 'Portaudio';
-end;
-
-function TAudio_Portaudio.GetPreferredApiIndex(): TPaHostApiIndex;
-var
- i: integer;
-begin
- result := -1;
-
- // select preferred sound-API
- for i:= 0 to High(ApiPreferenceOrder) do
- begin
- if(ApiPreferenceOrder[i] <> paDefaultApi) then begin
- // check if API is available
- result := Pa_HostApiTypeIdToHostApiIndex(ApiPreferenceOrder[i]);
- if(result >= 0) then
- break;
- end;
- end;
-
- // None of the preferred APIs is available -> use default
- if(result < 0) then begin
- result := Pa_GetDefaultHostApi();
- end;
-end;
-
-// TODO: should be a function with boolean return type
-procedure TAudio_Portaudio.InitializeRecord;
-var
- i: integer;
- apiIndex: TPaHostApiIndex;
- apiInfo: PPaHostApiInfo;
- deviceName: string;
- deviceIndex: TPaDeviceIndex;
- deviceInfo: PPaDeviceInfo;
- inputCnt: integer;
- inputName: string;
- SC: integer; // soundcard
- SCI: integer; // soundcard input
- err: TPaError;
- errMsg: string;
- paSoundCard: TPortaudioSoundCard;
- inputParams: TPaStreamParameters;
- stream: PPaStream;
- {$IFDEF UsePortmixer}
- mixer: PPxMixer;
- {$ENDIF}
-begin
- // TODO: call Pa_Terminate() on termination
- err := Pa_Initialize();
- if(err <> paNoError) then begin
- Log.CriticalError('Portaudio.InitializeRecord: ' + Pa_GetErrorText(err));
- //Log.LogError('Portaudio.InitializeRecord: ' + Pa_GetErrorText(err));
- // result := false;
- Exit;
- end;
-
- apiIndex := GetPreferredApiIndex();
- apiInfo := Pa_GetHostApiInfo(apiIndex);
-
- SC := 0;
-
- // init array-size to max. input-devices count
- SetLength(Recording.SoundCard, apiInfo^.deviceCount); // fix deviceCountL
- for i:= 0 to High(Recording.SoundCard) do
- begin
- // convert API-specific device-index to global index
- deviceIndex := Pa_HostApiDeviceIndexToDeviceIndex(apiIndex, i);
- deviceInfo := Pa_GetDeviceInfo(deviceIndex);
-
- // current device is no input device -> skip
- if(deviceInfo^.maxInputChannels <= 0) then
- continue;
-
- // TODO: free object on termination
- paSoundCard := TPortaudioSoundCard.Create();
- Recording.SoundCard[SC] := paSoundCard;
-
- // retrieve device-name
- deviceName := deviceInfo^.name;
- paSoundCard.Description := deviceName;
- paSoundCard.DeviceIndex := deviceIndex;
-
- // setup desired input parameters
- with inputParams do begin
- device := deviceIndex;
- channelCount := 2;
- sampleFormat := paInt16;
- suggestedLatency := deviceInfo^.defaultLowInputLatency;
- hostApiSpecificStreamInfo := nil;
- end;
-
- // check if device supports our input-format
- err := Pa_IsFormatSupported(@inputParams, nil, sampleRate);
- if(err <> 0) then begin
- // format not supported -> skip
- errMsg := Pa_GetErrorText(err);
- Log.LogError('Portaudio.InitializeRecord, device: "'+ deviceName +'" '
- + '('+ errMsg +')');
- paSoundCard.Free();
- continue;
- end;
-
- // TODO: retry with mono if stereo is not supported
- // TODO: retry with input-latency set to 20ms (defaultLowInputLatency might
- // not be set correctly in OSS)
-
- err := Pa_OpenStream(stream, @inputParams, nil, sampleRate,
- paFramesPerBufferUnspecified, paNoFlag, @MicrophoneCallback, nil);
- if(err <> paNoError) then begin
- // unable to open device -> skip
- errMsg := Pa_GetErrorText(err);
- Log.LogError('Portaudio.InitializeRecord, device: "'+ deviceName +'" '
- + '('+ errMsg +')');
- paSoundCard.Free();
- continue;
- end;
-
-
- {$IFDEF UsePortmixer}
-
- // use default mixer
- mixer := Px_OpenMixer(stream, 0);
-
- // get input count
- inputCnt := Px_GetNumInputSources(mixer);
- SetLength(paSoundCard.Input, inputCnt);
-
- // get input names
- for SCI := 0 to inputCnt-1 do
- begin
- inputName := Px_GetInputSourceName(mixer, SCI);
- paSoundCard.Input[SCI].Name := inputName;
- end;
-
- Px_CloseMixer(mixer);
-
- {$ELSE} // !UsePortmixer
-
- //Pa_StartStream(stream);
- // TODO: check if callback was called (this problem may occur on some devices)
- //Pa_StopStream(stream);
-
- Pa_CloseStream(stream);
-
- // create a standard input source
- SetLength(paSoundCard.Input, 1);
- paSoundCard.Input[0].Name := 'Standard';
-
- {$ENDIF}
-
- // use default input source
- paSoundCard.InputSelected := 0;
-
- Inc(SC);
- end;
-
- // adjust size to actual input-device count
- SetLength(Recording.SoundCard, SC);
-
- Log.LogStatus('#Soundcards: ' + inttostr(SC), 'Portaudio');
-
- {
- SoundCard[SC].InputSelected := Mic[Device];
- }
-end;
-
-// TODO: code is used by all IAudioInput implementors
-// -> move to a common superclass (TAudioInput_Generic?)
-procedure TAudio_Portaudio.CaptureStart;
-var
- S: integer;
- SC: integer;
- PlayerLeft, PlayerRight: integer;
- CaptureSoundLeft, CaptureSoundRight: TSound;
-begin
- for S := 0 to High(Recording.Sound) do
- Recording.Sound[S].BufferLong[0].Clear;
-
- for SC := 0 to High(Ini.CardList) do begin
- PlayerLeft := Ini.CardList[SC].ChannelL-1;
- PlayerRight := Ini.CardList[SC].ChannelR-1;
- if PlayerLeft >= PlayersPlay then PlayerLeft := -1;
- if PlayerRight >= PlayersPlay then PlayerRight := -1;
- if (PlayerLeft > -1) or (PlayerRight > -1) then begin
- if (PlayerLeft > -1) then
- CaptureSoundLeft := Recording.Sound[PlayerLeft]
- else
- CaptureSoundLeft := nil;
- if (PlayerRight > -1) then
- CaptureSoundRight := Recording.Sound[PlayerRight]
- else
- CaptureSoundRight := nil;
-
- CaptureCard(SC, CaptureSoundLeft, CaptureSoundRight);
- end;
- end;
-end;
-
-// TODO: code is used by all IAudioInput implementors
-// -> move to a common superclass (TAudioInput_Generic?)
-procedure TAudio_Portaudio.CaptureStop;
-var
- SC: integer;
- PlayerLeft: integer;
- PlayerRight: integer;
-begin
-
- for SC := 0 to High(Ini.CardList) do begin
- PlayerLeft := Ini.CardList[SC].ChannelL-1;
- PlayerRight := Ini.CardList[SC].ChannelR-1;
- if PlayerLeft >= PlayersPlay then PlayerLeft := -1;
- if PlayerRight >= PlayersPlay then PlayerRight := -1;
- if (PlayerLeft > -1) or (PlayerRight > -1) then
- StopCard(SC);
- end;
-
-end;
-
-{*
- * Portaudio input capture callback.
- *}
-function MicrophoneCallback(input: Pointer; output: Pointer; frameCount: Longword;
- timeInfo: PPaStreamCallbackTimeInfo; statusFlags: TPaStreamCallbackFlags;
- inputDevice: Pointer): Integer; cdecl;
-begin
- Recording.HandleMicrophoneData(input, frameCount*4, inputDevice);
- result := paContinue;
-end;
-
-{*
- * Start input-capturing on Soundcard specified by Card.
- * Params:
- * Card - soundcard index in Recording.SoundCard array
- * CaptureSoundLeft - sound(-buffer) used for left channel capture data
- * CaptureSoundRight - sound(-buffer) used for right channel capture data
- *}
-procedure TAudio_Portaudio.CaptureCard(Card: byte; CaptureSoundLeft, CaptureSoundRight: TSound);
-var
- Error: TPaError;
- ErrorMsg: string;
- inputParams: TPaStreamParameters;
- deviceInfo: PPaDeviceInfo;
- stream: PPaStream;
- paSoundCard: TPortaudioSoundCard;
-begin
- Log.LogStatus('Cap1', 'Portaudio');
-
- paSoundCard := TPortaudioSoundCard(Recording.SoundCard[Card]);
- paSoundCard.CaptureSoundLeft := CaptureSoundLeft;
- paSoundCard.CaptureSoundRight := CaptureSoundRight;
-
- // get input latency info
- deviceInfo := Pa_GetDeviceInfo(paSoundCard.DeviceIndex);
-
- // set input stream parameters
- with inputParams do begin
- device := paSoundCard.DeviceIndex;
- channelCount := 2;
- sampleFormat := paInt16;
- suggestedLatency := deviceInfo^.defaultLowInputLatency;
- hostApiSpecificStreamInfo := nil;
- end;
-
- Log.LogStatus(inttostr(paSoundCard.DeviceIndex), 'Portaudio');
- Log.LogStatus(floattostr(deviceInfo^.defaultLowInputLatency), 'Portaudio');
-
- // open input stream
- Error := Pa_OpenStream(stream, @inputParams, nil, sampleRate,
- paFramesPerBufferUnspecified, paNoFlag,
- @MicrophoneCallback, Pointer(paSoundCard));
- if(Error <> paNoError) then begin
- ErrorMsg := Pa_GetErrorText(Error);
- Log.CriticalError('TAudio_Portaudio.CaptureCard('+ IntToStr(Card) +'): Error opening stream: ' + ErrorMsg);
- //Halt;
- end;
-
- paSoundCard.RecordStream := stream;
-
- // start capture
- Error := Pa_StartStream(stream);
- if(Error <> paNoError) then begin
- Pa_CloseStream(stream);
- ErrorMsg := Pa_GetErrorText(Error);
- Log.CriticalError('TAudio_Portaudio.CaptureCard('+ IntToStr(Card) +'): Error starting stream: ' + ErrorMsg);
- //Halt;
- end;
-
- //Readln;
- Log.LogStatus('Cap2', 'Portaudio');
-end;
-
-{*
- * Stop input-capturing on Soundcard specified by Card.
- * Params:
- * Card - soundcard index in Recording.SoundCard array
- *}
-procedure TAudio_Portaudio.StopCard(Card: byte);
-var
- stream: PPaStream;
- paSoundCard: TPortaudioSoundCard;
-begin
- paSoundCard := TPortaudioSoundCard(Recording.SoundCard[Card]);
- stream := paSoundCard.RecordStream;
- if(stream <> nil) then begin
- Pa_StopStream(stream);
- Pa_CloseStream(stream);
- end;
-end;
-
-
-initialization
- singleton_MusicPortaudio := TAudio_Portaudio.create();
- writeln( 'UAudio_Portaudio - Register' );
- AudioManager.add( singleton_MusicPortaudio );
-
-finalization
- writeln( 'UAudio_Portaudio - UnRegister' );
- AudioManager.Remove( singleton_MusicPortaudio );
-
-end.
+unit UAudio_Portaudio;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+
+uses Classes,
+ {$IFDEF win32}
+ windows,
+ {$ENDIF}
+ Messages,
+ SysUtils,
+ {$IFNDEF FPC}
+ Forms,
+ {$ENDIF}
+ portaudio,
+ {$IFDEF UsePortmixer}
+ portmixer,
+ {$ENDIF}
+ ULog,
+ UMusic;
+
+implementation
+
+uses
+ {$IFDEF LAZARUS}
+ lclintf,
+ {$ENDIF}
+ URecord,
+ UIni,
+ UMain,
+ UCommon,
+ UThemes;
+{
+type
+ TPaHostApiIndex = PaHostApiIndex;
+ TPaDeviceIndex = PaDeviceIndex;
+ PPaStream = ^PaStreamPtr;
+ PPaStreamCallbackTimeInfo = ^PaStreamCallbackTimeInfo;
+ TPaStreamCallbackFlags = PaStreamCallbackFlags;
+ TPaHostApiTypeId = PaHostApiTypeId;
+ PPaHostApiInfo = ^PaHostApiInfo;
+ PPaDeviceInfo = ^PaDeviceInfo;
+ TPaError = PaError;
+ TPaStreamParameters = PaStreamParameters;
+}
+type
+ TAudio_Portaudio = class( TInterfacedObject, IAudioInput )
+ private
+ function GetPreferredApiIndex(): TPaHostApiIndex;
+ public
+ function GetName: String;
+ procedure InitializeRecord;
+
+ procedure CaptureStart;
+ procedure CaptureStop;
+
+ procedure CaptureCard(Card: byte; CaptureSoundLeft, CaptureSoundRight: TSound);
+ procedure StopCard(Card: byte);
+ end;
+
+ TPortaudioSoundCard = class(TGenericSoundCard)
+ RecordStream: PPaStream;
+ DeviceIndex: TPaDeviceIndex;
+ end;
+
+function MicrophoneCallback(input: Pointer; output: Pointer; frameCount: Longword;
+ timeInfo: PPaStreamCallbackTimeInfo; statusFlags: TPaStreamCallbackFlags;
+ inputDevice: Pointer): Integer; cdecl; forward;
+
+var
+ singleton_MusicPortaudio : IAudioInput;
+
+const
+ sampleRate: Double = 44100.;
+
+{* the default API used by Portaudio is the least common denominator
+ * and might lack efficiency. ApiPreferenceOrder defines the order of
+ * preferred APIs to use. The first API-type in the list is tried first. If it's
+ * not available the next is tried, ...
+ * If none of the preferred APIs was found the default API is used.
+ * Pascal doesn't permit zero-length static arrays, so you can use paDefaultApi
+ * as an array's only member if you do not have any preferences.
+ * paDefaultApi also terminates a preferences list but this is optional.
+ *}
+const
+ paDefaultApi = -1;
+var
+ ApiPreferenceOrder:
+{$IF Defined(WIN32)}
+ // Note1: Portmixer has no mixer support for paASIO and paWASAPI at the moment
+ // Note2: Windows Default-API is MME
+ //array[0..0] of TPaHostApiTypeId = ( paDirectSound, paMME );
+ array[0..0] of TPaHostApiTypeId = ( paDirectSound );
+{$ELSEIF Defined(LINUX)}
+ // Note1: Portmixer has no mixer support for paJACK at the moment
+ // Note2: Not tested, but ALSA might be better than OSS.
+ array[0..1] of TPaHostApiTypeId = ( paALSA, paOSS );
+{$ELSEIF Defined(DARWIN)}
+ // Note: Not tested.
+ //array[0..0] of TPaHostApiTypeId = ( paCoreAudio );
+ array[0..0] of TPaHostApiTypeId = ( paDefaultApi );
+{$ELSE}
+ array[0..0] of TPaHostApiTypeId = ( paDefaultApi );
+{$IFEND}
+
+function TAudio_Portaudio.GetName: String;
+begin
+ result := 'Portaudio';
+end;
+
+function TAudio_Portaudio.GetPreferredApiIndex(): TPaHostApiIndex;
+var
+ i: integer;
+begin
+ result := -1;
+
+ // select preferred sound-API
+ for i:= 0 to High(ApiPreferenceOrder) do
+ begin
+ if(ApiPreferenceOrder[i] <> paDefaultApi) then begin
+ // check if API is available
+ result := Pa_HostApiTypeIdToHostApiIndex(ApiPreferenceOrder[i]);
+ if(result >= 0) then
+ break;
+ end;
+ end;
+
+ // None of the preferred APIs is available -> use default
+ if(result < 0) then begin
+ result := Pa_GetDefaultHostApi();
+ end;
+end;
+
+// TODO: should be a function with boolean return type
+procedure TAudio_Portaudio.InitializeRecord;
+var
+ i: integer;
+ apiIndex: TPaHostApiIndex;
+ apiInfo: PPaHostApiInfo;
+ deviceName: string;
+ deviceIndex: TPaDeviceIndex;
+ deviceInfo: PPaDeviceInfo;
+ inputCnt: integer;
+ inputName: string;
+ SC: integer; // soundcard
+ SCI: integer; // soundcard input
+ err: TPaError;
+ errMsg: string;
+ paSoundCard: TPortaudioSoundCard;
+ inputParams: TPaStreamParameters;
+ stream: PPaStream;
+ {$IFDEF UsePortmixer}
+ mixer: PPxMixer;
+ {$ENDIF}
+begin
+ // TODO: call Pa_Terminate() on termination
+ err := Pa_Initialize();
+ if(err <> paNoError) then begin
+ Log.CriticalError('Portaudio.InitializeRecord: ' + Pa_GetErrorText(err));
+ //Log.LogError('Portaudio.InitializeRecord: ' + Pa_GetErrorText(err));
+ // result := false;
+ Exit;
+ end;
+
+ apiIndex := GetPreferredApiIndex();
+ apiInfo := Pa_GetHostApiInfo(apiIndex);
+
+ SC := 0;
+
+ // init array-size to max. input-devices count
+ SetLength(Recording.SoundCard, apiInfo^.deviceCount); // fix deviceCountL
+ for i:= 0 to High(Recording.SoundCard) do
+ begin
+ // convert API-specific device-index to global index
+ deviceIndex := Pa_HostApiDeviceIndexToDeviceIndex(apiIndex, i);
+ deviceInfo := Pa_GetDeviceInfo(deviceIndex);
+
+ // current device is no input device -> skip
+ if(deviceInfo^.maxInputChannels <= 0) then
+ continue;
+
+ // TODO: free object on termination
+ paSoundCard := TPortaudioSoundCard.Create();
+ Recording.SoundCard[SC] := paSoundCard;
+
+ // retrieve device-name
+ deviceName := deviceInfo^.name;
+ paSoundCard.Description := deviceName;
+ paSoundCard.DeviceIndex := deviceIndex;
+
+ // setup desired input parameters
+ with inputParams do begin
+ device := deviceIndex;
+ channelCount := 2;
+ sampleFormat := paInt16;
+ suggestedLatency := deviceInfo^.defaultLowInputLatency;
+ hostApiSpecificStreamInfo := nil;
+ end;
+
+ // check if device supports our input-format
+ err := Pa_IsFormatSupported(@inputParams, nil, sampleRate);
+ if(err <> 0) then begin
+ // format not supported -> skip
+ errMsg := Pa_GetErrorText(err);
+ Log.LogError('Portaudio.InitializeRecord, device: "'+ deviceName +'" '
+ + '('+ errMsg +')');
+ paSoundCard.Free();
+ continue;
+ end;
+
+ // TODO: retry with mono if stereo is not supported
+ // TODO: retry with input-latency set to 20ms (defaultLowInputLatency might
+ // not be set correctly in OSS)
+
+ err := Pa_OpenStream(stream, @inputParams, nil, sampleRate,
+ paFramesPerBufferUnspecified, paNoFlag, @MicrophoneCallback, nil);
+ if(err <> paNoError) then begin
+ // unable to open device -> skip
+ errMsg := Pa_GetErrorText(err);
+ Log.LogError('Portaudio.InitializeRecord, device: "'+ deviceName +'" '
+ + '('+ errMsg +')');
+ paSoundCard.Free();
+ continue;
+ end;
+
+
+ {$IFDEF UsePortmixer}
+
+ // use default mixer
+ mixer := Px_OpenMixer(stream, 0);
+
+ // get input count
+ inputCnt := Px_GetNumInputSources(mixer);
+ SetLength(paSoundCard.Input, inputCnt);
+
+ // get input names
+ for SCI := 0 to inputCnt-1 do
+ begin
+ inputName := Px_GetInputSourceName(mixer, SCI);
+ paSoundCard.Input[SCI].Name := inputName;
+ end;
+
+ Px_CloseMixer(mixer);
+
+ {$ELSE} // !UsePortmixer
+
+ //Pa_StartStream(stream);
+ // TODO: check if callback was called (this problem may occur on some devices)
+ //Pa_StopStream(stream);
+
+ Pa_CloseStream(stream);
+
+ // create a standard input source
+ SetLength(paSoundCard.Input, 1);
+ paSoundCard.Input[0].Name := 'Standard';
+
+ {$ENDIF}
+
+ // use default input source
+ paSoundCard.InputSelected := 0;
+
+ Inc(SC);
+ end;
+
+ // adjust size to actual input-device count
+ SetLength(Recording.SoundCard, SC);
+
+ Log.LogStatus('#Soundcards: ' + inttostr(SC), 'Portaudio');
+
+ {
+ SoundCard[SC].InputSelected := Mic[Device];
+ }
+end;
+
+// TODO: code is used by all IAudioInput implementors
+// -> move to a common superclass (TAudioInput_Generic?)
+procedure TAudio_Portaudio.CaptureStart;
+var
+ S: integer;
+ SC: integer;
+ PlayerLeft, PlayerRight: integer;
+ CaptureSoundLeft, CaptureSoundRight: TSound;
+begin
+ for S := 0 to High(Recording.Sound) do
+ Recording.Sound[S].BufferLong[0].Clear;
+
+ for SC := 0 to High(Ini.CardList) do begin
+ PlayerLeft := Ini.CardList[SC].ChannelL-1;
+ PlayerRight := Ini.CardList[SC].ChannelR-1;
+ if PlayerLeft >= PlayersPlay then PlayerLeft := -1;
+ if PlayerRight >= PlayersPlay then PlayerRight := -1;
+ if (PlayerLeft > -1) or (PlayerRight > -1) then begin
+ if (PlayerLeft > -1) then
+ CaptureSoundLeft := Recording.Sound[PlayerLeft]
+ else
+ CaptureSoundLeft := nil;
+ if (PlayerRight > -1) then
+ CaptureSoundRight := Recording.Sound[PlayerRight]
+ else
+ CaptureSoundRight := nil;
+
+ CaptureCard(SC, CaptureSoundLeft, CaptureSoundRight);
+ end;
+ end;
+end;
+
+// TODO: code is used by all IAudioInput implementors
+// -> move to a common superclass (TAudioInput_Generic?)
+procedure TAudio_Portaudio.CaptureStop;
+var
+ SC: integer;
+ PlayerLeft: integer;
+ PlayerRight: integer;
+begin
+
+ for SC := 0 to High(Ini.CardList) do begin
+ PlayerLeft := Ini.CardList[SC].ChannelL-1;
+ PlayerRight := Ini.CardList[SC].ChannelR-1;
+ if PlayerLeft >= PlayersPlay then PlayerLeft := -1;
+ if PlayerRight >= PlayersPlay then PlayerRight := -1;
+ if (PlayerLeft > -1) or (PlayerRight > -1) then
+ StopCard(SC);
+ end;
+
+end;
+
+{*
+ * Portaudio input capture callback.
+ *}
+function MicrophoneCallback(input: Pointer; output: Pointer; frameCount: Longword;
+ timeInfo: PPaStreamCallbackTimeInfo; statusFlags: TPaStreamCallbackFlags;
+ inputDevice: Pointer): Integer; cdecl;
+begin
+ Recording.HandleMicrophoneData(input, frameCount*4, inputDevice);
+ result := paContinue;
+end;
+
+{*
+ * Start input-capturing on Soundcard specified by Card.
+ * Params:
+ * Card - soundcard index in Recording.SoundCard array
+ * CaptureSoundLeft - sound(-buffer) used for left channel capture data
+ * CaptureSoundRight - sound(-buffer) used for right channel capture data
+ *}
+procedure TAudio_Portaudio.CaptureCard(Card: byte; CaptureSoundLeft, CaptureSoundRight: TSound);
+var
+ Error: TPaError;
+ ErrorMsg: string;
+ inputParams: TPaStreamParameters;
+ deviceInfo: PPaDeviceInfo;
+ stream: PPaStream;
+ paSoundCard: TPortaudioSoundCard;
+begin
+ paSoundCard := TPortaudioSoundCard(Recording.SoundCard[Card]);
+ paSoundCard.CaptureSoundLeft := CaptureSoundLeft;
+ paSoundCard.CaptureSoundRight := CaptureSoundRight;
+
+ // get input latency info
+ deviceInfo := Pa_GetDeviceInfo(paSoundCard.DeviceIndex);
+
+ // set input stream parameters
+ with inputParams do begin
+ device := paSoundCard.DeviceIndex;
+ channelCount := 2;
+ sampleFormat := paInt16;
+ suggestedLatency := deviceInfo^.defaultLowInputLatency;
+ hostApiSpecificStreamInfo := nil;
+ end;
+
+ Log.LogStatus(inttostr(paSoundCard.DeviceIndex), 'Portaudio');
+ Log.LogStatus(floattostr(deviceInfo^.defaultLowInputLatency), 'Portaudio');
+
+ // open input stream
+ Error := Pa_OpenStream(stream, @inputParams, nil, sampleRate,
+ paFramesPerBufferUnspecified, paNoFlag,
+ @MicrophoneCallback, Pointer(paSoundCard));
+ if(Error <> paNoError) then begin
+ ErrorMsg := Pa_GetErrorText(Error);
+ Log.CriticalError('TAudio_Portaudio.CaptureCard('+ IntToStr(Card) +'): Error opening stream: ' + ErrorMsg);
+ //Halt;
+ end;
+
+ paSoundCard.RecordStream := stream;
+
+ // start capture
+ Error := Pa_StartStream(stream);
+ if(Error <> paNoError) then begin
+ Pa_CloseStream(stream);
+ ErrorMsg := Pa_GetErrorText(Error);
+ Log.CriticalError('TAudio_Portaudio.CaptureCard('+ IntToStr(Card) +'): Error starting stream: ' + ErrorMsg);
+ //Halt;
+ end;
+end;
+
+{*
+ * Stop input-capturing on Soundcard specified by Card.
+ * Params:
+ * Card - soundcard index in Recording.SoundCard array
+ *}
+procedure TAudio_Portaudio.StopCard(Card: byte);
+var
+ stream: PPaStream;
+ paSoundCard: TPortaudioSoundCard;
+begin
+ paSoundCard := TPortaudioSoundCard(Recording.SoundCard[Card]);
+ stream := paSoundCard.RecordStream;
+ if(stream <> nil) then begin
+ Pa_StopStream(stream);
+ Pa_CloseStream(stream);
+ end;
+end;
+
+
+initialization
+ singleton_MusicPortaudio := TAudio_Portaudio.create();
+ writeln( 'UAudio_Portaudio - Register' );
+ AudioManager.add( singleton_MusicPortaudio );
+
+finalization
+ writeln( 'UAudio_Portaudio - UnRegister' );
+ AudioManager.Remove( singleton_MusicPortaudio );
+
+end.