Skip to content

Commit

Permalink
Add ReplayGain feature
Browse files Browse the repository at this point in the history
  • Loading branch information
complexlogic committed Jan 4, 2025
1 parent aea2312 commit 1862e67
Show file tree
Hide file tree
Showing 14 changed files with 258 additions and 43 deletions.
1 change: 1 addition & 0 deletions game/languages/English.ini
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ OPTION_VALUE_FULL_VID_BG=Full (BG & Video)
OPTION_VALUE_GAIN_SOFT=Soft
OPTION_VALUE_GAIN_MEDIUM=Medium
OPTION_VALUE_GAIN_HARD=Hard
OPTION_VALUE_GAIN_REPLAYGAIN=ReplayGain

OPTION_VALUE_AUTO=Auto
OPTION_VALUE_SEC=Second
Expand Down
1 change: 1 addition & 0 deletions game/languages/Language.new
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
;TODO: OPTION_VALUE_GAIN_SOFT=Soft
;TODO: OPTION_VALUE_GAIN_MEDIUM=Medium
;TODO: OPTION_VALUE_GAIN_HARD=Hard
;TODO: OPTION_VALUE_GAIN_REPLAYGAIN=ReplayGain

;TODO: OPTION_VALUE_AUTO=Auto
;TODO: OPTION_VALUE_SEC=Second
Expand Down
10 changes: 6 additions & 4 deletions src/base/UIni.pas
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ TInputDeviceConfig = record
TVisualizerOption = (voOff, voWhenNoVideo, voWhenNoVideoAndImage, voOn);
TBackgroundMusicOption = (bmoOff, bmoOn);
TSongMenuMode = ( smRoulette, smChessboard, smCarousel, smSlotMachine, smSlide, smList, smMosaic);
TMusicAutoGainOption = (magOff, {$IFDEF HaveBASS} magSoft, magMedium, magHard, {$ENDIF} magReplayGain);

TIni = class
private
Expand Down Expand Up @@ -371,9 +372,7 @@ TIni = class

IVoicePassthrough: array[0..1] of UTF8String = ('Off', 'On');

IMusicAutoGain: array[0..3] of UTF8String = ('Off', 'Soft', 'Medium', 'Hard');
IMusicAutoGainVals: array[0..3] of integer = (-1, 0, 1, 2);

IMusicAutoGain: array[0..{$IFDEF HaveBASS}4{$ELSE}1{$ENDIF}] of UTF8String = ('Off', {$IFDEF HaveBASS} 'Soft', 'Medium', 'Hard', {$ENDIF} 'ReplayGain');

const
ISyncTo: array[0..2] of UTF8String = ('Music', 'Lyrics', 'Off');
Expand Down Expand Up @@ -497,7 +496,7 @@ TIni = class

IVoicePassthroughTranslated: array[0..1] of UTF8String = ('Off', 'On');

IMusicAutoGainTranslated: array[0..3] of UTF8String = ('Off', 'Soft', 'Medium', 'Hard');
IMusicAutoGainTranslated: array[0..{$IFDEF HaveBASS}4{$ELSE}1{$ENDIF}] of UTF8String = ('Off', {$IFDEF HaveBASS} 'Soft', 'Medium', 'Hard', {$ENDIF} 'ReplayGain');

ISyncToTranslated: array[0..2] of UTF8String = ('Music', 'Lyrics', 'Off');

Expand Down Expand Up @@ -684,9 +683,12 @@ procedure TIni.TranslateOptionValues;
IVoicePassthroughTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON');

IMusicAutoGainTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF');
{$IFDEF HaveBass}
IMusicAutoGainTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_GAIN_SOFT');
IMusicAutoGainTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_GAIN_MEDIUM');
IMusicAutoGainTranslated[3] := ULanguage.Language.Translate('OPTION_VALUE_GAIN_HARD');
{$ENDIF}
IMusicAutoGainTranslated[{$IFDEF HaveBass}4{$ELSE}1{$ENDIF}] := ULanguage.Language.Translate('OPTION_VALUE_GAIN_REPLAYGAIN');

ISyncToTranslated[Ord(stMusic)] := ULanguage.Language.Translate('OPTION_VALUE_MUSIC');
ISyncToTranslated[Ord(stLyrics)] := ULanguage.Language.Translate('OPTION_VALUE_LYRICS');
Expand Down
61 changes: 59 additions & 2 deletions src/base/UMusic.pas
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ TSoundFX = class

end;

TReplayGain = class(TSoundFX)
TAutoGain = class(TSoundFX)
end;

type
Expand Down Expand Up @@ -286,6 +286,7 @@ TAudioSourceStream = class(TAudioProcessingStream)
function IsError(): boolean; virtual; abstract;
public
function ReadData(Buffer: PByte; BufferSize: integer): integer; virtual; abstract;
function GetReplayGain(): single; virtual;

property EOF: boolean read IsEOF;
property Error: boolean read IsError;
Expand All @@ -309,6 +310,8 @@ TAudioPlaybackStream = class(TAudioProcessingStream)
AvgSyncDiff: double; //** average difference between stream and sync clock
SyncSource: TSyncSource;
SourceStream: TAudioSourceStream;
RG: single;
RGEnabled: boolean;

function GetLatency(): double; virtual; abstract;
function GetStatus(): TStreamStatus; virtual; abstract;
Expand All @@ -317,6 +320,8 @@ TAudioPlaybackStream = class(TAudioProcessingStream)
function Synchronize(BufferSize: integer; FormatInfo: TAudioFormatInfo): integer;
procedure FillBufferWithFrame(Buffer: PByteArray; BufferSize: integer; Frame: PByteArray; FrameSize: integer);
public
constructor Create();

(**
* Opens a SourceStream for playback.
* Note that the caller (not the TAudioPlaybackStream) is responsible to
Expand All @@ -325,7 +330,7 @@ TAudioPlaybackStream = class(TAudioProcessingStream)
* guarantees to deliver this method's SourceStream parameter to
* the OnClose-handler. Freeing SourceStream at OnClose is allowed.
*)
function Open(SourceStream: TAudioSourceStream): boolean; virtual; abstract;
function Open(SourceStream: TAudioSourceStream): boolean; virtual;

procedure Play(); virtual; abstract;
procedure Pause(); virtual; abstract;
Expand All @@ -342,8 +347,14 @@ TAudioPlaybackStream = class(TAudioProcessingStream)
procedure SetSyncSource(SyncSource: TSyncSource);
function GetSourceStream(): TAudioSourceStream;

function GetRGAdjustment(): single;
procedure SetRGAdjustment(Adjustment: single);
procedure EnableReplayGain() virtual;
procedure DisableReplayGain() virtual;

property Status: TStreamStatus read GetStatus;
property Volume: single read GetVolume write SetVolume;
property RGAdjustment: single read GetRGAdjustment write SetRGAdjustment;
end;

TAudioDecodeStream = class(TAudioSourceStream)
Expand Down Expand Up @@ -1075,9 +1086,29 @@ procedure TAudioProcessingStream.PerformOnClose();
end;
end;

{ TAudioSourceStream }

function TAudioSourceStream.GetReplayGain(): single;
begin
Result := 1.0;
end;

{ TAudioPlaybackStream }

constructor TAudioPlaybackStream.Create();
begin
RG := 1.0;
RGEnabled := false;
inherited;
end;

function TAudioPlaybackStream.Open(SourceStream: TAudioSourceStream): boolean;
begin
if (Ini.MusicAutoGain = ord(magReplayGain)) then
RGAdjustment := SourceStream.GetReplayGain();
Result := true;
end;

function TAudioPlaybackStream.GetSourceStream(): TAudioSourceStream;
begin
Result := SourceStream;
Expand Down Expand Up @@ -1209,6 +1240,32 @@ procedure TAudioPlaybackStream.FillBufferWithFrame(Buffer: PByteArray; BufferSiz
Move(Frame[0], Buffer[i*FrameSize], FrameSize);
end;

procedure TAudioPlaybackStream.SetRGAdjustment(Adjustment: single);
begin
if ((Adjustment > 0.0) AND (Adjustment <= 1.0)) then
begin
RG := Adjustment;
end;
end;

function TAudioPlaybackStream.GetRGAdjustment(): single;
begin
if (RGEnabled) then
Result := RG
else
Result := 1.0;
end;

procedure TAudioPlaybackStream.EnableReplayGain();
begin
RGEnabled := true;
end;

procedure TAudioPlaybackStream.DisableReplayGain();
begin
RGEnabled := false;
end;

{ TAudioVoiceStream }

function TAudioVoiceStream.Open(ChannelMap: integer; FormatInfo: TAudioFormatInfo): boolean;
Expand Down
16 changes: 15 additions & 1 deletion src/lib/ffmpeg-5.0/avformat.pas
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ TAVFormatContext = record
we_do_not_use_packet_size: cuint;
we_do_not_use_max_delay: cint;
flags: cint;
we_do_not_use_probesize: cint64;
we_do_not_use_max_analyze_duration: cint64;
we_do_not_use_key: pointer;
we_do_not_use_keylen: cint;
we_do_not_use_nb_programs: cuint;
we_do_not_use_programs: pointer;
we_do_not_use_video_codec_id: cenum;
we_do_not_use_audio_codec_id: cenum;
we_do_not_use_subtitle_codec_id: cenum;
we_do_not_use_max_index_size: cuint;
we_do_not_use_max_picture_buffer: cuint;
we_do_not_use_nb_chapters: cuint;
we_do_not_use_chapters: pointer;
metadata: PAVDictionary;
do_not_instantiate_this_record: incomplete_record;
end;
TAVStream = record
Expand All @@ -93,7 +107,7 @@ TAVStream = record
we_do_not_use_disposition: cint;
we_do_not_use_discard: cenum;
we_do_not_use_sample_aspect_ratio: TAVRational;
we_do_not_use_metadata: PAVDictionary;
metadata: PAVDictionary;
we_do_not_use_avg_frame_rate: TAVRational;
we_do_not_use_atached_pic: TAVPacket;
we_do_not_use_side_data: pointer;
Expand Down
6 changes: 6 additions & 0 deletions src/lib/ffmpeg-5.0/avutil.pas
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ TAVFrame = record
TAVDictionary = record
do_not_instantiate_this_record: incomplete_record;
end;
PAVDictionaryEntry = ^TAVDictionaryEntry;
TAVDictionaryEntry = record
key: PAnsiChar;
value: PAnsiChar;
end;
procedure av_free(ptr: pointer); cdecl; external av__util;
procedure av_freep(ptr: pointer); cdecl; external av__util;
function av_malloc(size: csize_t): pointer; cdecl; external av__util;
Expand All @@ -169,6 +174,7 @@ function av_samples_alloc(var audio_data: pcuint8; linesize: pcint; nb_channels:
function av_get_packed_sample_fmt(sample_fmt: TAVSampleFormat): TAVSampleFormat; cdecl; external av__util;
function av_get_bytes_per_sample(sample_fmt: TAVSampleFormat): cint; cdecl; external av__util;
procedure av_log_set_level(level: cint); cdecl; external av__util;
function av_dict_get(m: PAVDictionary; const key: PAnsiChar; prev: PAVDictionaryEntry; flags: cint): PAVDictionaryEntry; cdecl; external av__util;
implementation
function AVERROR(e: cint): cint; {$IFDEF HasInline}inline;{$ENDIF}
begin
Expand Down
16 changes: 15 additions & 1 deletion src/lib/ffmpeg-6.0/avformat.pas
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ TAVFormatContext = record
we_do_not_use_packet_size: cuint;
we_do_not_use_max_delay: cint;
flags: cint;
we_do_not_use_probesize: cint64;
we_do_not_use_max_analyze_duration: cint64;
we_do_not_use_key: pointer;
we_do_not_use_keylen: cint;
we_do_not_use_nb_programs: cuint;
we_do_not_use_programs: pointer;
we_do_not_use_video_codec_id: cenum;
we_do_not_use_audio_codec_id: cenum;
we_do_not_use_subtitle_codec_id: cenum;
we_do_not_use_max_index_size: cuint;
we_do_not_use_max_picture_buffer: cuint;
we_do_not_use_nb_chapters: cuint;
we_do_not_use_chapters: pointer;
metadata: PAVDictionary;
do_not_instantiate_this_record: incomplete_record;
end;
TAVStream = record
Expand All @@ -90,7 +104,7 @@ TAVStream = record
we_do_not_use_disposition: cint;
we_do_not_use_discard: cenum;
we_do_not_use_sample_aspect_ratio: TAVRational;
we_do_not_use_metadata: PAVDictionary;
metadata: PAVDictionary;
we_do_not_use_avg_frame_rate: TAVRational;
we_do_not_use_attached_pic: TAVPacket;
we_do_not_use_side_data: pointer;
Expand Down
6 changes: 6 additions & 0 deletions src/lib/ffmpeg-6.0/avutil.pas
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ TAVFrame = record
TAVDictionary = record
do_not_instantiate_this_record: incomplete_record;
end;
PAVDictionaryEntry = ^TAVDictionaryEntry;
TAVDictionaryEntry = record
key: PAnsiChar;
value: PAnsiChar;
end;
procedure av_free(ptr: pointer); cdecl; external av__util;
procedure av_freep(ptr: pointer); cdecl; external av__util;
function av_malloc(size: csize_t): pointer; cdecl; external av__util;
Expand All @@ -169,6 +174,7 @@ function av_samples_alloc(var audio_data: pcuint8; linesize: pcint; nb_channels:
function av_get_packed_sample_fmt(sample_fmt: TAVSampleFormat): TAVSampleFormat; cdecl; external av__util;
function av_get_bytes_per_sample(sample_fmt: TAVSampleFormat): cint; cdecl; external av__util;
procedure av_log_set_level(level: cint); cdecl; external av__util;
function av_dict_get(m: PAVDictionary; const key: PAnsiChar; prev: PAVDictionaryEntry; flags: cint): PAVDictionaryEntry; cdecl; external av__util;
implementation
function AVERROR(e: cint): cint; {$IFDEF HasInline}inline;{$ENDIF}
begin
Expand Down
13 changes: 12 additions & 1 deletion src/lib/ffmpeg-7.0/avformat.pas
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ TAVFormatContext = record
we_do_not_use_packet_size: cuint;
we_do_not_use_max_delay: cint;
flags: cint;
we_do_not_use_probesize: cint64;
we_do_not_use_max_analyze_duration: cint64;
we_do_not_use_key: pointer;
we_do_not_use_keylen: cint;
we_do_not_use_nb_programs: cuint;
we_do_not_use_programs: pointer;
we_do_not_use_video_codec_id: cenum;
we_do_not_use_audio_codec_id: cenum;
we_do_not_use_subtitle_codec_id: cenum;
we_do_not_use_data_codec_id: cenum;
metadata: PAVDictionary;
do_not_instantiate_this_record: incomplete_record;
end;
TAVStream = record
Expand All @@ -94,7 +105,7 @@ TAVStream = record
we_do_not_use_disposition: cint;
we_do_not_use_discard: cenum;
we_do_not_use_sample_aspect_ratio: TAVRational;
we_do_not_use_metadata: PAVDictionary;
metadata: PAVDictionary;
we_do_not_use_avg_frame_rate: TAVRational;
we_do_not_use_attached_pic: TAVPacket;
we_do_not_use_side_data: pointer;
Expand Down
6 changes: 6 additions & 0 deletions src/lib/ffmpeg-7.0/avutil.pas
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ TAVChannelLayout = record
TAVDictionary = record
do_not_instantiate_this_record: incomplete_record;
end;
PAVDictionaryEntry = ^TAVDictionaryEntry;
TAVDictionaryEntry = record
key: PAnsiChar;
value: PAnsiChar;
end;
procedure av_channel_layout_default(ch_layout: PAVChannelLayout; nb_channels: cint); cdecl; external av__util;
function av_channel_layout_from_string(channel_layout: PAVChannelLayout; str: PAnsiChar): cint; cdecl; external av__util;
procedure av_free(ptr: pointer); cdecl; external av__util;
Expand All @@ -179,6 +184,7 @@ function av_opt_set_sample_fmt(obj: pointer; name: PAnsiChar; fmt: TAVSampleForm
function av_get_packed_sample_fmt(sample_fmt: TAVSampleFormat): TAVSampleFormat; cdecl; external av__util;
function av_get_bytes_per_sample(sample_fmt: TAVSampleFormat): cint; cdecl; external av__util;
procedure av_log_set_level(level: cint); cdecl; external av__util;
function av_dict_get(m: PAVDictionary; const key: PAnsiChar; prev: PAVDictionaryEntry; flags: cint): PAVDictionaryEntry; cdecl; external av__util;
implementation
function AVERROR(e: cint): cint; {$IFDEF HasInline}inline;{$ENDIF}
begin
Expand Down
Loading

0 comments on commit 1862e67

Please sign in to comment.