From 6880109d9d38abfed6dececf863fdb7430fbb837 Mon Sep 17 00:00:00 2001 From: tobigun Date: Mon, 3 Mar 2008 03:16:57 +0000 Subject: - Input now supports multiple SampleRates (not fixed to 44100Hz anymore) - Input is not restricted to stereo input-devices anymore (mono and multi-channel devices work too) - Some improvements on the input-device detection - Retrieves native capture sample-rate on Vista and MacOSX with BASS (maybe this solves some problems with wrong sample-rates) - Capture-volume preview: a little modification to jay's approach. Now there are volume bars and pitch-displays. It needs some fine-tuning, maybe mog can do this if he likes. - Some indentation/clean-up/translations git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@900 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Screens/UScreenOptionsRecord.pas | 492 ++++++++++++++++++++++------- 1 file changed, 386 insertions(+), 106 deletions(-) (limited to 'Game/Code/Screens/UScreenOptionsRecord.pas') diff --git a/Game/Code/Screens/UScreenOptionsRecord.pas b/Game/Code/Screens/UScreenOptionsRecord.pas index b6fb588e..3da29f43 100644 --- a/Game/Code/Screens/UScreenOptionsRecord.pas +++ b/Game/Code/Screens/UScreenOptionsRecord.pas @@ -2,36 +2,75 @@ unit UScreenOptionsRecord; interface +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + {$I switches.inc} uses - UMenu, SDL, UDisplay, UMusic, UFiles, UIni, UThemes; + UThemes, + UMusic, + URecord, + UMenu; type TScreenOptionsRecord = class(TMenu) private - Card: integer; // current input device + // max. count of input-channels determined for all devices + MaxChannelCount: integer; + + // current input device + CurrentDeviceIndex: integer; + PreviewDeviceIndex: integer; + + // string arrays for select-slide options + InputSourceNames: array of string; + InputDeviceNames: array of string; + + // dynamic generated themes for channel select-sliders + SelectSlideChannelTheme: array of TThemeSelectSlide; + + // indices for widget-updates + SelectSlideInputID: integer; + SelectSlideChannelID: array of integer; + TextPitchID: array of integer; - SelectSlideInput: integer; - SelectSlideChannelL: integer; - SelectSlideChannelR: integer; + // interaction IDs + ExitButtonIID: integer; + + // dummy data for non-available channels + ChannelToPlayerMapDummy: integer; + + // preview channel-buffers + PreviewChannel: array of TCaptureBuffer; + + procedure StartPreview; + procedure StopPreview; + procedure UpdateCard; public constructor Create; override; function Draw: boolean; override; function ParseInput(PressedKey: Cardinal; ScanCode: byte; PressedDown: Boolean): Boolean; override; procedure onShow; override; procedure onHide; override; - procedure UpdateCard; end; implementation -uses SysUtils, - UGraphic, - URecord, - UDraw, - UMain, - ULog; +uses + SysUtils, + SDL, + OpenGL12, + UGraphic, + UDraw, + UMain, + UMenuSelectSlide, + UMenuText, + UFiles, + UDisplay, + UIni, + ULog; function TScreenOptionsRecord.ParseInput(PressedKey: Cardinal; ScanCode: byte; PressedDown: Boolean): Boolean; begin @@ -52,7 +91,8 @@ begin end; SDLK_RETURN: begin - if SelInteraction = 5 then begin + if (SelInteraction = ExitButtonIID) then + begin Ini.Save; AudioPlayback.PlaySound(SoundLib.Back); FadeTo(@ScreenOptions); @@ -64,21 +104,21 @@ begin InteractPrev; SDLK_RIGHT: begin - if (SelInteraction >= 0) and (SelInteraction <= 4) then begin + if (SelInteraction >= 0) and (SelInteraction < ExitButtonIID) then + begin AudioPlayback.PlaySound(SoundLib.Option); InteractInc; end; -// if SelInteraction = 0 then UpdateCard; UpdateCard; end; SDLK_LEFT: begin - if (SelInteraction >= 0) and (SelInteraction <= 4) then begin + if (SelInteraction >= 0) and (SelInteraction < ExitButtonIID) then + begin AudioPlayback.PlaySound(SoundLib.Option); InteractDec; end; - UpdateCard; -// if SelInteraction = 0 then UpdateCard; + UpdateCard; end; end; end; @@ -86,146 +126,386 @@ end; constructor TScreenOptionsRecord.Create; var - SC: integer; - SCI: integer; + DeviceIndex: integer; + SourceIndex: integer; + ChannelIndex: integer; InputDevice: TAudioInputDevice; InputDeviceCfg: PInputDeviceConfig; + ChannelTheme: ^TThemeSelectSlide; + ButtonTheme: TThemeButton; begin inherited Create; - Card := 0; - LoadFromTheme(Theme.OptionsRecord); - SetLength(ICard, Length(AudioInputProcessor.Device)); - for SC := 0 to High(AudioInputProcessor.Device) do - ICard[SC] := AudioInputProcessor.Device[SC].Description; + // set CurrentDeviceIndex to a valid device + if (Length(AudioInputProcessor.Device) > 0) then + CurrentDeviceIndex := 0 + else + CurrentDeviceIndex := -1; - if (Card > High(AudioInputProcessor.Device)) then - Card := 0; + PreviewDeviceIndex := -1; + // init sliders if at least one device was detected if (Length(AudioInputProcessor.Device) > 0) then begin - InputDevice := AudioInputProcessor.Device[Card]; + InputDevice := AudioInputProcessor.Device[CurrentDeviceIndex]; InputDeviceCfg := @Ini.InputDeviceConfig[InputDevice.CfgIndex]; - - SetLength(IInput, Length(InputDevice.Source)); - for SCI := 0 to High(InputDevice.Source) do - IInput[SCI] := InputDevice.Source[SCI].Name; - AddSelectSlide(Theme.OptionsRecord.SelectSlideCard, Card, ICard); + // init device-selection slider + SetLength(InputDeviceNames, Length(AudioInputProcessor.Device)); + for DeviceIndex := 0 to High(AudioInputProcessor.Device) do + begin + InputDeviceNames[DeviceIndex] := AudioInputProcessor.Device[DeviceIndex].Description; + end; + // add device-selection slider (InteractionID: 0) + AddSelectSlide(Theme.OptionsRecord.SelectSlideCard, CurrentDeviceIndex, InputDeviceNames); + + // init source-selection slider + SetLength(InputSourceNames, Length(InputDevice.Source)); + for SourceIndex := 0 to High(InputDevice.Source) do + begin + InputSourceNames[SourceIndex] := InputDevice.Source[SourceIndex].Name; + end; + // add source-selection slider (InteractionID: 1) + SelectSlideInputID := AddSelectSlide(Theme.OptionsRecord.SelectSlideInput, + InputDeviceCfg.Input, InputSourceNames); + + // find max. channel count of all devices + MaxChannelCount := 0; + for DeviceIndex := 0 to High(AudioInputProcessor.Device) do + begin + if (AudioInputProcessor.Device[DeviceIndex].AudioFormat.Channels > MaxChannelCount) then + MaxChannelCount := AudioInputProcessor.Device[DeviceIndex].AudioFormat.Channels; + end; - SelectSlideInput := AddSelectSlide(Theme.OptionsRecord.SelectSlideInput, - InputDeviceCfg^.Input, IInput); - SelectSlideChannelL := AddSelectSlide(Theme.OptionsRecord.SelectSlideChannelL, - InputDeviceCfg^.ChannelToPlayerMap[0], IChannel); - SelectSlideChannelR := AddSelectSlide(Theme.OptionsRecord.SelectSlideChannelR, - InputDeviceCfg^.ChannelToPlayerMap[1], IChannel); + // init channel-to-player mapping sliders + SetLength(SelectSlideChannelID, MaxChannelCount); + SetLength(SelectSlideChannelTheme, MaxChannelCount); + SetLength(TextPitchID, MaxChannelCount); + + for ChannelIndex := 0 to MaxChannelCount-1 do + begin + // copy reference slide + SelectSlideChannelTheme[ChannelIndex] := + Theme.OptionsRecord.SelectSlideChannel; + // set current channel-theme + ChannelTheme := @SelectSlideChannelTheme[ChannelIndex]; + // adjust vertical position + ChannelTheme.Y := ChannelTheme.Y + ChannelIndex * ChannelTheme.H; + // append channel index to name + ChannelTheme.Text := ChannelTheme.Text + IntToStr(ChannelIndex+1); + + // add tone-pitch label + TextPitchID[ChannelIndex] := AddText( + ChannelTheme.X + ChannelTheme.W, + ChannelTheme.Y + ChannelTheme.H/2, + '-'); + + // show/hide widgets depending on whether the channel exists + if (ChannelIndex < Length(InputDeviceCfg.ChannelToPlayerMap)) then + begin + // current device has this channel + + // add slider + SelectSlideChannelID[ChannelIndex] := AddSelectSlide(ChannelTheme^, + InputDeviceCfg.ChannelToPlayerMap[ChannelIndex], IChannel); + end + else + begin + // current device does not have that many channels + + // add slider but hide it and assign a dummy variable to it + SelectSlideChannelID[ChannelIndex] := AddSelectSlide(ChannelTheme^, + ChannelToPlayerMapDummy, IChannel); + SelectsS[SelectSlideChannelID[ChannelIndex]].Visible := false; + + // hide pitch label + Text[TextPitchID[ChannelIndex]].Visible := false; + end; + end; - AddSelect(Theme.OptionsSound.SelectMicBoost, Ini.MicBoost, IMicBoost); + // TODO: move from sound-options to record-options (Themes must be changed first) + //AddSelect(Theme.OptionsSound.SelectMicBoost, Ini.MicBoost, IMicBoost); end; - AddButton(Theme.OptionsRecord.ButtonExit); - if (Length(Button[0].Text)=0) then + // add Exit-button + ButtonTheme := Theme.OptionsRecord.ButtonExit; + ButtonTheme.Y := Theme.OptionsRecord.SelectSlideChannel.Y + + MaxChannelCount * + Theme.OptionsRecord.SelectSlideChannel.H; + AddButton(ButtonTheme); + if (Length(Button[0].Text) = 0) then AddButtonText(14, 20, Theme.Options.Description[7]); + // store InteractionID + ExitButtonIID := MaxChannelCount + 2; + // set focus Interaction := 0; end; +procedure TScreenOptionsRecord.UpdateCard; +var + SourceIndex: integer; + InputDevice: TAudioInputDevice; + InputDeviceCfg: PInputDeviceConfig; + ChannelIndex: integer; +begin + Log.LogStatus('Update input-device', 'TScreenOptionsRecord.UpdateCard') ; + + StopPreview(); + + // set CurrentDeviceIndex to a valid device + if (CurrentDeviceIndex > High(AudioInputProcessor.Device)) then + CurrentDeviceIndex := 0; + + // update sliders if at least one device was detected + if (Length(AudioInputProcessor.Device) > 0) then + begin + InputDevice := AudioInputProcessor.Device[CurrentDeviceIndex]; + InputDeviceCfg := @Ini.InputDeviceConfig[InputDevice.CfgIndex]; + + // update source-selection slider + SetLength(InputSourceNames, Length(InputDevice.Source)); + for SourceIndex := 0 to High(InputDevice.Source) do + begin + InputSourceNames[SourceIndex] := InputDevice.Source[SourceIndex].Name; + end; + UpdateSelectSlideOptions(Theme.OptionsRecord.SelectSlideInput, SelectSlideInputID, + InputSourceNames, InputDeviceCfg.Input); + + // update channel-to-player mapping sliders + for ChannelIndex := 0 to MaxChannelCount-1 do + begin + // show/hide widgets depending on whether the channel exists + if (ChannelIndex < Length(InputDeviceCfg.ChannelToPlayerMap)) then + begin + // current device has this channel + + // show slider + UpdateSelectSlideOptions(SelectSlideChannelTheme[ChannelIndex], + SelectSlideChannelID[ChannelIndex], IChannel, + InputDeviceCfg.ChannelToPlayerMap[ChannelIndex]); + SelectsS[SelectSlideChannelID[ChannelIndex]].Visible := true; + + // show pitch label + Text[TextPitchID[ChannelIndex]].Visible := true; + end + else + begin + // current device does not have that many channels + + // hide slider and assign a dummy variable to it + UpdateSelectSlideOptions(SelectSlideChannelTheme[ChannelIndex], + SelectSlideChannelID[ChannelIndex], IChannel, + ChannelToPlayerMapDummy); + SelectsS[SelectSlideChannelID[ChannelIndex]].Visible := false; + + // hide pitch label + Text[TextPitchID[ChannelIndex]].Visible := false; + end; + end; + end; + + StartPreview(); +end; + procedure TScreenOptionsRecord.onShow; +var + ChannelIndex: integer; begin inherited; Interaction := 0; - writeln( 'AudioInput.CaptureStart') ; - PlayersPlay := 2; // TODO : This needs fixing - AudioInput.CaptureStart; + // create preview sound-buffers + SetLength(PreviewChannel, MaxChannelCount); + for ChannelIndex := 0 to High(PreviewChannel) do + PreviewChannel[ChannelIndex] := TCaptureBuffer.Create(); + StartPreview(); end; procedure TScreenOptionsRecord.onHide; +var + ChannelIndex: integer; begin - AudioInput.CaptureStop; + StopPreview(); + + // free preview buffers + for ChannelIndex := 0 to High(PreviewChannel) do + PreviewChannel[ChannelIndex].Free; + SetLength(PreviewChannel, 0); end; -procedure TScreenOptionsRecord.UpdateCard; +procedure TScreenOptionsRecord.StartPreview; var - SourceIndex: integer; - InputDevice: TAudioInputDevice; - InputDeviceCfg: PInputDeviceConfig; + ChannelIndex: integer; + Device: TAudioInputDevice; begin - Log.LogStatus('Update input-device', 'TScreenOptionsRecord.UpdateCard') ; - - AudioInput.CaptureStop; - - if (Card > High(AudioInputProcessor.Device)) then - Card := 0; - - if (Length(AudioInputProcessor.Device) > 0) then + if ((CurrentDeviceIndex >= 0) and + (CurrentDeviceIndex <= High(AudioInputProcessor.Device))) then begin - InputDevice := AudioInputProcessor.Device[Card]; - InputDeviceCfg := @Ini.InputDeviceConfig[InputDevice.CfgIndex]; - - SetLength(IInput, Length(InputDevice.Source)); - for SourceIndex := 0 to High(InputDevice.Source) do begin - IInput[SourceIndex] := InputDevice.Source[SourceIndex].Name; + Device := AudioInputProcessor.Device[CurrentDeviceIndex]; + // set preview channel as active capture channel + for ChannelIndex := 0 to High(Device.CaptureChannel) do + begin + PreviewChannel[ChannelIndex].Clear(); + Device.LinkCaptureBuffer(ChannelIndex, PreviewChannel[ChannelIndex]); end; - - UpdateSelectSlideOptions(Theme.OptionsRecord.SelectSlideInput, SelectSlideInput, IInput, - InputDeviceCfg^.Input); - UpdateSelectSlideOptions(Theme.OptionsRecord.SelectSlideChannelL, SelectSlideChannelL, IChannel, - InputDeviceCfg^.ChannelToPlayerMap[0]); - UpdateSelectSlideOptions(Theme.OptionsRecord.SelectSlideChannelR, SelectSlideChannelR, IChannel, - InputDeviceCfg^.ChannelToPlayerMap[1]); + Device.Start(); + PreviewDeviceIndex := CurrentDeviceIndex; end; +end; - AudioInput.CaptureStart; +procedure TScreenOptionsRecord.StopPreview; +var + ChannelIndex: integer; + Device: TAudioInputDevice; +begin + if ((PreviewDeviceIndex >= 0) and + (PreviewDeviceIndex <= High(AudioInputProcessor.Device))) then + begin + Device := AudioInputProcessor.Device[PreviewDeviceIndex]; + Device.Stop; + for ChannelIndex := 0 to High(Device.CaptureChannel) do + Device.CaptureChannel[ChannelIndex] := nil; + end; + PreviewDeviceIndex := -1; end; function TScreenOptionsRecord.Draw: boolean; +var + i: integer; + x1, x2, y1, y2: real; + R, G, B, RD, GD, BD: real; + ChannelIndex: integer; + Device: TAudioInputDevice; + DeviceCfg: PInputDeviceConfig; + SelectSlide: TSelectSlide; + ToneBoxWidth: real; begin DrawBG; DrawFG; - // TODO : this needs to be positioned correctly - if PlayersPlay = 1 then - SingDrawOscilloscope(190 + 10*ScreenX, 55, 180, 40, 0); - - if PlayersPlay = 2 then begin - SingDrawOscilloscope(190 + 10*ScreenX, 55, 180, 40, 0); - SingDrawOscilloscope(425 + 10*ScreenX, 55, 180, 40, 1); - end; + if ((PreviewDeviceIndex >= 0) and + (PreviewDeviceIndex <= High(AudioInputProcessor.Device))) then + begin + Device := AudioInputProcessor.Device[PreviewDeviceIndex]; + DeviceCfg := @Ini.InputDeviceConfig[Device.CfgIndex]; + + glBegin(GL_QUADS); + for ChannelIndex := 0 to High(Device.CaptureChannel) do + begin + // load player color mapped to current input channel + if (DeviceCfg.ChannelToPlayerMap[ChannelIndex] > 0) then + begin + // set mapped channel to corresponding player-color + LoadColor(R, G, B, 'P'+ IntToStr(DeviceCfg.ChannelToPlayerMap[ChannelIndex]) + 'Dark'); + end + else + begin + // set non-mapped channel to white + R := 1; G := 1; B := 1; + end; - if PlayersPlay = 4 then begin - if ScreenAct = 1 then begin - SingDrawOscilloscope(190 + 10*ScreenX, 55, 180, 40, 0); - SingDrawOscilloscope(425 + 10*ScreenX, 55, 180, 40, 1); - end; - if ScreenAct = 2 then begin - SingDrawOscilloscope(190 + 10*ScreenX, 55, 180, 40, 2); - SingDrawOscilloscope(425 + 10*ScreenX, 55, 180, 40, 3); - end; - end; + // dark player colors + RD := 0.2 * R; + GD := 0.2 * G; + BD := 0.2 * B; + + // channel select slide + SelectSlide := SelectsS[SelectSlideChannelID[ChannelIndex]]; + + ////////// + // draw Volume + // + + // coordinates for black rect + x1 := SelectSlide.TextureSBG.X; + x2 := x1 + SelectSlide.TextureSBG.W; + y2 := SelectSlide.TextureSBG.Y + SelectSlide.TextureSBG.H; + y1 := y2 - 11; + + // draw black background-rect + glColor3f(0, 0, 0); + glVertex2f(x1, y1); + glVertex2f(x2, y1); + glVertex2f(x2, y2); + glVertex2f(x1, y2); + + // coordinates for volume bar + x1 := x1 + 1; + x2 := x1 + Trunc((SelectSlide.TextureSBG.W-4) * + PreviewChannel[ChannelIndex].MaxSampleVolume()) + 1; + y1 := y1 + 1; + y2 := y2 - 1; + + // draw volume bar + glColor3f(RD, GD, BD); + glVertex2f(x1, y1); + glColor3f(R, G, B); + glVertex2f(x2, y1); + glColor3f(R, G, B); + glVertex2f(x2, y2); + glColor3f(RD, GD, BD); + glVertex2f(x1, y2); + + ////////// + // draw Pitch + // + + // calc tone pitch + PreviewChannel[ChannelIndex].AnalyzeBuffer(); + + // coordinates for black rect + x1 := SelectSlide.TextureSBG.X; + x2 := x1 + SelectSlide.TextureSBG.W; + y1 := SelectSlide.TextureSBG.Y + SelectSlide.TextureSBG.H; + y2 := y1 + 11; + + // draw black background-rect + glColor3f(0, 0, 0); + glVertex2f(x1, y1); + glVertex2f(x2, y1); + glVertex2f(x2, y2); + glVertex2f(x1, y2); + + // coordinates for tone boxes + ToneBoxWidth := SelectSlide.TextureSBG.W / NumHalftones; + y1 := y1 + 1; + y2 := y2 - 1; + + // draw tone boxes + for i := 0 to NumHalftones-1 do + begin + x1 := SelectSlide.TextureSBG.X + i * ToneBoxWidth + 2; + x2 := x1 + ToneBoxWidth - 4; + + if ((PreviewChannel[ChannelIndex].ToneValid) and + (PreviewChannel[ChannelIndex].ToneAbs = i)) then + begin + // highlight current tone-pitch + glColor3f(1, i / (NumHalftones-1), 0) + end + else + begin + // grey other tone-pitches + glColor3f(0.3, i / (NumHalftones-1) * 0.3, 0); + end; - if PlayersPlay = 3 then begin - SingDrawOscilloscope(75 + 10*ScreenX, 95, 100, 20, 0); - SingDrawOscilloscope(370 + 10*ScreenX, 95, 100, 20, 1); - SingDrawOscilloscope(670 + 10*ScreenX, 95, 100, 20, 2); - end; + glVertex2f(x1, y1); + glVertex2f(x2, y1); + glVertex2f(x2, y2); + glVertex2f(x1, y2); + end; - if PlayersPlay = 6 then begin - if ScreenAct = 1 then begin - SingDrawOscilloscope( 75 + 10*ScreenX, 95, 100, 20, 0); - SingDrawOscilloscope(370 + 10*ScreenX, 95, 100, 20, 1); - SingDrawOscilloscope(670 + 10*ScreenX, 95, 100, 20, 2); - end; - if ScreenAct = 2 then begin - SingDrawOscilloscope( 75 + 10*ScreenX, 95, 100, 20, 3); - SingDrawOscilloscope(370 + 10*ScreenX, 95, 100, 20, 4); - SingDrawOscilloscope(670 + 10*ScreenX, 95, 100, 20, 5); + // update tone-pitch label + Text[TextPitchID[ChannelIndex]].Text := + PreviewChannel[ChannelIndex].ToneString; end; - end; + glEnd; + end; Result := True; end; -- cgit v1.2.3