From b5a738fa52c8b0f2212deb5febd2d7f0b8f6544f Mon Sep 17 00:00:00 2001 From: tobigun Date: Fri, 9 May 2008 19:19:28 +0000 Subject: - input-source selection works now (with bass or portaudio with portmixer) - audio-effects (DSP) interface for audio-playback plus a simple voice removal example (does not sound that good) - FFMpeg support for BASS - audio-clock for FFMpeg for GetPosition and synchronisation - more compatible seeking in FFMpeg - clean termination of the audio interfaces/streams (especially ffmpeg) - Audio output device enumeration (selection will be added later to the sounds option screen) - display of threshold and volume in the record-options screen - threshold and volume can be changed with the 'T' (threshold) and '+'/'-' (source volume) keys - added a FadeIn() method to the IAudioPlayback interface - some minor changes to the audio classes/screens - new base-class for audio-playback classes (used by bass, portaudio and sdl) git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1078 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Screens/UScreenOptionsRecord.pas | 532 +++++++++++++++++++++-------- Game/Code/Screens/UScreenOptionsSound.pas | 2 +- Game/Code/Screens/UScreenSong.pas | 95 +++--- 3 files changed, 438 insertions(+), 191 deletions(-) (limited to 'Game/Code/Screens') diff --git a/Game/Code/Screens/UScreenOptionsRecord.pas b/Game/Code/Screens/UScreenOptionsRecord.pas index b9000991..8e3d0f67 100644 --- a/Game/Code/Screens/UScreenOptionsRecord.pas +++ b/Game/Code/Screens/UScreenOptionsRecord.pas @@ -15,6 +15,17 @@ uses UMenu; type + TDrawState = record + ChannelIndex: integer; + R, G, B: real; // mapped player color (normal) + RD, GD, BD: real; // mapped player color (dark) + end; + + TPeakInfo = record + Volume: single; + Time: cardinal; + end; + TScreenOptionsRecord = class(TMenu) private // max. count of input-channels determined for all devices @@ -32,11 +43,11 @@ type SelectSlideChannelTheme: array of TThemeSelectSlide; // indices for widget-updates - SelectSlideInputID: integer; + SelectInputSourceID: integer; SelectSlideChannelID: array of integer; TextPitchID: array of integer; - // interaction IDs + // interaction IDs ExitButtonIID: integer; // dummy data for non-available channels @@ -44,10 +55,19 @@ type // preview channel-buffers PreviewChannel: array of TCaptureBuffer; + ChannelPeak: array of TPeakInfo; + + // Device source volume + SourceVolume: single; + NextVolumePollTime: cardinal; procedure StartPreview; procedure StopPreview; - procedure UpdateCard; + procedure UpdateInputDevice; + procedure ChangeVolume(VolumeChange: integer); + procedure DrawVolume(x, y, Width, Height: single); + procedure DrawVUMeter(const State: TDrawState; x, y, Width, Height: single); + procedure DrawPitch(const State: TDrawState; x, y, Width, Height: single); public constructor Create; override; function Draw: boolean; override; @@ -56,10 +76,21 @@ type procedure onHide; override; end; +const + PeakDecay = 0.2; // strength of peak-decay (reduction after one sec) + +const + BarHeight = 11; // height of each bar (volume/vu-meter/pitch) + BarUpperSpacing = 1; // spacing between a bar-area and the previous widget + BarLowerSpacing = 3; // spacing between a bar-area and the next widget + SourceBarsTotalHeight = BarHeight + BarUpperSpacing + BarLowerSpacing; + ChannelBarsTotalHeight = 2*BarHeight + BarUpperSpacing + BarLowerSpacing; + implementation uses SysUtils, + Math, SDL, gl, UGraphic, @@ -84,8 +115,27 @@ begin Result := false; Exit; end; + '+': + begin + // FIXME: add a nice volume-slider instead + // or at least provide visualization and acceleration if the user holds the key pressed. + ChangeVolume(2); + end; + '-': + begin + // FIXME: add a nice volume-slider instead + // or at least provide visualization and acceleration if the user holds the key pressed. + ChangeVolume(-2); + end; + 'T': + begin + if ((SDL_GetModState() and KMOD_SHIFT) <> 0) then + Ini.ThresholdIndex := (Ini.ThresholdIndex + Length(IThresholdVals) - 1) mod Length(IThresholdVals) + else + Ini.ThresholdIndex := (Ini.ThresholdIndex + 1) mod Length(IThresholdVals); + end; end; - + // check special keys case PressedKey of SDLK_ESCAPE, @@ -115,7 +165,7 @@ begin AudioPlayback.PlaySound(SoundLib.Option); InteractInc; end; - UpdateCard; + UpdateInputDevice; end; SDLK_LEFT: begin @@ -124,7 +174,7 @@ begin AudioPlayback.PlaySound(SoundLib.Option); InteractDec; end; - UpdateCard; + UpdateInputDevice; end; end; end; @@ -139,13 +189,14 @@ var InputDeviceCfg: PInputDeviceConfig; ChannelTheme: ^TThemeSelectSlide; ButtonTheme: TThemeButton; + WidgetYPos: integer; begin inherited Create; LoadFromTheme(Theme.OptionsRecord); // set CurrentDeviceIndex to a valid device - if (Length(AudioInputProcessor.Device) > 0) then + if (Length(AudioInputProcessor.DeviceList) > 0) then CurrentDeviceIndex := 0 else CurrentDeviceIndex := -1; @@ -153,16 +204,16 @@ begin PreviewDeviceIndex := -1; // init sliders if at least one device was detected - if (Length(AudioInputProcessor.Device) > 0) then + if (Length(AudioInputProcessor.DeviceList) > 0) then begin - InputDevice := AudioInputProcessor.Device[CurrentDeviceIndex]; + InputDevice := AudioInputProcessor.DeviceList[CurrentDeviceIndex]; InputDeviceCfg := @Ini.InputDeviceConfig[InputDevice.CfgIndex]; // init device-selection slider - SetLength(InputDeviceNames, Length(AudioInputProcessor.Device)); - for DeviceIndex := 0 to High(AudioInputProcessor.Device) do + SetLength(InputDeviceNames, Length(AudioInputProcessor.DeviceList)); + for DeviceIndex := 0 to High(AudioInputProcessor.DeviceList) do begin - InputDeviceNames[DeviceIndex] := AudioInputProcessor.Device[DeviceIndex].Description; + InputDeviceNames[DeviceIndex] := AudioInputProcessor.DeviceList[DeviceIndex].Name; end; // add device-selection slider (InteractionID: 0) AddSelectSlide(Theme.OptionsRecord.SelectSlideCard, CurrentDeviceIndex, InputDeviceNames); @@ -174,15 +225,20 @@ begin InputSourceNames[SourceIndex] := InputDevice.Source[SourceIndex].Name; end; // add source-selection slider (InteractionID: 1) - SelectSlideInputID := AddSelectSlide(Theme.OptionsRecord.SelectSlideInput, + SelectInputSourceID := AddSelectSlide(Theme.OptionsRecord.SelectSlideInput, InputDeviceCfg.Input, InputSourceNames); + // add space for source volume bar + WidgetYPos := Theme.OptionsRecord.SelectSlideInput.Y + + Theme.OptionsRecord.SelectSlideInput.H + + SourceBarsTotalHeight; + // find max. channel count of all devices MaxChannelCount := 0; - for DeviceIndex := 0 to High(AudioInputProcessor.Device) do + for DeviceIndex := 0 to High(AudioInputProcessor.DeviceList) do begin - if (AudioInputProcessor.Device[DeviceIndex].AudioFormat.Channels > MaxChannelCount) then - MaxChannelCount := AudioInputProcessor.Device[DeviceIndex].AudioFormat.Channels; + if (AudioInputProcessor.DeviceList[DeviceIndex].AudioFormat.Channels > MaxChannelCount) then + MaxChannelCount := AudioInputProcessor.DeviceList[DeviceIndex].AudioFormat.Channels; end; // init channel-to-player mapping sliders @@ -198,7 +254,9 @@ begin // set current channel-theme ChannelTheme := @SelectSlideChannelTheme[ChannelIndex]; // adjust vertical position - ChannelTheme.Y := ChannelTheme.Y + ChannelIndex * ChannelTheme.H; + ChannelTheme.Y := WidgetYPos; + // calc size of next slide (add space for bars) + WidgetYPos := WidgetYPos + ChannelTheme.H + ChannelBarsTotalHeight; // append channel index to name ChannelTheme.Text := ChannelTheme.Text + IntToStr(ChannelIndex+1); @@ -215,7 +273,7 @@ begin // add slider SelectSlideChannelID[ChannelIndex] := AddSelectSlide(ChannelTheme^, - InputDeviceCfg.ChannelToPlayerMap[ChannelIndex], IChannel); + InputDeviceCfg.ChannelToPlayerMap[ChannelIndex], IChannelPlayer); end else begin @@ -223,52 +281,50 @@ begin // add slider but hide it and assign a dummy variable to it SelectSlideChannelID[ChannelIndex] := AddSelectSlide(ChannelTheme^, - ChannelToPlayerMapDummy, IChannel); + ChannelToPlayerMapDummy, IChannelPlayer); 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; + ButtonTheme.Y := WidgetYPos; AddButton(ButtonTheme); if (Length(Button[0].Text) = 0) then AddButtonText(14, 20, Theme.Options.Description[7]); // store InteractionID - ExitButtonIID := MaxChannelCount + 2; + if (Length(AudioInputProcessor.DeviceList) > 0) then + ExitButtonIID := MaxChannelCount + 2 + else + ExitButtonIID := 0; // set focus Interaction := 0; end; -procedure TScreenOptionsRecord.UpdateCard; +procedure TScreenOptionsRecord.UpdateInputDevice; var SourceIndex: integer; InputDevice: TAudioInputDevice; InputDeviceCfg: PInputDeviceConfig; ChannelIndex: integer; begin - Log.LogStatus('Update input-device', 'TScreenOptionsRecord.UpdateCard') ; + //Log.LogStatus('Update input-device', 'TScreenOptionsRecord.UpdateCard') ; StopPreview(); // set CurrentDeviceIndex to a valid device - if (CurrentDeviceIndex > High(AudioInputProcessor.Device)) then + if (CurrentDeviceIndex > High(AudioInputProcessor.DeviceList)) then CurrentDeviceIndex := 0; // update sliders if at least one device was detected - if (Length(AudioInputProcessor.Device) > 0) then + if (Length(AudioInputProcessor.DeviceList) > 0) then begin - InputDevice := AudioInputProcessor.Device[CurrentDeviceIndex]; + InputDevice := AudioInputProcessor.DeviceList[CurrentDeviceIndex]; InputDeviceCfg := @Ini.InputDeviceConfig[InputDevice.CfgIndex]; // update source-selection slider @@ -277,7 +333,7 @@ begin begin InputSourceNames[SourceIndex] := InputDevice.Source[SourceIndex].Name; end; - UpdateSelectSlideOptions(Theme.OptionsRecord.SelectSlideInput, SelectSlideInputID, + UpdateSelectSlideOptions(Theme.OptionsRecord.SelectSlideInput, SelectInputSourceID, InputSourceNames, InputDeviceCfg.Input); // update channel-to-player mapping sliders @@ -290,7 +346,7 @@ begin // show slider UpdateSelectSlideOptions(SelectSlideChannelTheme[ChannelIndex], - SelectSlideChannelID[ChannelIndex], IChannel, + SelectSlideChannelID[ChannelIndex], IChannelPlayer, InputDeviceCfg.ChannelToPlayerMap[ChannelIndex]); SelectsS[SelectSlideChannelID[ChannelIndex]].Visible := true; @@ -303,7 +359,7 @@ begin // hide slider and assign a dummy variable to it UpdateSelectSlideOptions(SelectSlideChannelTheme[ChannelIndex], - SelectSlideChannelID[ChannelIndex], IChannel, + SelectSlideChannelID[ChannelIndex], IChannelPlayer, ChannelToPlayerMapDummy); SelectsS[SelectSlideChannelID[ChannelIndex]].Visible := false; @@ -316,6 +372,31 @@ begin StartPreview(); end; +procedure TScreenOptionsRecord.ChangeVolume(VolumeChange: integer); +var + InputDevice: TAudioInputDevice; + Volume: integer; +begin + // validate CurrentDeviceIndex + if ((CurrentDeviceIndex < 0) or + (CurrentDeviceIndex > High(AudioInputProcessor.DeviceList))) then + begin + Exit; + end; + + InputDevice := AudioInputProcessor.DeviceList[CurrentDeviceIndex]; + if not assigned(InputDevice) then + Exit; + + // set new volume + Volume := InputDevice.GetVolume() + VolumeChange; + InputDevice.SetVolume(Volume); + //DebugWriteln('Volume: ' + inttostr(InputDevice.GetVolume)); + + // volume must be polled again + NextVolumePollTime := 0; +end; + procedure TScreenOptionsRecord.onShow; var ChannelIndex: integer; @@ -329,6 +410,8 @@ begin for ChannelIndex := 0 to High(PreviewChannel) do PreviewChannel[ChannelIndex] := TCaptureBuffer.Create(); + SetLength(ChannelPeak, MaxChannelCount); + StartPreview(); end; @@ -342,6 +425,7 @@ begin for ChannelIndex := 0 to High(PreviewChannel) do PreviewChannel[ChannelIndex].Free; SetLength(PreviewChannel, 0); + SetLength(ChannelPeak, 0); end; procedure TScreenOptionsRecord.StartPreview; @@ -350,17 +434,21 @@ var Device: TAudioInputDevice; begin if ((CurrentDeviceIndex >= 0) and - (CurrentDeviceIndex <= High(AudioInputProcessor.Device))) then + (CurrentDeviceIndex <= High(AudioInputProcessor.DeviceList))) then begin - Device := AudioInputProcessor.Device[CurrentDeviceIndex]; + Device := AudioInputProcessor.DeviceList[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]); + FillChar(ChannelPeak[ChannelIndex], SizeOf(TPeakInfo), 0); end; Device.Start(); PreviewDeviceIndex := CurrentDeviceIndex; + + // volume must be polled again + NextVolumePollTime := 0; end; end; @@ -370,9 +458,9 @@ var Device: TAudioInputDevice; begin if ((PreviewDeviceIndex >= 0) and - (PreviewDeviceIndex <= High(AudioInputProcessor.Device))) then + (PreviewDeviceIndex <= High(AudioInputProcessor.DeviceList))) then begin - Device := AudioInputProcessor.Device[PreviewDeviceIndex]; + Device := AudioInputProcessor.DeviceList[PreviewDeviceIndex]; Device.Stop; for ChannelIndex := 0 to High(Device.CaptureChannel) do Device.CaptureChannel[ChannelIndex] := nil; @@ -380,137 +468,281 @@ begin PreviewDeviceIndex := -1; end; + +procedure TScreenOptionsRecord.DrawVolume(x, y, Width, Height: single); +var + x1, y1, x2, y2: single; + VolBarInnerWidth: integer; +const + VolBarInnerHSpacing = 2; + VolBarInnerVSpacing = 1; +begin + // coordinates for black rect + x1 := x; + y1 := y; + x2 := x1 + Width; + y2 := y1 + Height; + + // draw black background-rect + glColor4f(0, 0, 0, 0.8); + glBegin(GL_QUADS); + glVertex2f(x1, y1); + glVertex2f(x2, y1); + glVertex2f(x2, y2); + glVertex2f(x1, y2); + glEnd(); + + VolBarInnerWidth := Trunc(Width - 2*VolBarInnerHSpacing); + + // coordinates for first half of the volume bar + x1 := x + VolBarInnerHSpacing; + x2 := x1 + VolBarInnerWidth * SourceVolume; + y1 := y1 + VolBarInnerVSpacing; + y2 := y2 - VolBarInnerVSpacing; + + // draw volume-bar + glBegin(GL_QUADS); + // draw volume bar + glColor3f(0.4, 0.3, 0.3); + glVertex2f(x1, y1); + glVertex2f(x1, y2); + glColor3f(1, 0.1, 0.1); + glVertex2f(x2, y2); + glVertex2f(x2, y1); + glEnd(); + + { not needed anymore + // coordinates for separator + x1 := x + VolBarInnerHSpacing; + x2 := x1 + VolBarInnerWidth; + + // draw separator + glBegin(GL_LINE_STRIP); + glColor4f(0.1, 0.1, 0.1, 0.2); + glVertex2f(x1, y2); + glColor4f(0.4, 0.4, 0.4, 0.2); + glVertex2f((x1+x2)/2, y2); + glColor4f(0.1, 0.1, 0.1, 0.2); + glVertex2f(x2, y2); + glEnd(); + } +end; + +procedure TScreenOptionsRecord.DrawVUMeter(const State: TDrawState; x, y, Width, Height: single); +var + x1, y1, x2, y2: single; + Volume, PeakVolume: single; + Delta: single; + VolBarInnerWidth: integer; +const + VolBarInnerHSpacing = 2; + VolBarInnerVSpacing = 1; +begin + // coordinates for black rect + x1 := x; + y1 := y; + x2 := x1 + Width; + y2 := y1 + Height; + + // draw black background-rect + glColor4f(0, 0, 0, 0.8); + glBegin(GL_QUADS); + glVertex2f(x1, y1); + glVertex2f(x2, y1); + glVertex2f(x2, y2); + glVertex2f(x1, y2); + glEnd(); + + VolBarInnerWidth := Trunc(Width - 2*VolBarInnerHSpacing); + + // vertical positions + y1 := y1 + VolBarInnerVSpacing; + y2 := y2 - VolBarInnerVSpacing; + + // coordinates for bevel + x1 := x + VolBarInnerHSpacing; + x2 := x1 + VolBarInnerWidth; + + glBegin(GL_QUADS); + Volume := PreviewChannel[State.ChannelIndex].MaxSampleVolume(); + + // coordinates for volume bar + x1 := x + VolBarInnerHSpacing; + x2 := x1 + VolBarInnerWidth * Volume; + + // draw volume bar + glColor3f(State.RD, State.GD, State.BD); + glVertex2f(x1, y1); + glVertex2f(x1, y2); + glColor3f(State.R, State.G, State.B); + glVertex2f(x2, y2); + glVertex2f(x2, y1); + + Delta := (SDL_GetTicks() - ChannelPeak[State.ChannelIndex].Time)/1000; + PeakVolume := ChannelPeak[State.ChannelIndex].Volume - Delta*Delta*PeakDecay; + + // determine new peak-volume + if (Volume > PeakVolume) then + begin + PeakVolume := Volume; + ChannelPeak[State.ChannelIndex].Volume := Volume; + ChannelPeak[State.ChannelIndex].Time := SDL_GetTicks(); + end; + + x1 := x + VolBarInnerHSpacing + VolBarInnerWidth * PeakVolume; + x2 := x1 + 2; + + // draw peak + glColor3f(0.8, 0.8, 0.8); + glVertex2f(x1, y1); + glVertex2f(x1, y2); + glVertex2f(x2, y2); + glVertex2f(x2, y1); + + // draw threshold + x1 := x + VolBarInnerHSpacing; + x2 := x1 + VolBarInnerWidth * IThresholdVals[Ini.ThresholdIndex]; + + glColor4f(0.3, 0.3, 0.3, 0.6); + glVertex2f(x1, y1); + glVertex2f(x1, y2); + glVertex2f(x2, y2); + glVertex2f(x2, y1); + glEnd(); +end; + +procedure TScreenOptionsRecord.DrawPitch(const State: TDrawState; x, y, Width, Height: single); +var + x1, y1, x2, y2: single; + i: integer; + ToneBoxWidth: real; +const + PitchBarInnerHSpacing = 2; + PitchBarInnerVSpacing = 1; +begin + // calc tone pitch + PreviewChannel[State.ChannelIndex].AnalyzeBuffer(); + + // coordinates for black rect + x1 := x; + y1 := y; + x2 := x + Width; + y2 := y + Height; + + // draw black background-rect + glColor4f(0, 0, 0, 0.8); + glBegin(GL_QUADS); + glVertex2f(x1, y1); + glVertex2f(x2, y1); + glVertex2f(x2, y2); + glVertex2f(x1, y2); + glEnd(); + + // coordinates for tone boxes + ToneBoxWidth := Width / NumHalftones; + y1 := y1 + PitchBarInnerVSpacing; + y2 := y2 - PitchBarInnerVSpacing; + + glBegin(GL_QUADS); + // draw tone boxes + for i := 0 to NumHalftones-1 do + begin + x1 := x + i * ToneBoxWidth + PitchBarInnerHSpacing; + x2 := x1 + ToneBoxWidth - 2*PitchBarInnerHSpacing; + + if ((PreviewChannel[State.ChannelIndex].ToneValid) and + (PreviewChannel[State.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; + glEnd(); + + // update tone-pitch label + Text[TextPitchID[State.ChannelIndex]].Text := + PreviewChannel[State.ChannelIndex].ToneString; +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; + BarXOffset, BarYOffset, BarWidth: real; + ChannelIndex: integer; + State: TDrawState; begin DrawBG; DrawFG; if ((PreviewDeviceIndex >= 0) and - (PreviewDeviceIndex <= High(AudioInputProcessor.Device))) then + (PreviewDeviceIndex <= High(AudioInputProcessor.DeviceList))) then begin - Device := AudioInputProcessor.Device[PreviewDeviceIndex]; + Device := AudioInputProcessor.DeviceList[PreviewDeviceIndex]; DeviceCfg := @Ini.InputDeviceConfig[Device.CfgIndex]; - glBegin(GL_QUADS); - for ChannelIndex := 0 to High(Device.CaptureChannel) do + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // update source volume + if (SDL_GetTicks() >= NextVolumePollTime) then + begin + NextVolumePollTime := SDL_GetTicks() + 500; // next poll in 500ms + SourceVolume := Device.GetVolume()/100; + end; + + // get source select slide + SelectSlide := SelectsS[SelectInputSourceID]; + BarXOffset := SelectSlide.TextureSBG.X; + BarYOffset := SelectSlide.TextureSBG.Y + SelectSlide.TextureSBG.H + BarUpperSpacing; + BarWidth := SelectSlide.TextureSBG.W; + DrawVolume(SelectSlide.TextureSBG.X, BarYOffset, BarWidth, BarHeight); + + for ChannelIndex := 0 to High(Device.CaptureChannel) do + begin + // load player color mapped to current input channel + if (DeviceCfg.ChannelToPlayerMap[ChannelIndex] > 0) then 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; + // set mapped channel to corresponding player-color + LoadColor(State.R, State.G, State.B, 'P'+ IntToStr(DeviceCfg.ChannelToPlayerMap[ChannelIndex]) + 'Dark'); + end + else + begin + // set non-mapped channel to white + State.R := 1; State.G := 1; State.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; + // dark player colors + State.RD := 0.2 * State.R; + State.GD := 0.2 * State.G; + State.BD := 0.2 * State.B; - 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; + // channel select slide + SelectSlide := SelectsS[SelectSlideChannelID[ChannelIndex]]; - glVertex2f(x1, y1); - glVertex2f(x2, y1); - glVertex2f(x2, y2); - glVertex2f(x1, y2); - end; + BarXOffset := SelectSlide.TextureSBG.X; + BarYOffset := SelectSlide.TextureSBG.Y + SelectSlide.TextureSBG.H + BarUpperSpacing; + BarWidth := SelectSlide.TextureSBG.W; - // update tone-pitch label - Text[TextPitchID[ChannelIndex]].Text := - PreviewChannel[ChannelIndex].ToneString; - end; - glEnd; + State.ChannelIndex := ChannelIndex; + + DrawVUMeter(State, BarXOffset, BarYOffset, BarWidth, BarHeight); + DrawPitch(State, BarXOffset, BarYOffset+BarHeight, BarWidth, BarHeight); + end; + + glDisable(GL_BLEND); end; Result := True; diff --git a/Game/Code/Screens/UScreenOptionsSound.pas b/Game/Code/Screens/UScreenOptionsSound.pas index c3ef523b..2d807d02 100644 --- a/Game/Code/Screens/UScreenOptionsSound.pas +++ b/Game/Code/Screens/UScreenOptionsSound.pas @@ -87,7 +87,7 @@ begin AddSelect(Theme.OptionsSound.SelectMicBoost, Ini.MicBoost, IMicBoost); // TODO - This need moving to ScreenOptionsRecord AddSelect(Theme.OptionsSound.SelectClickAssist, Ini.ClickAssist, IClickAssist); AddSelect(Theme.OptionsSound.SelectBeatClick, Ini.BeatClick, IBeatClick); - AddSelect(Theme.OptionsSound.SelectThreshold, Ini.Threshold, IThreshold); + AddSelect(Theme.OptionsSound.SelectThreshold, Ini.ThresholdIndex, IThreshold); //Song Preview AddSelectSlide(Theme.OptionsSound.SelectSlidePreviewVolume, Ini.PreviewVolume, IPreviewVolume); diff --git a/Game/Code/Screens/UScreenSong.pas b/Game/Code/Screens/UScreenSong.pas index e52b4c98..82d5100a 100644 --- a/Game/Code/Screens/UScreenSong.pas +++ b/Game/Code/Screens/UScreenSong.pas @@ -34,6 +34,8 @@ type EqualizerData: TFFTData; // moved here to avoid stack overflows EqualizerBands: array of Byte; EqualizerTime: Cardinal; + + procedure StartMusicPreview(Song: TSong); public TextArtist: integer; TextTitle: integer; @@ -51,6 +53,8 @@ type HighSpeed: boolean; CoverFull: boolean; CoverTime: real; + MusicStartTime: cardinal; + CoverX: integer; CoverY: integer; CoverW: integer; @@ -1454,19 +1458,8 @@ begin if Length(CatSongs.Song) > 0 then begin //Load Music only when Song Preview is activated if ( Ini.PreviewVolume <> 0 ) then - begin - if(AudioPlayback.Open(CatSongs.Song[Interaction].Path + CatSongs.Song[Interaction].Mp3)) then - begin - AudioPlayback.SetLoop(false); - AudioPlayback.Position := AudioPlayback.Length / 4; - AudioPlayback.Play; - - //Set Preview Volume - AudioPlayback.SetMusicVolume (Ini.PreviewVolume * 10); - {//if Music Fade is activated, Set Volume to 0 % - if (Ini.PreviewFading <> 0) then - Music.SetMusicVolume(0);} - end; + begin // to - do : new Song management + StartMusicPreview(CatSongs.Song[Interaction]); end; SetScroll; @@ -1506,7 +1499,7 @@ procedure TScreenSong.onHide; begin //When Music Fading is activated, Turn Music to 100 % If (Ini.PreviewVolume <> 100) or (Ini.PreviewFading <> 0) then - AudioPlayback.SetMusicVolume(100); + AudioPlayback.SetVolume(100); //If Preview is deactivated: Load MUsicfile now If (Ini.PreviewVolume = 0) then @@ -1557,7 +1550,7 @@ begin //Fading Functions, Only if Covertime is under 5 Seconds If (CoverTime < 5) then begin - // 0.5.0: cover fade + // cover fade if (CoverTime < 1) and (CoverTime + TimeSkip >= 1) then begin // load new texture @@ -1568,18 +1561,17 @@ begin end; //Song Fade - if (CatSongs.VisibleSongs > 0) AND (Ini.PreviewVolume <> 0) AND (Not CatSongs.Song[Interaction].Main) AND (Ini.PreviewFading <> 0) then + if (CatSongs.VisibleSongs > 0) and + (not CatSongs.Song[Interaction].Main) and + (Ini.PreviewVolume <> 0) and + (Ini.PreviewFading <> 0) then begin //Start Song Fade after a little Time, to prevent Song to be Played on Scrolling - if (CoverTime < 0.2) and (CoverTime + TimeSkip >= 0.2) then - AudioPlayback.Play; - - //Update Song Volume - if (CoverTime < Ini.PreviewFading) then - AudioPlayback.SetMusicVolume(Round (CoverTime * Ini.PreviewVolume / Ini.PreviewFading * 10)) - else - AudioPlayback.SetMusicVolume(Ini.PreviewVolume * 10); - + if ((MusicStartTime > 0) and (SDL_GetTicks() >= MusicStartTime)) then + begin + MusicStartTime := 0; + StartMusicPreview(CatSongs.Song[Interaction]); + end; end; @@ -1588,7 +1580,8 @@ begin //Update Fading Texture Button[Interaction].Texture2.Alpha := (CoverTime - 1) * 1.5; - if Button[Interaction].Texture2.Alpha > 1 then Button[Interaction].Texture2.Alpha := 1; + if Button[Interaction].Texture2.Alpha > 1 then + Button[Interaction].Texture2.Alpha := 1; end; @@ -1694,30 +1687,52 @@ begin end; *) +procedure TScreenSong.StartMusicPreview(Song: TSong); +begin + AudioPlayback.Close(); + + if not assigned(Song) then + Exit; + + if AudioPlayback.Open(Song.Path + Song.Mp3) then + begin + AudioPlayback.Position := AudioPlayback.Length / 4; + // set preview volume + if (Ini.PreviewFading = 0) then + begin + // music fade disabled: start with full volume + AudioPlayback.SetVolume(Ini.PreviewVolume * 10); + AudioPlayback.Play() + end + else + begin + // music fade enabled: start muted and fade-in + AudioPlayback.SetVolume(0); + AudioPlayback.FadeIn(Ini.PreviewFading, Ini.PreviewVolume * 10); + end; + end; +end; + //Procedure Change current played Preview procedure TScreenSong.ChangeMusic; begin //When Music Preview is avtivated -> then Change Music if (Ini.PreviewVolume <> 0) then begin - if (NOT CatSongs.Song[Interaction].Main) AND(CatSongs.VisibleSongs > 0) then + // Stop previous song + AudioPlayback.Stop; + // Disable music start delay + MusicStartTime := 0; + + if (CatSongs.VisibleSongs > 0) then begin - AudioPlayback.Close; - if AudioPlayback.Open(CatSongs.Song[Interaction].Path + CatSongs.Song[Interaction].Mp3) then begin - AudioPlayback.Position := AudioPlayback.Length / 4; - //If Song Fading is activated then don't Play directly, and Set Volume to Null, else Play normal - if (Ini.PreviewFading = 0) then - AudioPlayback.Play - else - AudioPlayback.SetMusicVolume(0); - end; - end - else - AudioPlayback.Stop; + // delay start of music for 200ms (see Draw()) + MusicStartTime := SDL_GetTicks() + 200; + end; end; end; -procedure TScreenSong.SkipTo(Target: Cardinal); // 0.5.0 +procedure TScreenSong.SkipTo(Target: Cardinal); var // Skip: integer; // Auto Removed, Unused Variable I: integer; -- cgit v1.2.3