Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions CS-Jukebox/AdditionalSongsForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,30 @@ namespace CS_Jukebox
/// <summary>Edits the optional tracks for one game event.</summary>
public class AdditionalSongsForm : Form
{
private readonly MusicEventType eventType;
private readonly List<SongProfile> songs;
private readonly ListBox songsList = new ListBox();
private readonly TextBox pathTextBox = new TextBox();
private readonly TrackBar volumeTrackBar = new TrackBar();
private readonly TextBox startMinutesTextBox = new TextBox();
private readonly TextBox startSecondsTextBox = new TextBox();
private readonly Jukebox previewJukebox = new Jukebox();
private readonly Button previewButton = new Button();
private int selectedIndex = -1;
private bool refreshingSongList;
private SongProfile previewSong;

public List<SongProfile> Songs => songs;

public AdditionalSongsForm(string eventName, IEnumerable<SongProfile> currentSongs)
public AdditionalSongsForm(MusicEventType eventType, IEnumerable<SongProfile> currentSongs)
{
this.eventType = eventType;
songs = currentSongs?
.Where(song => song != null)
.Select(song => new SongProfile(song.Path, song.Volume) { Start = song.Start, NormalizationGain = song.NormalizationGain })
.ToList() ?? new List<SongProfile>();

Text = eventName + " - Extra tracks";
Text = MusicEventTiming.GetDisplayName(eventType) + " - Extra tracks";
Width = 580;
Height = 330;
MinimizeBox = false;
Expand All @@ -38,7 +41,9 @@ public AdditionalSongsForm(string eventName, IEnumerable<SongProfile> currentSon

BuildControls();
RefreshSongList();
FormClosed += (sender, args) => previewJukebox.Dispose();
previewJukebox.PreviewCompleted += PreviewJukebox_PreviewCompleted;
FormClosed += AdditionalSongsForm_FormClosed;
ThemeManager.Apply(this);
}

private void BuildControls()
Expand All @@ -53,7 +58,8 @@ private void BuildControls()

var pathLabel = new Label { Text = "Audio file:", Left = 240, Top = 18, AutoSize = true };
pathTextBox.SetBounds(240, 40, 180, 23);
var previewButton = new Button { Text = "▶", Left = 428, Top = 39, Width = 30, Height = 28 };
previewButton.Text = "▶";
previewButton.SetBounds(428, 39, 30, 28);
previewButton.Click += (sender, args) => TogglePreview(previewButton);
var browseButton = new Button { Text = "Browse...", Left = 478, Top = 39, Width = 78 };
browseButton.Click += (sender, args) => BrowseForSong();
Expand Down Expand Up @@ -180,7 +186,10 @@ private void TogglePreview(Button previewButton)
return;
}

previewJukebox.PlayPreviewSong(song);
if (Properties.LimitPreviewToEventDuration)
previewJukebox.PlayPreviewSong(song, MusicEventTiming.GetPreviewDurationSeconds(eventType));
else
previewJukebox.PlayPreviewSong(song);
previewSong = song;
previewButton.Text = "■";
}
Expand All @@ -191,6 +200,24 @@ private void TogglePreview(Button previewButton)
}
}

private void PreviewJukebox_PreviewCompleted(object sender, PreviewCompletedEventArgs e)
{
previewSong = null;
previewButton.Text = "▶";

if (IsDisposed || Disposing) return;
string message = e.Reason == PreviewCompletionReason.EventEnded
? "Event ended before music."
: "Music ended before event.";
MessageBox.Show(this, message, "Preview complete", MessageBoxButtons.OK, MessageBoxIcon.Information);
}

private void AdditionalSongsForm_FormClosed(object sender, FormClosedEventArgs e)
{
previewJukebox.PreviewCompleted -= PreviewJukebox_PreviewCompleted;
previewJukebox.Dispose();
}

private void VolumeTrackBar_ValueChanged(object sender, EventArgs e)
{
if (previewSong == null || !previewJukebox.IsPlaybackActive() ||
Expand Down
18 changes: 15 additions & 3 deletions CS-Jukebox/CS-Jukebox.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,27 @@
<UseWindowsForms>true</UseWindowsForms>
<StartupObject>CS_Jukebox.Program</StartupObject>
<ApplicationIcon>logo.ico</ApplicationIcon>
<Version>1.3.3</Version> <!-- Don't forget about us !!! -->
<AssemblyVersion>1.3.3.0</AssemblyVersion> <!-- Don't forget about us !!! -->
<FileVersion>1.3.3.0</FileVersion> <!-- Don't forget about us !!! -->
<!-- Publish as self-contained by default to avoid requiring pre-installed runtime on target machines -->
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSelfContained>true</PublishSelfContained>
<!-- Portable release: bundle the runtime and native audio dependencies into one executable. -->
<PublishSingleFile>true</PublishSingleFile>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
<!-- Avoid aggressive trimming for compatibility with dynamic serialization and audio codecs -->
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<CopyOutputSymbolsToPublishDirectory>false</CopyOutputSymbolsToPublishDirectory>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NAudio" Version="2.1.0" />
Expand All @@ -25,9 +38,8 @@
</ItemGroup>

<ItemGroup>
<None Update="gamestate_integration_jukebox.cfg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<EmbeddedResource Include="gamestate_integration_jukebox.cfg"
LogicalName="CS_Jukebox.gamestate_integration_jukebox.cfg" />
</ItemGroup>

</Project>
9 changes: 5 additions & 4 deletions CS-Jukebox/GameLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,10 @@ void OnNewGameState(GameState gs)
if (gs.Round.Phase == RoundPhase.Live && musicState != MusicState.Live && musicState != MusicState.BombPlanted)
{
jukebox.Stop();
// Play the round-start track to completion, matching the behaviour
// of FreezeTime and MVP which also use the two-arg overload.
if (PlaySongIfValid(Properties.SelectedKit.PickSong(Properties.SelectedKit.startSong, Properties.SelectedKit.startSongs), false))
if (PlaySongIfValid(
Properties.SelectedKit.PickSong(Properties.SelectedKit.startSong, Properties.SelectedKit.startSongs),
false,
MusicEventTiming.RoundStartSeconds))
{
musicState = MusicState.Live;
Logger.LogEvent("TransitionToLive", $"SelectedKit={Properties.SelectedKit.Name}");
Expand Down Expand Up @@ -240,7 +241,7 @@ private void HandlePlayerDeath(GameState gs)
if (PlaySongIfValid(
Properties.SelectedKit.PickSong(Properties.SelectedKit.deathSong, Properties.SelectedKit.deathSongs),
false,
5))
MusicEventTiming.PlayerDeathSeconds))
{
Console.WriteLine("Local player died");
Logger.LogEvent("PlayerDeathDetected");
Expand Down
1 change: 1 addition & 0 deletions CS-Jukebox/GamePathForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public GamePathForm()
MinimizeBox = false;
if (!string.IsNullOrWhiteSpace(Properties.GameDir))
dirTextBox.Text = Properties.GameDir;
ThemeManager.Apply(this);
}

//Open folder browser dialog
Expand Down
66 changes: 65 additions & 1 deletion CS-Jukebox/Jukebox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ public class Jukebox : IDisposable

private bool isPlaying = false;
private long scheduledStopAtMilliseconds;
private bool previewCompletionPending;
private long previewStopAtMilliseconds;

public event EventHandler<PreviewCompletedEventArgs> PreviewCompleted;

public Jukebox()
{
Expand Down Expand Up @@ -110,7 +114,24 @@ public void PlaySong(SongProfile song, bool loop)

public void PlayPreviewSong(SongProfile song)
{
Logger.LogEntry($"song={song?.Path}");
PlayPreviewSongInternal(song, null, false);
}

/// <summary>
/// Plays a preview and reports why it completed. A null duration keeps
/// the preview unbounded but still reports when the source file ends.
/// </summary>
public void PlayPreviewSong(SongProfile song, int? durationSeconds)
{
if (durationSeconds.HasValue && durationSeconds.Value <= 0)
throw new ArgumentOutOfRangeException(nameof(durationSeconds));

PlayPreviewSongInternal(song, durationSeconds, true);
}

private void PlayPreviewSongInternal(SongProfile song, int? durationSeconds, bool reportCompletion)
{
Logger.LogEntry($"song={song?.Path}, duration={durationSeconds?.ToString() ?? "unbounded"}, reportCompletion={reportCompletion}");
if (song == null || string.IsNullOrWhiteSpace(song.Path) || !File.Exists(song.Path))
{
Logger.Log($"WARNING: Song file path is missing or invalid: '{song?.Path}'");
Expand All @@ -121,6 +142,7 @@ public void PlayPreviewSong(SongProfile song)
lock (lockObj)
{
CancelScheduledStopInternal();
CancelPreviewCompletionInternal();
CancelFadeInternal();
previewMode = true;
currentSong = song;
Expand All @@ -144,12 +166,17 @@ public void PlayPreviewSong(SongProfile song)
outputDevice = new WaveOutEvent();
outputDevice.Init(waveStream);
outputDevice.Play();
previewCompletionPending = reportCompletion;
previewStopAtMilliseconds = durationSeconds.HasValue
? Environment.TickCount64 + (durationSeconds.Value * 1000L)
: 0;
songTimer.Start();
Logger.LogExit("Preview playback started successfully");
}
catch (Exception ex)
{
Logger.LogError("PlayPreviewSong failed", ex);
CancelPreviewCompletionInternal();
CleanupOutput();
throw new InvalidOperationException("The selected audio file could not be played.", ex);
}
Expand Down Expand Up @@ -272,6 +299,7 @@ public void Stop()
lock (lockObj)
{
CancelScheduledStopInternal();
CancelPreviewCompletionInternal();
if (!IsPlaybackActive())
{
CleanupOutput();
Expand Down Expand Up @@ -347,6 +375,12 @@ private void CancelScheduledStopInternal()
scheduledStopAtMilliseconds = 0;
}

private void CancelPreviewCompletionInternal()
{
previewCompletionPending = false;
previewStopAtMilliseconds = 0;
}

private void CancelFadeInternal()
{
CancellationTokenSource cts;
Expand All @@ -364,6 +398,7 @@ public void StopImmediately()
lock (lockObj)
{
CancelScheduledStopInternal();
CancelPreviewCompletionInternal();
CancelFadeInternal();
CleanupOutput();
}
Expand Down Expand Up @@ -452,11 +487,28 @@ private void TimerTick(object sender, EventArgs e)

if (outputDevice.PlaybackState == PlaybackState.Stopped && fadeCts == null)
{
bool reportMusicEnded = previewCompletionPending;
CancelScheduledStopInternal();
CancelPreviewCompletionInternal();
lock (lockObj)
{
CleanupOutput();
}
if (reportMusicEnded)
OnPreviewCompleted(PreviewCompletionReason.MusicEnded);
return;
}

if (previewCompletionPending && previewStopAtMilliseconds > 0 &&
Environment.TickCount64 >= previewStopAtMilliseconds)
{
CancelScheduledStopInternal();
CancelPreviewCompletionInternal();
lock (lockObj)
{
CleanupOutput();
}
OnPreviewCompleted(PreviewCompletionReason.EventEnded);
return;
}

Expand All @@ -469,6 +521,18 @@ private void TimerTick(object sender, EventArgs e)
UpdateVolume();
}

private void OnPreviewCompleted(PreviewCompletionReason reason)
{
try
{
PreviewCompleted?.Invoke(this, new PreviewCompletedEventArgs(reason));
}
catch (Exception ex)
{
Logger.LogError("PreviewCompleted handler failed", ex);
}
}

private static bool IsGameFocused()
{
try
Expand Down
22 changes: 18 additions & 4 deletions CS-Jukebox/MainForm.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 14 additions & 2 deletions CS-Jukebox/MainForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public partial class MainForm : Form
static extern bool AllocConsole();

private GameLogic logic;
private bool initializingTheme;

public MainForm()
{
Expand All @@ -26,9 +27,11 @@ public MainForm()
//AllocConsole(); //Enable console
MaximizeBox = false;

// Use default system theme (no custom theming applied)

Properties.Load();
initializingTheme = true;
darkThemeCheckBox.Checked = Properties.DarkTheme;
initializingTheme = false;
ThemeManager.Apply(this);

// If GameDir is missing or invalid, attempt auto-detection of CS2 install.
bool gameDirValid = false;
Expand Down Expand Up @@ -559,6 +562,15 @@ private void autoCheckBox_CheckedChanged(object sender, EventArgs e)
RegisterInStartup(autoCheckBox.Checked);
}

private void darkThemeCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (initializingTheme) return;

Properties.DarkTheme = darkThemeCheckBox.Checked;
ThemeManager.ApplyToOpenForms();
Properties.SaveProperties();
}

private async void exportButton_Click(object sender, EventArgs e)
{
if (Properties.SelectedKit == null)
Expand Down
Loading
Loading