From 2e41ddbf9dc84772cdc9c8a9ab80d4861d6e4177 Mon Sep 17 00:00:00 2001 From: tobigun Date: Fri, 7 Dec 2007 02:32:44 +0000 Subject: Fixed some linux visualizer errors git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@694 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UAudio_portaudio.pas | 863 ++++++++++++++++----------------- 1 file changed, 429 insertions(+), 434 deletions(-) (limited to 'Game/Code/Classes/UAudio_portaudio.pas') 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. -- cgit v1.2.3