Skip to content

Commit 4fff519

Browse files
Add option to adjust volume after msupcm++ generation
1 parent ea314ba commit 4fff519

File tree

9 files changed

+152
-27
lines changed

9 files changed

+152
-27
lines changed

MSUScripter/Configs/MsuSongMsuPcmInfo.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ public class MsuSongMsuPcmInfo
4040
[Description("Normalize the current track to the specified RMS level, overrides the global normalization value")]
4141
public double? Normalization { get; set; }
4242

43+
[Description("Alter the volume after msupcm++ has generated the PCM file")]
44+
public float? PostGenerateVolumeModifier { get; set; }
45+
46+
[Description("If the volume modifier after msupcm++ generation should be adding decibels")]
47+
public bool IsPostGenerateVolumeDecibels { get; set; }
48+
4349
[Description("Apply dynamic range compression to the current track")]
4450
public bool? Compression { get; set; }
4551

MSUScripter/MSUScripter.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
99
<ApplicationIcon>MSUScripterIcon.ico</ApplicationIcon>
1010
<PackageIcon>MSUScripterIcon.ico</PackageIcon>
11-
<Version>5.0.3</Version>
11+
<Version>5.1.0-beta.1</Version>
1212
<RuntimeFrameworkVersion>9.0.0</RuntimeFrameworkVersion>
1313
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
1414
<LangVersion>12</LangVersion>

MSUScripter/Models/MsuProjectGenerationCache.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,20 @@ public class MsuProjectGenerationCache
1010

1111
public class MsuProjectSongCache
1212
{
13+
public const int CurrentCacheVersion = 1;
1314
public ulong JsonHash { get; init; }
1415
public int JsonLength { get; init; }
1516
public DateTime FileGenerationTime { get; init; }
1617
public long FileLength { get; init; }
18+
public int? CacheVersion { get; init; }
19+
public float PostGenerateVolumeModifier { get; init; }
20+
public bool IsPostGenerateVolumeDecibels { get; init; }
1721

1822
public static bool IsValid(MsuProjectSongCache? a, MsuProjectSongCache? b)
1923
{
2024
if (a is null || b is null) return false;
2125
return a.JsonHash == b.JsonHash && a.JsonLength == b.JsonLength &&
22-
a.FileGenerationTime == b.FileGenerationTime & a.FileLength == b.FileLength;
26+
a.FileGenerationTime == b.FileGenerationTime & a.FileLength == b.FileLength && a.CacheVersion == b.CacheVersion &&
27+
Math.Abs(a.PostGenerateVolumeModifier - b.PostGenerateVolumeModifier) < 0.01 && a.IsPostGenerateVolumeDecibels == b.IsPostGenerateVolumeDecibels;
2328
}
2429
}

MSUScripter/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ private static IServiceCollection ConfigureServices(IServiceCollection collectio
121121
.AddSingleton<StatusBarService>()
122122
.AddSingleton<PythonCompanionService>()
123123
.AddSingleton<DependencyInstallerService>()
124+
.AddSingleton<PcmModifierService>()
124125
.AddAvaloniaControlServices<Program>()
125126
.AddTransient<ApplicationInitializationService>();
126127

MSUScripter/Services/MsuPcmService.cs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ public class MsuPcmService(
4343
ConverterService converterService,
4444
YamlService yamlService,
4545
IAudioPlayerService audioPlayerService,
46-
DependencyInstallerService dependencyInstallerService)
46+
DependencyInstallerService dependencyInstallerService,
47+
PcmModifierService pcmModifierService)
4748
{
4849
private string _cacheFolder2 = "";
4950
private string _msuPcmPath = string.Empty;
@@ -212,7 +213,7 @@ public async Task<GeneratePcmFileResponse> CreatePcm(MsuProject project, MsuSong
212213
}
213214

214215
project.GenerationCache.Songs.TryGetValue(song.Id, out var previousCache);
215-
var currentCache = cacheResults ? GetSongCacheData(jsonResponse.JsonText, outputPath) : null;
216+
var currentCache = cacheResults ? GetSongCacheData(jsonResponse.JsonText, outputPath, song) : null;
216217

217218
if (MsuProjectSongCache.IsValid(previousCache, currentCache))
218219
{
@@ -272,8 +273,17 @@ public async Task<GeneratePcmFileResponse> CreatePcm(MsuProject project, MsuSong
272273
Directory.CreateDirectory(directory);
273274
}
274275

275-
logger.LogInformation("Moving generated PCM to {Path}", outputPath);
276-
File.Move(tempPcmPath, outputPath);
276+
if (song.MsuPcmInfo.PostGenerateVolumeModifier is >= .01f or <= -.01f)
277+
{
278+
logger.LogInformation("Modifying PCM file and copying to {Path}", outputPath);
279+
pcmModifierService.UpdatePcmFile(tempPcmPath, outputPath, song);
280+
}
281+
else
282+
{
283+
logger.LogInformation("Moving generated PCM to {Path}", outputPath);
284+
File.Move(tempPcmPath, outputPath);
285+
}
286+
277287
}
278288
catch (Exception ex)
279289
{
@@ -287,7 +297,7 @@ public async Task<GeneratePcmFileResponse> CreatePcm(MsuProject project, MsuSong
287297
// Move to the cache
288298
if (cacheResults)
289299
{
290-
currentCache = GetSongCacheData(jsonResponse.JsonText, outputPath);
300+
currentCache = GetSongCacheData(jsonResponse.JsonText, outputPath, song);
291301

292302
if (currentCache != null)
293303
{
@@ -337,7 +347,7 @@ public void SaveGenerationCache(MsuProject project)
337347
}
338348
}
339349

340-
private MsuProjectSongCache? GetSongCacheData(string json, string outputPath)
350+
private MsuProjectSongCache? GetSongCacheData(string json, string outputPath, MsuSongInfo song)
341351
{
342352
if (!File.Exists(outputPath))
343353
{
@@ -353,6 +363,9 @@ public void SaveGenerationCache(MsuProject project)
353363
JsonLength = json.Length,
354364
FileGenerationTime = fileInfo.LastWriteTime,
355365
FileLength = fileInfo.Length,
366+
CacheVersion = MsuProjectSongCache.CurrentCacheVersion,
367+
PostGenerateVolumeModifier = song.MsuPcmInfo.PostGenerateVolumeModifier ?? 0,
368+
IsPostGenerateVolumeDecibels = song.MsuPcmInfo.IsPostGenerateVolumeDecibels,
356369
};
357370
}
358371

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using MSUScripter.Configs;
5+
using NAudio.Wave;
6+
using NAudio.Wave.SampleProviders;
7+
8+
namespace MSUScripter.Services;
9+
10+
public class PcmModifierService
11+
{
12+
public void UpdatePcmFile(string tempFile, string outFile, MsuSongInfo song)
13+
{
14+
var volumeMultiplier = song.MsuPcmInfo.IsPostGenerateVolumeDecibels
15+
? MathF.Pow(10, song.MsuPcmInfo.PostGenerateVolumeModifier!.Value / 20f)
16+
: song.MsuPcmInfo.PostGenerateVolumeModifier!.Value / 100f;
17+
18+
var waveFormat = new WaveFormat(
19+
rate: 44100,
20+
bits: 16,
21+
channels: 2
22+
);
23+
24+
using var inputStream = File.OpenRead(tempFile);
25+
26+
// Get the bytes for looping
27+
var headerBytes = new byte[8];
28+
inputStream.ReadExactly(headerBytes, 0, 8);
29+
inputStream.Position = 0;
30+
31+
// Load the source, convert to samples, apply modifiers, and convert back to PCM
32+
using var rawSource = new RawSourceWaveStream(inputStream, waveFormat);
33+
var sampleProvider = rawSource.ToSampleProvider();
34+
var volumeProvider = new VolumeSampleProvider(sampleProvider)
35+
{
36+
Volume = volumeMultiplier
37+
};
38+
var pcm16Provider = new SampleToWaveProvider16(volumeProvider);
39+
40+
using var outputStream = File.Create(outFile);
41+
var buffer = new byte[4096];
42+
int bytesRead;
43+
var isFirstRead = true;
44+
45+
// Write the modified stream to file, using the previous first 8 bytes
46+
while ((bytesRead = pcm16Provider.Read(buffer, 0, buffer.Length)) > 0)
47+
{
48+
if (isFirstRead)
49+
{
50+
isFirstRead = false;
51+
outputStream.Write(headerBytes, 0, 8);
52+
outputStream.Write(buffer.Skip(8).ToArray(), 0, bytesRead - 8);
53+
}
54+
else
55+
{
56+
outputStream.Write(buffer, 0, bytesRead);
57+
}
58+
}
59+
}
60+
}

MSUScripter/Text/ApplicationText.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ public static void SetLanguage(ApplicationText newLanguage)
123123
public string MsuPcmTempoToolTip { get; } = "Alter the tempo of the current track by a specified ratio.";
124124
public string MsuPcmCompressionLabel { get; } = "Compression";
125125
public string MsuPcmCompressionToolTip { get; } = "Apply dynamic range compression to the current track. Helps to minimize very loud and very quiet portions of the track.";
126+
public string PostGenerationVolumeLabel { get; } = "Post Generation Volume Modifier";
127+
public string PostGenerationVolumeToolTip { get; } = "Alter the volume after MsuPcm++ has generated the PCM file. If set, anyone generating the PCM themselves with MsuPcm++ via the tracks json file will not see these changes.";
126128
public string MsuPcmDitherLabel { get; } = "Dither";
127129
public string MsuPcmDitherToolTip { get; } = "Whether or not to apply audio dither to the final output. If set, overrides the default value.";
128130

MSUScripter/ViewModels/MsuSongAdvancedPanelViewModel.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public class MsuSongAdvancedPanelViewModel : SavableViewModelBase
5555
[Reactive] public double? Normalization { get; set; }
5656

5757
[Reactive] public bool? Compression { get; set; }
58+
[Reactive] public float? PostGenerateVolumeModifier { get; set; }
59+
[Reactive] public bool IsPostGenerateVolumeDecibels { get; set; }
5860

5961
[Reactive] public bool? Dither { get; set; }
6062
[Reactive] public bool ShowDither { get; set; }
@@ -272,6 +274,8 @@ public void SetSelectedTreeData(MsuSongAdvancedPanelViewModelModelTreeData treeD
272274
Tempo = treeData.MsuPcmInfo.Tempo;
273275
Normalization = treeData.MsuPcmInfo.Normalization;
274276
Compression = treeData.MsuPcmInfo.Compression;
277+
PostGenerateVolumeModifier = treeData.MsuPcmInfo.PostGenerateVolumeModifier;
278+
IsPostGenerateVolumeDecibels = treeData.MsuPcmInfo.IsPostGenerateVolumeDecibels;
275279
_isTopLevelMsuPcmInfo = treeData.ParentIndex < 0;
276280
if (_isTopLevelMsuPcmInfo)
277281
{
@@ -312,6 +316,8 @@ public override void SaveChanges()
312316
_currentSongMsuPcmInfo.Tempo = Tempo;
313317
_currentSongMsuPcmInfo.Normalization = Normalization;
314318
_currentSongMsuPcmInfo.Compression = Compression;
319+
_currentSongMsuPcmInfo.PostGenerateVolumeModifier = PostGenerateVolumeModifier;
320+
_currentSongMsuPcmInfo.IsPostGenerateVolumeDecibels = IsPostGenerateVolumeDecibels;
315321
_currentSongMsuPcmInfo.Dither = Dither;
316322

317323
if (_isTopLevelMsuPcmInfo)

MSUScripter/Views/MsuSongAdvancedPanel.axaml

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -478,29 +478,60 @@
478478
<views:FormLabel
479479
Grid.Column="0"
480480
Grid.Row="12"
481+
Grid.ColumnSpan="3"
481482
Margin="0 8 3 2"
482483
LabelText="{Binding Text.MsuPcmNormalizationLabel}"
483484
ToolTipText="{Binding Text.MsuPcmNormalizationToolTip}"
484485
/>
485486
<controls:NumericUpDownNoScroll
486487
Grid.Row="13"
487488
Grid.Column="0"
488-
Grid.ColumnSpan="2"
489+
Grid.ColumnSpan="3"
489490
Margin="0 0 3 0"
490491
Value="{Binding Normalization}"
491492
/>
492493

494+
<!-- Post Generation Volume -->
495+
<views:FormLabel
496+
Grid.Column="3"
497+
Grid.Row="12"
498+
Grid.ColumnSpan="3"
499+
Margin="3 8 0 2"
500+
LabelText="{Binding Text.PostGenerationVolumeLabel}"
501+
ToolTipText="{Binding Text.PostGenerationVolumeToolTip}"
502+
/>
503+
<Grid
504+
Grid.Row="13"
505+
Grid.Column="3"
506+
Grid.ColumnSpan="3"
507+
ColumnDefinitions="*, Auto"
508+
>
509+
<controls:NumericUpDownNoScroll
510+
Grid.Column="0"
511+
Margin="3 0 3 0"
512+
Value="{Binding PostGenerateVolumeModifier}"
513+
/>
514+
<controls:BoolComboBox
515+
Grid.Column="1"
516+
TrueDisplayText="Db"
517+
FalseDisplayText="%"
518+
Value="{Binding IsPostGenerateVolumeDecibels}"
519+
Width="50"
520+
/>
521+
</Grid>
522+
493523
<!-- Tempo -->
494524
<views:FormLabel
495-
Grid.Column="2"
496-
Grid.Row="12"
525+
Grid.Column="0"
526+
Grid.Row="14"
527+
Grid.ColumnSpan="2"
497528
Margin="3 8 3 2"
498529
LabelText="{Binding Text.MsuPcmTempoLabel}"
499530
ToolTipText="{Binding Text.MsuPcmTempoToolTip}"
500531
/>
501532
<controls:NumericUpDownNoScroll
502-
Grid.Row="13"
503-
Grid.Column="2"
533+
Grid.Row="15"
534+
Grid.Column="0"
504535
Grid.ColumnSpan="2"
505536
Margin="3 0 3 0"
506537
Value="{Binding Tempo}"
@@ -509,36 +540,37 @@
509540

510541
<!-- Compression -->
511542
<views:FormLabel
512-
Grid.Column="4"
513-
Grid.Row="12"
514-
Margin="3 8 0 2"
543+
Grid.Column="2"
544+
Grid.Row="14"
545+
Grid.ColumnSpan="2"
546+
Margin="3 8 3 2"
515547
LabelText="{Binding Text.MsuPcmCompressionLabel}"
516548
ToolTipText="{Binding Text.MsuPcmCompressionToolTip}"
517549
/>
518550
<controls:BoolComboBox
519-
Grid.Row="13"
520-
Grid.Column="4"
551+
Grid.Row="15"
552+
Grid.Column="2"
521553
Grid.ColumnSpan="2"
522-
Margin="3 0 0 0"
554+
Margin="3 0 3 0"
523555
Value="{Binding Compression, Mode=TwoWay}"
524556
AllowNulls="True"
525557
></controls:BoolComboBox>
526558

527-
<!-- Compression -->
559+
<!-- Dither -->
528560
<views:FormLabel
529-
Grid.Column="0"
561+
Grid.Column="4"
530562
Grid.Row="14"
531-
Margin="0 8 3 2"
532-
IsVisible="{Binding ShowDither}"
563+
Grid.ColumnSpan="2"
564+
Margin="3 8 0 2"
533565
LabelText="{Binding Text.MsuPcmDitherLabel}"
534566
ToolTipText="{Binding Text.MsuPcmDitherToolTip}"
535567
/>
536568
<controls:BoolComboBox
537569
Grid.Row="15"
538-
Grid.Column="0"
539-
Grid.ColumnSpan="3"
540-
Margin="0 0 3 0"
541-
IsVisible="{Binding ShowDither}"
570+
Grid.Column="4"
571+
Grid.ColumnSpan="2"
572+
Margin="3 0 0 0"
573+
IsEnabled="{Binding ShowDither}"
542574
Value="{Binding Dither, Mode=TwoWay}"
543575
AllowNulls="True"
544576
></controls:BoolComboBox>

0 commit comments

Comments
 (0)