diff options
Diffstat (limited to 'Game/Code/Classes/UAudio_portaudio.pas')
-rw-r--r-- | Game/Code/Classes/UAudio_portaudio.pas | 863 |
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. |