aboutsummaryrefslogblamecommitdiffstats
path: root/Game/Code/Screens/UScreenOptionsRecord.pas
blob: 6ee200fa9ec23e28ad0fcc6dbd1ac719b14535f6 (plain) (tree)
1
2
3
4
5
6
7
8


                           


                 

                  


           


                                      
















                                                                 
 










                                               

                                           
                                                                                                                 
                                    


               











                    
 
                                                                                                                   


                        
                         
                                           
                           
                
             


                          







                                                  
                                                   









                                                    
                                                                             

                                                      


                      
                                                                             

                                                      
                      





                                         

                         
                                      
                                    

                    
                                      



                                                   
 
                            
 
                                                      
                                                   
                                                                   
                                                                    
 
























                                                                                               
 











































                                                                            
 
                                                                                      
       





                                                              
                                                         
                                        
 
               

                    
































































                                                                                       
                                       
                         
      
             
                    
 


                                                             
 
                  

                                       
                         
      




                                                   
     
                                             
    
                             
      
                                                                     
        




                                                                            
         
                                              
       
     
 












                                                                     

                                             







                                 
                  


          


















                                                                                                  
 























                                                                     
                                                                  
                                      
                                                                     




                               
                            
                            
                            









































                                                                   
 



                              
 

                                                     
           
           




                  
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.