diff --git a/Pulsus/Audio/AudioEngine.cs b/Pulsus/Audio/AudioEngine.cs
index cbcc665..6918a72 100644
--- a/Pulsus/Audio/AudioEngine.cs
+++ b/Pulsus/Audio/AudioEngine.cs
@@ -51,8 +51,12 @@ public SoundInstanceInternal(SoundInstance instance)
long buffersMixedCount = 0;
float volume = 1.0f;
- public AudioEngine(string audioDevice = null, AudioDriver driver = AudioDriver.Default, int sampleRate = 44100, int bufferLength = 1024)
+ private ResampleQuality resampleQuality;
+
+ public AudioEngine(string audioDevice = null, AudioDriver driver = AudioDriver.Default, int sampleRate = 44100, int bufferLength = 1024, ResampleQuality resampleQuality = ResampleQuality.High)
{
+ this.resampleQuality = resampleQuality;
+
SDL.SDL_InitSubSystem(SDL.SDL_INIT_AUDIO);
audioCallback = AudioCallback;
@@ -261,7 +265,7 @@ public uint GetBufferOffset()
public SoundData LoadFromFile(string path)
{
return new SoundData(FFmpeg.FFmpegHelper.SoundFromFileResample(path,
- audioSpec.freq, audioSpec.channels, audioSpec.format));
+ audioSpec.freq, audioSpec.channels, audioSpec.format, resampleQuality));
}
public void Play(SoundInstance soundInstance, int polyphony)
diff --git a/Pulsus/FFmpeg/FFmpegContext.cs b/Pulsus/FFmpeg/FFmpegContext.cs
index 926e308..dd3eae1 100644
--- a/Pulsus/FFmpeg/FFmpegContext.cs
+++ b/Pulsus/FFmpeg/FFmpegContext.cs
@@ -296,7 +296,7 @@ private void SetupCodecContext(AVStream* stream)
format = (int)codecContext->sample_fmt;
}
- public void ConvertToFormat(AVSampleFormat sampleFormat, int sampleRate, int channels)
+ public void ConvertToFormat(AVSampleFormat sampleFormat, int sampleRate, int channels, ResampleQuality resampleQuality = ResampleQuality.High)
{
if (format == (int)sampleFormat &&
this.sampleRate == sampleRate &&
@@ -312,6 +312,7 @@ public void ConvertToFormat(AVSampleFormat sampleFormat, int sampleRate, int cha
int channelLayout = (int)ffmpeg.av_get_default_channel_layout(channels);
swrContext = ffmpeg.swr_alloc();
+
ffmpeg.av_opt_set_int(swrContext, "in_channel_layout", (int)codecContext->channel_layout, 0);
ffmpeg.av_opt_set_int(swrContext, "out_channel_layout", channelLayout, 0);
ffmpeg.av_opt_set_int(swrContext, "in_channel_count", codecContext->channels, 0);
@@ -321,6 +322,25 @@ public void ConvertToFormat(AVSampleFormat sampleFormat, int sampleRate, int cha
ffmpeg.av_opt_set_sample_fmt(swrContext, "in_sample_fmt", codecContext->sample_fmt, 0);
ffmpeg.av_opt_set_sample_fmt(swrContext, "out_sample_fmt", sampleFormat, 0);
+ switch (resampleQuality)
+ {
+ case ResampleQuality.Low:
+ ffmpeg.av_opt_set_int(swrContext, "filter_size", 0, 0);
+ ffmpeg.av_opt_set_int(swrContext, "phase_shift", 0, 0);
+ break;
+ case ResampleQuality.Medium:
+ // default ffmpeg settings
+ break;
+ case ResampleQuality.High:
+ ffmpeg.av_opt_set_int(swrContext, "filter_size", 128, 0);
+ ffmpeg.av_opt_set_double(swrContext, "cutoff", 1.0, 0);
+ break;
+ case ResampleQuality.Highest:
+ ffmpeg.av_opt_set_int(swrContext, "filter_size", 256, 0);
+ ffmpeg.av_opt_set_double(swrContext, "cutoff", 1.0, 0);
+ break;
+ }
+
if (ffmpeg.swr_init(swrContext) != 0)
throw new ApplicationException("Failed init SwrContext: " + FFmpegHelper.logLastLine);
}
diff --git a/Pulsus/FFmpeg/FFmpegHelper.cs b/Pulsus/FFmpeg/FFmpegHelper.cs
index 62d1616..6fe8c72 100644
--- a/Pulsus/FFmpeg/FFmpegHelper.cs
+++ b/Pulsus/FFmpeg/FFmpegHelper.cs
@@ -134,7 +134,7 @@ public static byte[] SoundFromFile(string path, out int sampleRate, out int chan
}
}
- public static byte[] SoundFromFileResample(string path, int sampleRate, int channels, ushort sampleFormatSDL)
+ public static byte[] SoundFromFileResample(string path, int sampleRate, int channels, ushort sampleFormatSDL, ResampleQuality resampleQuality = ResampleQuality.High)
{
AVSampleFormat targetFormat2;
switch (sampleFormatSDL)
@@ -157,7 +157,7 @@ public static byte[] SoundFromFileResample(string path, int sampleRate, int chan
ffContext.SelectStream(AVMediaType.AVMEDIA_TYPE_AUDIO);
// setup resamplers and other format converters if needed
- ffContext.ConvertToFormat(targetFormat2, sampleRate, channels);
+ ffContext.ConvertToFormat(targetFormat2, sampleRate, channels, resampleQuality);
// FFmpeg only approximates stream durations but is
// usually not far from the real duration.
diff --git a/Pulsus/Game.cs b/Pulsus/Game.cs
index e787674..af3f4e2 100644
--- a/Pulsus/Game.cs
+++ b/Pulsus/Game.cs
@@ -34,7 +34,9 @@ public Game()
FFmpegHelper.Init();
Log.Info("Initializing Audio...");
- audio = new AudioEngine(null, settings.audio.driver, (int)settings.audio.sampleRate, (int)settings.audio.bufferLength);
+ audio = new AudioEngine(null, settings.audio.driver,
+ (int)settings.audio.sampleRate, (int)settings.audio.bufferLength,
+ settings.audio.resampleQuality);
audio.SetVolume(Math.Min(settings.audio.volume, 100) / 100.0f);
Log.Info("Audio driver: " + audio.audioDriver.ToString());
diff --git a/Pulsus/Pulsus.csproj b/Pulsus/Pulsus.csproj
index 3a824da..281039a 100644
--- a/Pulsus/Pulsus.csproj
+++ b/Pulsus/Pulsus.csproj
@@ -199,6 +199,7 @@
+
diff --git a/Pulsus/Shared/ResampleQuality.cs b/Pulsus/Shared/ResampleQuality.cs
new file mode 100644
index 0000000..d75b725
--- /dev/null
+++ b/Pulsus/Shared/ResampleQuality.cs
@@ -0,0 +1,10 @@
+namespace Pulsus
+{
+ public enum ResampleQuality
+ {
+ Low,
+ Medium,
+ High,
+ Highest,
+ }
+}
diff --git a/Pulsus/Shared/Settings.cs b/Pulsus/Shared/Settings.cs
index ad10be9..b75eddb 100644
--- a/Pulsus/Shared/Settings.cs
+++ b/Pulsus/Shared/Settings.cs
@@ -1,11 +1,11 @@
-using Jil;
+using System;
+using System.Collections.Generic;
+using Jil;
using Pulsus.Audio;
using Pulsus.Gameplay;
using Pulsus.Graphics;
using Pulsus.Input;
using SDL2;
-using System.Collections.Generic;
-using System;
namespace Pulsus
{
@@ -82,10 +82,10 @@ public class VideoSettings
public class AudioSettings
{
public AudioDriver driver = AudioDriver.Default;
-
public uint volume = 30;
public uint sampleRate = 44100;
public uint bufferLength = 768;
+ public ResampleQuality resampleQuality = ResampleQuality.Low;
}
public class InputSettings
diff --git a/Pulsus/Shared/SettingsParser.cs b/Pulsus/Shared/SettingsParser.cs
index 2e82cf6..cd4fb5c 100644
--- a/Pulsus/Shared/SettingsParser.cs
+++ b/Pulsus/Shared/SettingsParser.cs
@@ -32,6 +32,8 @@ private Settings settings
}
}
+ private bool resampleQualityOverridden = false;
+
public Settings Parse(string[] args)
{
for (int i = 1; i < args.Length; i++)
@@ -67,12 +69,17 @@ private static void PrintHelp()
Tuple.Create("-m VALUE, --measure VALUE", "Starts the chart from measure number VALUE [0-999]"),
Tuple.Create("", ""),
Tuple.Create("--render OUTPUT.wav", "Renders all audio of CHARTFILE to file"),
+ Tuple.Create("--resample-quality [low|medium|high|highest]",
+ "Changes resampling quality of audio samples"),
Tuple.Create("--dump-timestamps OUTPUT", "Dumps all generated note event timestamps of CHARTFILE"),
};
foreach (var option in options)
{
- Console.WriteLine(" {0,-35}{1}", option.Item1, option.Item2);
+ if (option.Item1.Length < 35)
+ Console.WriteLine(" {0,-35}{1}", option.Item1, option.Item2);
+ else
+ Console.WriteLine(" {0,-35}\n {2,-35}{1}", option.Item1, option.Item2, "");
}
Console.Write("\n");
@@ -127,6 +134,20 @@ public bool ParseArg(string key, string value)
settings.audio.volume = 100;
settings.outputPath = value;
+ if (!resampleQualityOverridden)
+ settings.audio.resampleQuality = ResampleQuality.Highest;
+ break;
+ case "--resample-quality":
+ {
+ ResampleQuality resampleQuality;
+ if (Enum.TryParse(value, true, out resampleQuality))
+ {
+ settings.audio.resampleQuality = resampleQuality;
+ resampleQualityOverridden = true;
+ }
+ else
+ Log.Warning("Unknown resample quality value: " + value);
+ }
break;
case "--dump-timestamps":
settings.outputMode = OutputMode.DumpTimestamps;