diff options
Diffstat (limited to '')
-rw-r--r-- | mediaplugin/src/media/UAudioConverter.pas | 315 | ||||
-rw-r--r-- | mediaplugin/src/media/UMediaPlugin.pas | 5 | ||||
-rw-r--r-- | mediaplugin/src/ultrastardx.dpr | 7 |
3 files changed, 9 insertions, 318 deletions
diff --git a/mediaplugin/src/media/UAudioConverter.pas b/mediaplugin/src/media/UAudioConverter.pas index de6840ca..343d0742 100644 --- a/mediaplugin/src/media/UAudioConverter.pas +++ b/mediaplugin/src/media/UAudioConverter.pas @@ -36,10 +36,6 @@ interface uses UMusic, ULog, - ctypes, - {$IFDEF UseSRCResample} - samplerate, - {$ENDIF} UMediaPlugin, UMediaCore_SDL, sdl, @@ -76,51 +72,6 @@ type function GetRatio(): double; override; end; - {$IFDEF UseFFmpegResample} - TAudioConverterPlugin = class(TAudioConverter) - private - fPluginInfo: PMediaPluginInfo; - fAudioConverter: PAudioConverterInfo; - fStream: PAudioConvertStream; - public - function Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; override; - destructor Destroy(); override; - - function Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; override; - function GetOutputBufferSize(InputSize: integer): integer; override; - function GetRatio(): double; override; - end; - {$ENDIF} - - {$IFDEF UseSRCResample} - TAudioConverter_SRC = class(TAudioConverter) - private - ConverterState: PSRC_STATE; - ConversionData: SRC_DATA; - FormatConverter: TAudioConverter; - public - function Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; override; - destructor Destroy(); override; - - function Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; override; - function GetOutputBufferSize(InputSize: integer): integer; override; - function GetRatio(): double; override; - end; - - // Note: SRC (=libsamplerate) provides several converters with different quality - // speed trade-offs. The SINC-types are slow but offer best quality. - // The SRC_SINC_* converters are too slow for realtime conversion, - // (SRC_SINC_FASTEST is approx. ten times slower than SRC_LINEAR) resulting - // in audible clicks and pops. - // SRC_LINEAR is very fast and should have a better quality than SRC_ZERO_ORDER_HOLD - // because it interpolates the samples. Normal "non-audiophile" users should not - // be able to hear a difference between the SINC_* ones and LINEAR. Especially - // if people sing along with the song. - // But FFmpeg might offer a better quality/speed ratio than SRC_LINEAR. - const - SRC_CONVERTER_TYPE = SRC_LINEAR; - {$ENDIF} - implementation { TAudioConverter_SDL } @@ -255,272 +206,6 @@ begin Result := cvt.len_cvt; end; - -{$IFDEF UseFFmpegResample} - -const -{$IFDEF MSWINDOWS} - ffmpegPlugin = 'ffmpeg_playback.dll'; -{$ENDIF} -{$IFDEF LINUX} - ffmpegPlugin = 'ffmpeg_playback'; -{$ENDIF} -{$IFDEF DARWIN} - ffmpegPlugin = 'ffmpeg_playback.dylib'; - {$linklib ffmpegPlugin} -{$ENDIF} - -function Plugin_register(core: PMediaPluginCore): PMediaPluginInfo; - cdecl; external ffmpegPlugin; - -function TAudioConverterPlugin.Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; -var - CSrcFormatInfo: TCAudioFormatInfo; - CDstFormatInfo: TCAudioFormatInfo; -begin - inherited Init(SrcFormatInfo, DstFormatInfo); - - Result := false; - fPluginInfo := Plugin_register(MediaPluginCore); - fPluginInfo.initialize(); - fAudioConverter := fPluginInfo.audioConverter; - - AudioFormatInfoToCStruct(SrcFormatInfo, CSrcFormatInfo); - AudioFormatInfoToCStruct(DstFormatInfo, CDstFormatInfo); - fStream := fAudioConverter.open(@CSrcFormatInfo, @CDstFormatInfo); - if (fStream = nil) then - begin - Log.LogError('fAudioConverter.open() failed', 'TAudioConverterPlugin.Init'); - Exit; - end; - - Result := true; -end; - -destructor TAudioConverterPlugin.Destroy(); -begin - if (fStream <> nil) then - fAudioConverter.close(fStream); - inherited; -end; - -function TAudioConverterPlugin.Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; -begin - Result := fAudioConverter.convert(fStream, - PCuint8(InputBuffer), PCuint8(OutputBuffer), @InputSize); -end; - -function TAudioConverterPlugin.GetOutputBufferSize(InputSize: integer): integer; -begin - Result := fAudioConverter.getOutputBufferSize(fStream, InputSize); -end; - -function TAudioConverterPlugin.GetRatio(): double; -begin - Result := fAudioConverter.getRatio(fStream); -end; - -{$ENDIF} - - -{$IFDEF UseSRCResample} - -function TAudioConverter_SRC.Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; -var - error: integer; - TempSrcFormatInfo: TAudioFormatInfo; - TempDstFormatInfo: TAudioFormatInfo; -begin - inherited Init(SrcFormatInfo, DstFormatInfo); - - Result := false; - - FormatConverter := nil; - - // SRC does not handle channel or format conversion - if ((SrcFormatInfo.Channels <> DstFormatInfo.Channels) or - not (SrcFormatInfo.Format in [asfS16, asfFloat])) then - begin - // SDL can not convert to float, so we have to convert to SInt16 first - TempSrcFormatInfo := TAudioFormatInfo.Create( - SrcFormatInfo.Channels, SrcFormatInfo.SampleRate, SrcFormatInfo.Format); - TempDstFormatInfo := TAudioFormatInfo.Create( - DstFormatInfo.Channels, SrcFormatInfo.SampleRate, asfS16); - - // init format/channel conversion - FormatConverter := TAudioConverter_SDL.Create(); - if (not FormatConverter.Init(TempSrcFormatInfo, TempDstFormatInfo)) then - begin - Log.LogError('Unsupported input format', 'TAudioConverter_SRC.Init'); - FormatConverter.Free; - // exit after the format-info is freed - end; - - // this info was copied so we do not need it anymore - TempSrcFormatInfo.Free; - TempDstFormatInfo.Free; - - // leave if the format is not supported - if (not assigned(FormatConverter)) then - Exit; - - // adjust our copy of the input audio-format for SRC conversion - Self.SrcFormatInfo.Channels := DstFormatInfo.Channels; - Self.SrcFormatInfo.Format := asfS16; - end; - - if ((DstFormatInfo.Format <> asfS16) and - (DstFormatInfo.Format <> asfFloat)) then - begin - Log.LogError('Unsupported output format', 'TAudioConverter_SRC.Init'); - Exit; - end; - - ConversionData.src_ratio := DstFormatInfo.SampleRate / SrcFormatInfo.SampleRate; - if (src_is_valid_ratio(ConversionData.src_ratio) = 0) then - begin - Log.LogError('Invalid samplerate ratio', 'TAudioConverter_SRC.Init'); - Exit; - end; - - ConverterState := src_new(SRC_CONVERTER_TYPE, DstFormatInfo.Channels, @error); - if (ConverterState = nil) then - begin - Log.LogError('src_new() failed: ' + src_strerror(error), 'TAudioConverter_SRC.Init'); - Exit; - end; - - Result := true; -end; - -destructor TAudioConverter_SRC.Destroy(); -begin - if (ConverterState <> nil) then - src_delete(ConverterState); - FormatConverter.Free; - inherited; -end; - -function TAudioConverter_SRC.Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; -var - FloatInputBuffer: PSingle; - FloatOutputBuffer: PSingle; - TempBuffer: PByteArray; - TempSize: integer; - NumSamples: integer; - OutputSize: integer; - error: integer; -begin - Result := -1; - - TempBuffer := nil; - - // format conversion with external converter (to correct number of channels and format) - if (assigned(FormatConverter)) then - begin - TempSize := FormatConverter.GetOutputBufferSize(InputSize); - GetMem(TempBuffer, TempSize); - InputSize := FormatConverter.Convert(InputBuffer, TempBuffer, InputSize); - InputBuffer := TempBuffer; - end; - - if (InputSize <= 0) then - begin - // avoid div-by-zero problems - if (InputSize = 0) then - Result := 0; - if (TempBuffer <> nil) then - FreeMem(TempBuffer); - Exit; - end; - - if (SrcFormatInfo.Format = asfFloat) then - begin - FloatInputBuffer := PSingle(InputBuffer); - end else begin - NumSamples := InputSize div AudioSampleSize[SrcFormatInfo.Format]; - GetMem(FloatInputBuffer, NumSamples * SizeOf(Single)); - src_short_to_float_array(PCshort(InputBuffer), PCfloat(FloatInputBuffer), NumSamples); - end; - - // calculate approx. output size - OutputSize := Ceil(InputSize * ConversionData.src_ratio); - - if (DstFormatInfo.Format = asfFloat) then - begin - FloatOutputBuffer := PSingle(OutputBuffer); - end else begin - NumSamples := OutputSize div AudioSampleSize[DstFormatInfo.Format]; - GetMem(FloatOutputBuffer, NumSamples * SizeOf(Single)); - end; - - with ConversionData do - begin - data_in := PCFloat(FloatInputBuffer); - input_frames := InputSize div SrcFormatInfo.FrameSize; - data_out := PCFloat(FloatOutputBuffer); - output_frames := OutputSize div DstFormatInfo.FrameSize; - // TODO: set this to 1 at end of file-playback - end_of_input := 0; - end; - - error := src_process(ConverterState, @ConversionData); - if (error <> 0) then - begin - Log.LogError(src_strerror(error), 'TAudioConverter_SRC.Convert'); - if (SrcFormatInfo.Format <> asfFloat) then - FreeMem(FloatInputBuffer); - if (DstFormatInfo.Format <> asfFloat) then - FreeMem(FloatOutputBuffer); - if (TempBuffer <> nil) then - FreeMem(TempBuffer); - Exit; - end; - - if (SrcFormatInfo.Format <> asfFloat) then - FreeMem(FloatInputBuffer); - - if (DstFormatInfo.Format <> asfFloat) then - begin - NumSamples := ConversionData.output_frames_gen * DstFormatInfo.Channels; - src_float_to_short_array(PCfloat(FloatOutputBuffer), PCshort(OutputBuffer), NumSamples); - FreeMem(FloatOutputBuffer); - end; - - // free format conversion buffer if used - if (TempBuffer <> nil) then - FreeMem(TempBuffer); - - if (assigned(FormatConverter)) then - InputSize := ConversionData.input_frames_used * FormatConverter.SrcFormatInfo.FrameSize - else - InputSize := ConversionData.input_frames_used * SrcFormatInfo.FrameSize; - - // set result to output size according to SRC - Result := ConversionData.output_frames_gen * DstFormatInfo.FrameSize; -end; - -function TAudioConverter_SRC.GetOutputBufferSize(InputSize: integer): integer; -begin - Result := Ceil(InputSize * GetRatio()); -end; - -function TAudioConverter_SRC.GetRatio(): double; -begin - // if we need additional channel/format conversion, use this ratio - if (assigned(FormatConverter)) then - Result := FormatConverter.GetRatio() - else - Result := 1.0; - - // now the SRC ratio (Note: the format might change from SInt16 to float) - Result := Result * - ConversionData.src_ratio * - (DstFormatInfo.FrameSize / SrcFormatInfo.FrameSize); -end; - -{$ENDIF} - initialization MediaManager.add(TAudioConverter_SDL.Create); diff --git a/mediaplugin/src/media/UMediaPlugin.pas b/mediaplugin/src/media/UMediaPlugin.pas index 762b5af0..bf921423 100644 --- a/mediaplugin/src/media/UMediaPlugin.pas +++ b/mediaplugin/src/media/UMediaPlugin.pas @@ -161,6 +161,7 @@ uses UPathUtils, ULog, UAudioDecoderPlugin, + UAudioConverterPlugin, UVideoDecoderPlugin; var @@ -397,10 +398,10 @@ begin // register modules if (PluginInfo.audioDecoder <> nil) then MediaManager.Add(TAudioDecoderPlugin.Create(PluginInfo)); + if (PluginInfo.audioConverter <> nil) then + MediaManager.Add(TAudioConverterPlugin.Create(PluginInfo)); if (PluginInfo.videoDecoder <> nil) then MediaManager.Add(TVideoDecoderPlugin.Create(PluginInfo)); - //if (PluginInfo.audioConverter <> nil) then - // MediaManager.Add(); end; end; diff --git a/mediaplugin/src/ultrastardx.dpr b/mediaplugin/src/ultrastardx.dpr index 26ff65d5..e5922a65 100644 --- a/mediaplugin/src/ultrastardx.dpr +++ b/mediaplugin/src/ultrastardx.dpr @@ -245,7 +245,6 @@ uses UFFT in 'lib\fft\UFFT.pas', UAudioPlayback_SoftMixer in 'media\UAudioPlayback_SoftMixer.pas', {$IFEND} - UAudioConverter in 'media\UAudioConverter.pas', //****************************** //Pluggable media modules @@ -256,6 +255,7 @@ uses UMediaPlugin in 'media\UMediaPlugin.pas', UVideoDecoderPlugin in 'media\UVideoDecoderPlugin.pas', UAudioDecoderPlugin in 'media\UAudioDecoderPlugin.pas', + UAudioConverterPlugin in 'media\UAudioConverterPlugin.pas', UVideo in 'media\UVideo.pas', @@ -282,6 +282,11 @@ uses {$IFDEF UsePortaudioPlayback} UAudioPlayback_Portaudio in 'media\UAudioPlayback_Portaudio.pas', {$ENDIF} + UAudioConverter in 'media\UAudioConverter.pas', +// TODO +{$IFDEF UseSRCResample} + //UAudioConverter_SRC in 'media\UAudioConverter_SRC.pas', +{$ENDIF} // fallback dummy, must be last UMedia_dummy in 'media\UMedia_dummy.pas', |