unit UScreenOptionsRecord; interface {$IFDEF FPC} {$MODE Delphi} {$ENDIF} {$I switches.inc} uses UThemes, UMusic, URecord, UMenu; type TScreenOptionsRecord = class(TMenu) private // 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; // 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; CharCode: WideChar; PressedDown: Boolean): Boolean; override; procedure onShow; override; procedure onHide; override; end; implementation uses SysUtils, SDL, OpenGL12, UGraphic, UDraw, UMain, UMenuSelectSlide, UMenuText, UFiles, UDisplay, UIni, ULog; function TScreenOptionsRecord.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; begin Result := true; If (PressedDown) Then begin // Key Down // check normal keys case WideCharUpperCase(CharCode)[1] of 'Q': begin Result := false; Exit; end; end; // check special keys case PressedKey of SDLK_ESCAPE, SDLK_BACKSPACE: begin Ini.Save; AudioPlayback.PlaySound(SoundLib.Back); FadeTo(@ScreenOptions); end; SDLK_RETURN: begin if (SelInteraction = ExitButtonIID) then begin Ini.Save; AudioPlayback.PlaySound(SoundLib.Back); FadeTo(@ScreenOptions); end; end; SDLK_DOWN: InteractNext; SDLK_UP : InteractPrev; SDLK_RIGHT: begin if (SelInteraction >= 0) and (SelInteraction < ExitButtonIID) then begin AudioPlayback.PlaySound(SoundLib.Option); InteractInc; end; UpdateCard; end; SDLK_LEFT: begin if (SelInteraction >= 0) and (SelInteraction < ExitButtonIID) then begin AudioPlayback.PlaySound(SoundLib.Option); InteractDec; end; UpdateCard; end; end; end; end; constructor TScreenOptionsRecord.Create; var DeviceIndex: integer; SourceIndex: integer; ChannelIndex: integer; InputDevice: TAudioInputDevice; InputDeviceCfg: PInputDeviceConfig; ChannelTheme: ^TThemeSelectSlide; ButtonTheme: TThemeButton; begin inherited Create; LoadFromTheme(Theme.OptionsRecord); // set CurrentDeviceIndex to a valid device if (Length(AudioInputProcessor.Device) > 0) then CurrentDeviceIndex := 0 else CurrentDeviceIndex := -1; PreviewDeviceIndex := -1; // init sliders if at least one device was detected if (Length(AudioInputProcessor.Device) > 0) then begin InputDevice := AudioInputProcessor.Device[CurrentDeviceIndex]; InputDeviceCfg := @Ini.InputDeviceConfig[InputDevice.CfgIndex]; // 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; // 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; // TODO: move from sound-options to record-options (Themes must be changed first) //AddSelect(Theme.OptionsSound.SelectMicBoost, Ini.MicBoost, IMicBoost); end; // 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; // 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 StopPreview(); // free preview buffers for ChannelIndex := 0 to High(PreviewChannel) do PreviewChannel[ChannelIndex].Free; SetLength(PreviewChannel, 0); end; procedure TScreenOptionsRecord.StartPreview; var ChannelIndex: integer; Device: TAudioInputDevice; begin if ((CurrentDeviceIndex >= 0) and (CurrentDeviceIndex <= High(AudioInputProcessor.Device))) then begin 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; Device.Start(); PreviewDeviceIndex := CurrentDeviceIndex; end; end; 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; Volume: single; begin DrawBG; DrawFG; 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; // 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); Volume := PreviewChannel[ChannelIndex].MaxSampleVolume(); // coordinates for volume bar x1 := x1 + 1; x2 := x1 + Trunc((SelectSlide.TextureSBG.W-4) * Volume) + 1; y1 := y1 + 1; y2 := y2 - 1; // draw volume bar glColor3f(RD, GD, BD); glVertex2f(x1, y1); glVertex2f(x1, y2); glColor3f(R, G, B); glVertex2f(x2, y2); glVertex2f(x2, y1); ////////// // 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; glVertex2f(x1, y1); glVertex2f(x2, y1); glVertex2f(x2, y2); glVertex2f(x1, y2); end; // update tone-pitch label Text[TextPitchID[ChannelIndex]].Text := PreviewChannel[ChannelIndex].ToneString; end; glEnd; end; Result := True; end; end.