├── .gitignore ├── COPYING.modifiedLGPL ├── COPYING.txt ├── LICENSES.txt ├── README.md ├── examples ├── encode2wav │ ├── writewav.lpi │ └── writewav.lpr ├── flac │ ├── flac_decode.lpi │ ├── flac_decode.lpr │ ├── pa_flac_decode.lpi │ └── pa_flac_decode.lpr ├── fpgui_player │ ├── guiplayer.lpi │ ├── guiplayer.lpr │ └── main_frm.pas ├── noiseremoval │ ├── noise.ogg │ ├── noiseremoval.lpi │ ├── noiseremoval.lpr │ ├── noisestereo.ogg │ ├── noisyaudio.ogg │ └── noisyaudiostereo.ogg ├── noiseremoval2 │ ├── noiseremoval2.lpi │ └── noiseremoval2.pas ├── playogg │ ├── playogg.lpi │ └── playogg.lpr ├── playopus │ ├── playopus.lpi │ └── playopus.lpr └── smartplay │ ├── smartplay.lpi │ └── smartplay.lpr ├── pascalaudioio ├── audacity_noiseremoval.pas ├── audacity_realfftf.pas ├── bs2b.pas ├── flac_callbacks.inc ├── flac_classes.pas ├── flac_decode.inc ├── flac_encode.inc ├── flac_format.inc ├── flac_metadata.inc ├── ladspa.pas ├── ladspa_classes.pas ├── mp4codec.pas ├── mp4codec_mp4a.pas ├── noiseremovalmultichannel.pas ├── ogghfobject.pas ├── pa_ringbuffer.pas ├── paio_channelhelper.pas ├── paio_faad2.pas ├── paio_messagequeue.pas ├── paio_mmdevice.pas ├── paio_ogg_container.pas ├── paio_ogg_opus.pas ├── paio_opus.pas ├── paio_types.pas ├── paio_utils.pas ├── paio_vorbis_comment.pas ├── pascalaudioio.lpk ├── pascalaudioio.pas ├── quicktimeatoms.pas ├── quicktimecontainer.pas ├── resample.pas └── samplerate.pas └── pascalaudiosuite ├── pa_base.pas ├── pa_binaural.pas ├── pa_cdaudio.pas ├── pa_dec_oggvorbis.pas ├── pa_enc_oggvorbis.pas ├── pa_flac.pas ├── pa_ladspa.pas ├── pa_lists.pas ├── pa_m4a.pas ├── pa_mmdevice.pas ├── pa_noiseremoval.pas ├── pa_ogg_opus.pas ├── pa_process.pas ├── pa_pulse_simple.pas ├── pa_register.pas ├── pa_resample.pas ├── pa_samplerate.pas ├── pa_sox.pas ├── pa_stream.pas ├── pa_wav.pas ├── pascalaudiosuite.lpk └── pascalaudiosuite.pas /.gitignore: -------------------------------------------------------------------------------- 1 | # Lazarus compiler-generated binaries (safe to delete) 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | *.lrs 7 | *.res 8 | *.compiled 9 | *.dbg 10 | *.ppu 11 | *.[oa] 12 | *.or 13 | 14 | # Lazarus autogenerated files (duplicated info) 15 | *.rst 16 | *.rsj 17 | *.lrt 18 | 19 | # Lazarus local files (user-specific info) 20 | *.lps 21 | 22 | # Lazarus backups and unit output folders. 23 | # These can be changed by user in Lazarus/project options. 24 | backup/ 25 | *.bak* 26 | lib/ 27 | units/ 28 | 29 | -------------------------------------------------------------------------------- /COPYING.modifiedLGPL: -------------------------------------------------------------------------------- 1 | 2 | This is the file COPYING.modifiedLGPL, it applies to all pascalaudio sources 3 | distributed with this file unless otherwise stated in each file. All files 4 | contains headers showing the appropriate license. 5 | 6 | See licenses.txt for an attempt to clarify which files are which license. 7 | 8 | These files are distributed under the GNU Library Public License 9 | (see the file COPYING.txt) with the following modification: 10 | 11 | As a special exception, the copyright holders of this library give you 12 | permission to link this library with independent modules to produce an 13 | executable, regardless of the license terms of these independent modules, 14 | and to copy and distribute the resulting executable under terms of your choice, 15 | provided that you also meet, for each linked independent module, the terms 16 | and conditions of the license of that module. An independent module is a 17 | module which is not derived from or based on this library. If you modify this 18 | library, you may extend this exception to your version of the library, but 19 | you are not obligated to do so. If you do not wish to do so, delete this 20 | exception statement from your version. 21 | 22 | 23 | If you didn't receive a copy of the file COPYING.txt, contact: 24 | Free Software Foundation, Inc., 25 | 675 Mass Ave 26 | Cambridge, MA 02139 27 | USA 28 | 29 | -------------------------------------------------------------------------------- /LICENSES.txt: -------------------------------------------------------------------------------- 1 | ladspa.pas - LGPL * 2 | ladspa_classes.pas - LGPL * 3 | samplerate.pas - LGPL ** 4 | resample.pas - LGPL * 5 | audacity_noiseremoval.pas - GPL # 6 | audacity_realfft.pas - GPL # 7 | ogghfobject.pas - LGPL * 8 | bs2b.pas - LGPL *** 9 | 10 | Other files prefixed with pa_*.pas are modified LGPL with static linking exception 11 | 12 | 13 | * modified LGPL with static linking exception. this applies to the .o file made 14 | by the unit. To the best of my knowledge the libraries the .o files link to, 15 | if any, are LGPL libraries. 16 | 17 | ** modified LGPL with static linking exception. this applies to the .o file made 18 | by the unit. However the libraries they link to are GPL not LGPL. 19 | 20 | ***modified LGPL with static linking exception. this applies to the .o file made 21 | by the unit. b2sb - seems very open. http://bs2b.sourceforge.net/license.html 22 | 23 | # Audacity is GPL and the converted filter came from audacity's source. Might be 24 | considered LGPL but assume GPL. Find out. 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pascal Audio 2 | 3 | Audio classes to decode and encode various formats including Ogg, Opus, Flac, 4 | m4a made for use with the Free Pascal compiler. It's also possible to use other 5 | external programs such as Sox, mpg321 or ffplay to decode any other format. 6 | It can currently output audio to the _MM_ system of windows or _PulseAudio_ for 7 | linux. 8 | 9 | Also there can be effects applied to the audio such as resampling via 10 | _libresample_ or _libsamplerate_. LADSPA2 plugins are also possible to 11 | incorporate. As well as the noise removal filter from Audacity. 12 | 13 | It is divided into two packages: PascalAudioIO and PascalAudioSuite. 14 | 15 | ## PascalAudioIO 16 | 17 | This package contains the bindings and simple classes to directly use the 18 | libraries such as libogg or libflac etc. If you don't want to use the 19 | "Suite" which has a somewhat complicated threaded mechanism, then you need only 20 | this. 21 | 22 | ## PascalAudioSuite 23 | 24 | Using "Links" starting with a _Source_ link and ending with a _Destination_ 25 | link it's possible to decode and audio file and convert/play it or using any 26 | links. If the final link in the chan is PulseAudio then the decoded file will 27 | play on the speakers. Each link in the chain operates inside it's own thread 28 | and processes the data before handing off it's buffer to be handled by the next 29 | link. 30 | 31 | To apply some effect to the decoded audio just insert some links into the 32 | middle of the chain. See the examples folder to see how simple this is. 33 | 34 | ## Adding new codecs or sources and destinations 35 | 36 | Adding new codecs is really not that hard and can be done in a few minutes or 37 | hours as long as the binding already exists. 38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/encode2wav/writewav.lpi: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | <UseAppBundle Value="False"/> 14 | <ResourceType Value="res"/> 15 | <XPManifest> 16 | <TextName Value="CompanyName.ProductName.AppName"/> 17 | <TextDesc Value="Your application description."/> 18 | </XPManifest> 19 | </General> 20 | <VersionInfo> 21 | <StringTable ProductVersion=""/> 22 | </VersionInfo> 23 | <BuildModes Count="1"> 24 | <Item1 Name="Default" Default="True"/> 25 | </BuildModes> 26 | <PublishOptions> 27 | <Version Value="2"/> 28 | </PublishOptions> 29 | <RunParams> 30 | <local> 31 | <FormatVersion Value="1"/> 32 | </local> 33 | </RunParams> 34 | <RequiredPackages Count="2"> 35 | <Item1> 36 | <PackageName Value="PascalAudioSuite"/> 37 | </Item1> 38 | <Item2> 39 | <PackageName Value="PascalAudioIO"/> 40 | </Item2> 41 | </RequiredPackages> 42 | <Units Count="1"> 43 | <Unit0> 44 | <Filename Value="writewav.lpr"/> 45 | <IsPartOfProject Value="True"/> 46 | </Unit0> 47 | </Units> 48 | </ProjectOptions> 49 | <CompilerOptions> 50 | <Version Value="11"/> 51 | <Target> 52 | <Filename Value="writewav"/> 53 | </Target> 54 | <SearchPaths> 55 | <IncludeFiles Value="$(ProjOutDir)"/> 56 | <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> 57 | </SearchPaths> 58 | </CompilerOptions> 59 | <Debugging> 60 | <Exceptions Count="3"> 61 | <Item1> 62 | <Name Value="EAbort"/> 63 | </Item1> 64 | <Item2> 65 | <Name Value="ECodetoolError"/> 66 | </Item2> 67 | <Item3> 68 | <Name Value="EFOpenError"/> 69 | </Item3> 70 | </Exceptions> 71 | </Debugging> 72 | </CONFIG> 73 | -------------------------------------------------------------------------------- /examples/encode2wav/writewav.lpr: -------------------------------------------------------------------------------- 1 | program writewav; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | uses 6 | {$IFDEF UNIX} 7 | cthreads, 8 | {$ENDIF} 9 | Classes, sysutils, pa_wav, pa_dec_oggvorbis; 10 | 11 | var 12 | Ogg: TPAOggVorbisDecoderSource; 13 | Wav: TPAWavDest; 14 | 15 | const 16 | InFile = '../noiseremoval/noisyaudio.ogg'; 17 | OutFile = 'writewav.wav'; 18 | 19 | procedure CreateObjects; 20 | begin 21 | Ogg := TPAOggVorbisDecoderSource.Create; 22 | Ogg.Stream := TFileStream.Create(InFile, fmOpenRead); 23 | Ogg.InitValues; 24 | Wav := TPAWavDest.Create(TFileStream.Create(OutFile, fmOpenWrite or fmCreate), True); 25 | Wav.DataSource := Ogg; 26 | end; 27 | 28 | procedure Encode; 29 | begin 30 | Ogg.StartData; 31 | 32 | while Ogg.Working or Wav.Working do 33 | CheckSynchronize(1); 34 | end; 35 | 36 | procedure FreeObjects; 37 | begin 38 | Ogg.Stream.Free; 39 | Ogg.Free; 40 | Wav.Free; 41 | end; 42 | 43 | 44 | begin 45 | CreateObjects; 46 | Encode; 47 | FreeObjects; 48 | end. 49 | 50 | -------------------------------------------------------------------------------- /examples/flac/flac_decode.lpi: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <CONFIG> 3 | <ProjectOptions> 4 | <Version Value="10"/> 5 | <General> 6 | <Flags> 7 | <MainUnitHasCreateFormStatements Value="False"/> 8 | <MainUnitHasTitleStatement Value="False"/> 9 | </Flags> 10 | <SessionStorage Value="InProjectDir"/> 11 | <MainUnit Value="0"/> 12 | <Title Value="flac_decode"/> 13 | <UseAppBundle Value="False"/> 14 | <ResourceType Value="res"/> 15 | <XPManifest> 16 | <TextName Value="CompanyName.ProductName.AppName"/> 17 | <TextDesc Value="Your application description."/> 18 | </XPManifest> 19 | </General> 20 | <BuildModes Count="1"> 21 | <Item1 Name="Default" Default="True"/> 22 | </BuildModes> 23 | <PublishOptions> 24 | <Version Value="2"/> 25 | </PublishOptions> 26 | <RunParams> 27 | <local> 28 | <FormatVersion Value="1"/> 29 | </local> 30 | </RunParams> 31 | <RequiredPackages Count="2"> 32 | <Item1> 33 | <PackageName Value="PascalAudioSuite"/> 34 | </Item1> 35 | <Item2> 36 | <PackageName Value="PascalAudioIO"/> 37 | </Item2> 38 | </RequiredPackages> 39 | <Units Count="1"> 40 | <Unit0> 41 | <Filename Value="flac_decode.lpr"/> 42 | <IsPartOfProject Value="True"/> 43 | </Unit0> 44 | </Units> 45 | </ProjectOptions> 46 | <CompilerOptions> 47 | <Version Value="11"/> 48 | <Target> 49 | <Filename Value="flac_decode"/> 50 | </Target> 51 | <SearchPaths> 52 | <IncludeFiles Value="$(ProjOutDir)"/> 53 | <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> 54 | </SearchPaths> 55 | </CompilerOptions> 56 | <Debugging> 57 | <Exceptions Count="3"> 58 | <Item1> 59 | <Name Value="EAbort"/> 60 | </Item1> 61 | <Item2> 62 | <Name Value="ECodetoolError"/> 63 | </Item2> 64 | <Item3> 65 | <Name Value="EFOpenError"/> 66 | </Item3> 67 | </Exceptions> 68 | </Debugging> 69 | </CONFIG> 70 | -------------------------------------------------------------------------------- /examples/flac/flac_decode.lpr: -------------------------------------------------------------------------------- 1 | program flac_decode; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | uses 6 | {$IFDEF UNIX}{$IFDEF UseCThreads} 7 | cthreads, 8 | {$ENDIF}{$ENDIF} 9 | Classes, flac_classes, paio_channelhelper; 10 | 11 | type 12 | TDecode = class 13 | private 14 | Decoder: TFlacStreamDecoder; 15 | OutputStream: TStream; 16 | function DataEvent(Sender: TFlacStreamDecoder; Samples: Integer; Channels: Integer; ChannelData: PPLongInt): Boolean; 17 | procedure MetadataEvent(Sender: TFlacStreamDecoder; Metadata: TFlacStreamMetadata); 18 | public 19 | constructor Create(AFile: String); 20 | end; 21 | var 22 | Decode: TDecode; 23 | 24 | { TDecode } 25 | 26 | constructor TDecode.Create(AFile: String); 27 | var 28 | InputStream: TFileStream; 29 | begin 30 | // create input and output streams 31 | InputStream := TFileStream.Create(AFile, fmOpenRead); 32 | OutputStream := THandleStream.Create(StdOutputHandle); // :) 33 | // create the decoder object and set callbacks 34 | Decoder := TFlacStreamDecoder.Create(InputStream, False); 35 | Decoder.OnOutput:=@DataEvent; 36 | Decoder.OnMetadata:=@MetadataEvent; 37 | 38 | // process metadata to fill channels, samplerate and bitspersample 39 | Decoder.ProcessUntilEndOfMetadata; 40 | 41 | // write some info to stderr since stdout will contain raw audio data 42 | WriteLn(StdErr, 'Channels: ', Decoder.Channels); 43 | WriteLn(StdErr, 'SampleRate: ', Decoder.SampleRate); 44 | WriteLn(StdErr, 'BitsPerSample: ', Decoder.BitsPerSample); 45 | 46 | // process the audio data 47 | Decoder.ProcessUntilEndOfStream; 48 | Decoder.Flush; 49 | 50 | // free stuff 51 | Decoder.Free; 52 | OutputStream.Free; 53 | InputStream.Free; 54 | end; 55 | 56 | function TDecode.DataEvent(Sender: TFlacStreamDecoder; Samples: Integer; Channels: Integer; ChannelData: PPLongInt): Boolean; 57 | var 58 | i, j: Integer; 59 | DataSize: Integer; 60 | begin 61 | // write the output plexing the channels 62 | DataSize:=Sender.BitsPerSample div 8; 63 | for i := 0 to Samples-1 do 64 | for j := 0 to Channels-1 do 65 | OutputStream.Write(ChannelData[j][i], DataSize); 66 | Result := True; 67 | end; 68 | 69 | procedure TDecode.MetadataEvent(Sender: TFlacStreamDecoder; Metadata: TFlacStreamMetadata); 70 | begin 71 | WriteLn(Stderr, 'Metadata: ', Metadata.ClassName); 72 | end; 73 | 74 | begin 75 | Decode := TDecode.Create(ParamStr(1)); 76 | Decode.Free; 77 | end. 78 | 79 | -------------------------------------------------------------------------------- /examples/flac/pa_flac_decode.lpi: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <CONFIG> 3 | <ProjectOptions> 4 | <Version Value="10"/> 5 | <General> 6 | <Flags> 7 | <MainUnitHasCreateFormStatements Value="False"/> 8 | <MainUnitHasTitleStatement Value="False"/> 9 | </Flags> 10 | <SessionStorage Value="InProjectDir"/> 11 | <MainUnit Value="0"/> 12 | <Title Value="pa_flac_decode"/> 13 | <UseAppBundle Value="False"/> 14 | <ResourceType Value="res"/> 15 | <XPManifest> 16 | <TextName Value="CompanyName.ProductName.AppName"/> 17 | <TextDesc Value="Your application description."/> 18 | </XPManifest> 19 | </General> 20 | <BuildModes Count="1"> 21 | <Item1 Name="Default" Default="True"/> 22 | </BuildModes> 23 | <PublishOptions> 24 | <Version Value="2"/> 25 | </PublishOptions> 26 | <RunParams> 27 | <local> 28 | <FormatVersion Value="1"/> 29 | </local> 30 | </RunParams> 31 | <RequiredPackages Count="2"> 32 | <Item1> 33 | <PackageName Value="PascalAudioSuite"/> 34 | </Item1> 35 | <Item2> 36 | <PackageName Value="PascalAudioIO"/> 37 | </Item2> 38 | </RequiredPackages> 39 | <Units Count="1"> 40 | <Unit0> 41 | <Filename Value="pa_flac_decode.lpr"/> 42 | <IsPartOfProject Value="True"/> 43 | </Unit0> 44 | </Units> 45 | </ProjectOptions> 46 | <CompilerOptions> 47 | <Version Value="11"/> 48 | <Target> 49 | <Filename Value="pa_flac_decode"/> 50 | </Target> 51 | <SearchPaths> 52 | <IncludeFiles Value="$(ProjOutDir)"/> 53 | <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> 54 | </SearchPaths> 55 | </CompilerOptions> 56 | <Debugging> 57 | <Exceptions Count="3"> 58 | <Item1> 59 | <Name Value="EAbort"/> 60 | </Item1> 61 | <Item2> 62 | <Name Value="ECodetoolError"/> 63 | </Item2> 64 | <Item3> 65 | <Name Value="EFOpenError"/> 66 | </Item3> 67 | </Exceptions> 68 | </Debugging> 69 | </CONFIG> 70 | -------------------------------------------------------------------------------- /examples/flac/pa_flac_decode.lpr: -------------------------------------------------------------------------------- 1 | program pa_flac_decode; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | uses 6 | {$IFDEF UNIX} 7 | cthreads, 8 | {$ENDIF} 9 | Classes, pa_flac, pa_stream; 10 | 11 | var 12 | SourceFlac: TPAFlacSource; 13 | DestStream: TPAStreamDestination; 14 | procedure CreateObjects; 15 | var 16 | InputStream: TFileStream; 17 | OutputStream: THandleStream; 18 | begin 19 | 20 | InputStream := TFileStream.Create(ParamStr(1), fmOpenRead); 21 | OutputStream := THandleStream.Create(StdOutputHandle); // :) 22 | 23 | SourceFlac := TPAFlacSource.Create(InputStream, True); 24 | DestStream := TPAStreamDestination.Create(OutputStream, True); 25 | DestStream.DataSource := SourceFlac; 26 | 27 | // write some info to stderr since stdout will contain raw audio data 28 | WriteLn(StdErr, 'Channels: ', SourceFlac.Channels); 29 | WriteLn(StdErr, 'SampleRate: ', SourceFlac.SamplesPerSecond); 30 | WriteLn(StdErr, 'Format: ', SourceFlac.Format); 31 | end; 32 | 33 | procedure FreeObjects; 34 | begin 35 | SourceFlac.Free; 36 | DestStream.Free; 37 | end; 38 | 39 | procedure Decode; 40 | begin 41 | SourceFlac.StartData; 42 | 43 | while SourceFlac.Working or DestStream.Working do 44 | CheckSynchronize(1); 45 | end; 46 | 47 | begin 48 | CreateObjects; 49 | Decode; 50 | FreeObjects; 51 | end. 52 | 53 | -------------------------------------------------------------------------------- /examples/fpgui_player/guiplayer.lpi: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <CONFIG> 3 | <ProjectOptions> 4 | <Version Value="12"/> 5 | <General> 6 | <Flags> 7 | <CompatibilityMode Value="True"/> 8 | </Flags> 9 | <SessionStorage Value="InProjectDir"/> 10 | <Title Value="guiplayer"/> 11 | <UseAppBundle Value="False"/> 12 | <ResourceType Value="res"/> 13 | </General> 14 | <BuildModes Count="2"> 15 | <Item1 Name="Default" Default="True"/> 16 | <Item2 Name="Win64"> 17 | <CompilerOptions> 18 | <Version Value="11"/> 19 | <Target> 20 | <Filename Value="guiplayer"/> 21 | </Target> 22 | <SearchPaths> 23 | <IncludeFiles Value="$(ProjOutDir)"/> 24 | <UnitOutputDirectory Value="units"/> 25 | </SearchPaths> 26 | </CompilerOptions> 27 | </Item2> 28 | </BuildModes> 29 | <PublishOptions> 30 | <Version Value="2"/> 31 | </PublishOptions> 32 | <RunParams> 33 | <FormatVersion Value="2"/> 34 | <Modes Count="1"> 35 | <Mode0 Name="default"/> 36 | </Modes> 37 | </RunParams> 38 | <RequiredPackages Count="2"> 39 | <Item1> 40 | <PackageName Value="PascalAudioSuite"/> 41 | </Item1> 42 | <Item2> 43 | <PackageName Value="fpgui_toolkit"/> 44 | </Item2> 45 | </RequiredPackages> 46 | <Units Count="2"> 47 | <Unit0> 48 | <Filename Value="guiplayer.lpr"/> 49 | <IsPartOfProject Value="True"/> 50 | </Unit0> 51 | <Unit1> 52 | <Filename Value="main_frm.pas"/> 53 | <IsPartOfProject Value="True"/> 54 | </Unit1> 55 | </Units> 56 | </ProjectOptions> 57 | <CompilerOptions> 58 | <Version Value="11"/> 59 | <Target> 60 | <Filename Value="guiplayer"/> 61 | </Target> 62 | <SearchPaths> 63 | <IncludeFiles Value="$(ProjOutDir)"/> 64 | <UnitOutputDirectory Value="units"/> 65 | </SearchPaths> 66 | </CompilerOptions> 67 | <Debugging> 68 | <Exceptions Count="4"> 69 | <Item1> 70 | <Name Value="EAbort"/> 71 | </Item1> 72 | <Item2> 73 | <Name Value="ECodetoolError"/> 74 | </Item2> 75 | <Item3> 76 | <Name Value="EFOpenError"/> 77 | </Item3> 78 | <Item4> 79 | <Name Value="EGroupLookupError"/> 80 | </Item4> 81 | </Exceptions> 82 | </Debugging> 83 | </CONFIG> 84 | -------------------------------------------------------------------------------- /examples/fpgui_player/guiplayer.lpr: -------------------------------------------------------------------------------- 1 | program guiplayer; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | uses 6 | {$IFDEF UNIX} 7 | cthreads, 8 | {$ENDIF} 9 | Classes, SysUtils, fpg_main, 10 | main_frm; 11 | 12 | 13 | procedure MainProc; 14 | var 15 | frm: TMainForm; 16 | begin 17 | fpgApplication.Initialize; 18 | fpgApplication.CreateForm(TMainForm, frm); 19 | try 20 | frm.Show; 21 | fpgApplication.Run; 22 | finally 23 | frm.Free; 24 | end; 25 | end; 26 | 27 | begin 28 | MainProc; 29 | end. 30 | 31 | -------------------------------------------------------------------------------- /examples/fpgui_player/main_frm.pas: -------------------------------------------------------------------------------- 1 | unit main_frm; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils, 9 | fpg_base, fpg_main, fpg_form, fpg_label, fpg_progressbar, fpg_button, pa_base, pa_stream; 10 | 11 | type 12 | 13 | TMainForm = class(TfpgForm) 14 | private 15 | {@VFD_HEAD_BEGIN: MainForm} 16 | Label1: TfpgLabel; 17 | btnOpen: TfpgButton; 18 | ProgressBar1: TfpgProgressBar; 19 | lblPosition: TfpgLabel; 20 | lblTotal: TfpgLabel; 21 | {@VFD_HEAD_END: MainForm} 22 | ProgTimer: TfpgTimer; 23 | FSource: TPAStreamSource; 24 | FDest: TPAAudioDestination; 25 | FFileName: String; 26 | 27 | procedure btnOpenClick(Sender: TObject); 28 | procedure ProgressMouseDown(Sender: TObject; AButton: TMouseButton; AShift: TShiftState; const AMousePos: TPoint); 29 | procedure ProgTimerTimer(Sender: TObject); 30 | function GetFilter: String; 31 | procedure OpenFile(AFileName: String); 32 | public 33 | procedure AfterCreate; override; 34 | procedure BeforeDestruction; override; 35 | end; 36 | 37 | {@VFD_NEWFORM_DECL} 38 | 39 | implementation 40 | 41 | uses 42 | pa_dec_oggvorbis, 43 | pa_flac, 44 | pa_wav, 45 | pa_m4a, 46 | pa_register, 47 | pa_pulse_simple, 48 | fpg_dialogs; 49 | 50 | {@VFD_NEWFORM_IMPL} 51 | 52 | function SecondsToTime(ASeconds: Double): String; 53 | var 54 | Hours, 55 | Minutes, 56 | Seconds: Integer; 57 | function Pad(AInteger: Integer): String; 58 | begin 59 | Result := IntToStr(AInteger); 60 | if Length(Result) < 2 then 61 | Result := '0'+Result; 62 | end; 63 | 64 | begin 65 | Hours := Trunc(ASeconds) div 3600; 66 | Minutes := Trunc(ASeconds) div 60 mod 60; 67 | Seconds:= Trunc(ASeconds) mod 60; 68 | 69 | if Hours > 0 then 70 | Result := Pad(Hours)+':' 71 | else 72 | Result := ''; 73 | 74 | Result := Result + Pad(Minutes)+':'+Pad(Seconds); 75 | 76 | 77 | end; 78 | 79 | procedure TMainForm.ProgTimerTimer(Sender: TObject); 80 | var 81 | Playable: IPAPlayable; 82 | PosMax, 83 | PosCurrent: Double; 84 | begin 85 | if Assigned(FSource) then 86 | if FSource.GetInterface('IPAPlayable', Playable) then 87 | begin 88 | PosMax := Playable.GetMaxPosition; 89 | PosCurrent:=Playable.GetPosition; 90 | ProgressBar1.Max:=Trunc(PosMax*100); 91 | ProgressBar1.Position:=Trunc(PosCurrent*100); 92 | 93 | lblPosition.Text := SecondsToTime(PosCurrent); 94 | lblTotal.Text:=SecondsToTime(PosMax); 95 | end; 96 | end; 97 | 98 | function TMainForm.GetFilter: String; 99 | var 100 | Items: TStrings; 101 | Extentions: TStrings; 102 | i: Integer; 103 | AllAudioFilter: String; 104 | EachFilter: String; 105 | begin 106 | Extentions := TStringList.Create; 107 | Items := PARegisteredGetList(partDecoder, Extentions); 108 | 109 | if Assigned(Items) then 110 | begin 111 | for i := 0 to Items.Count-1 do 112 | begin 113 | if i > 0 then 114 | AllAudioFilter := AllAudioFilter +';'; 115 | AllAudioFilter := AllAudioFilter + '*'+Extentions[i]; 116 | EachFilter:=EachFilter+'|'+Items[I]+'|*'+Extentions[i]; 117 | 118 | end; 119 | Items.Free; 120 | end; 121 | Extentions.Free; 122 | 123 | Result := 'Audio files|'+AllAudioFilter+EachFilter+'|All files|*'; 124 | 125 | end; 126 | 127 | procedure TMainForm.OpenFile(AFileName: String); 128 | begin 129 | if Assigned(FSource) then 130 | begin 131 | FSource.Free; 132 | //FDest.Free; 133 | end; 134 | 135 | FSource := PARegisteredGetDecoderClass(AFileName, False).Create(TFileStream.Create(AFileName, fmOpenRead)); 136 | 137 | if not Assigned(FDest) then 138 | FDest := PARegisteredGetDeviceOut('').Create; 139 | FDest.DataSource := FSource; 140 | FSource.StartData; 141 | ProgTimer.Enabled:=True; 142 | end; 143 | 144 | procedure TMainForm.btnOpenClick(Sender: TObject); 145 | var 146 | dlg: TfpgFileDialog; 147 | begin 148 | dlg := TfpgFileDialog.Create(Self); 149 | if FFileName <> '' then 150 | dlg.InitialDir := ExtractFileDir(FFileName) 151 | else 152 | dlg.InitialDir:=GetUserDir; 153 | dlg.Filter:=GetFilter; 154 | 155 | if dlg.RunOpenFile then 156 | begin 157 | dlg.Close; 158 | FFileName:=dlg.FileName; 159 | OpenFile(dlg.FileName); 160 | Label1.Text:=dlg.FileName; 161 | end; 162 | 163 | dlg.Free; 164 | 165 | end; 166 | 167 | procedure TMainForm.ProgressMouseDown(Sender: TObject; AButton: TMouseButton; AShift: TShiftState; const AMousePos: TPoint); 168 | var 169 | Percent: Double; 170 | Playable: IPAPlayable; 171 | begin 172 | if Assigned(FSource) then 173 | if FSource.GetInterface('IPAPlayable', Playable) then 174 | begin 175 | Percent:=AMousePos.x / ProgressBar1.Width; 176 | Playable.SetPosition(Playable.GetMaxPosition*Percent); 177 | end; 178 | end; 179 | 180 | procedure TMainForm.AfterCreate; 181 | begin 182 | {%region 'Auto-generated GUI code' -fold} 183 | {@VFD_BODY_BEGIN: MainForm} 184 | Name := 'MainForm'; 185 | SetPosition(960, 231, 380, 211); 186 | WindowTitle := 'MainForm'; 187 | Hint := ''; 188 | IconName := ''; 189 | 190 | Label1 := TfpgLabel.Create(self); 191 | with Label1 do 192 | begin 193 | Name := 'Label1'; 194 | SetPosition(20, 55, 345, 15); 195 | Anchors := [anLeft,anRight,anTop]; 196 | FontDesc := '#Label1'; 197 | Hint := ''; 198 | Text := 'Label'; 199 | end; 200 | 201 | btnOpen := TfpgButton.Create(self); 202 | with btnOpen do 203 | begin 204 | Name := 'btnOpen'; 205 | SetPosition(285, 20, 80, 23); 206 | Anchors := [anRight,anTop]; 207 | Text := 'Open File'; 208 | FontDesc := '#Label1'; 209 | Hint := ''; 210 | ImageName := ''; 211 | TabOrder := 3; 212 | OnClick:=@btnOpenClick; 213 | end; 214 | 215 | ProgressBar1 := TfpgProgressBar.Create(self); 216 | with ProgressBar1 do 217 | begin 218 | Name := 'ProgressBar1'; 219 | SetPosition(20, 80, 345, 22); 220 | Anchors := [anLeft,anRight,anTop]; 221 | Hint := 'Click to seek'; 222 | ParentShowHint := False; 223 | ShowHint := True; 224 | OnMouseDown:=@ProgressMouseDown; 225 | end; 226 | 227 | lblPosition := TfpgLabel.Create(self); 228 | with lblPosition do 229 | begin 230 | Name := 'lblPosition'; 231 | SetPosition(20, 110, 80, 15); 232 | FontDesc := '#Label1'; 233 | Hint := ''; 234 | Text := 'CurPos'; 235 | end; 236 | 237 | lblTotal := TfpgLabel.Create(self); 238 | with lblTotal do 239 | begin 240 | Name := 'lblTotal'; 241 | SetPosition(285, 110, 80, 15); 242 | Anchors := [anRight,anTop]; 243 | Alignment := taRightJustify; 244 | FontDesc := '#Label1'; 245 | Hint := ''; 246 | Text := 'Total'; 247 | end; 248 | 249 | {@VFD_BODY_END: MainForm} 250 | {%endregion} 251 | ProgTimer := TfpgTimer.Create(100); 252 | ProgTimer.OnTimer:=@ProgTimerTimer; 253 | end; 254 | 255 | procedure TMainForm.BeforeDestruction; 256 | begin 257 | inherited BeforeDestruction; 258 | if Assigned(FSource) then 259 | FreeAndNil(FSource); 260 | if Assigned(FDest) then 261 | FreeAndNil(FDest); 262 | end; 263 | 264 | end. 265 | 266 | -------------------------------------------------------------------------------- /examples/noiseremoval/noise.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewd207/PascalAudio/5a9a73576dfc9eb96451ee5c0b132ba77c7d8002/examples/noiseremoval/noise.ogg -------------------------------------------------------------------------------- /examples/noiseremoval/noiseremoval.lpi: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <CONFIG> 3 | <ProjectOptions> 4 | <Version Value="9"/> 5 | <General> 6 | <Flags> 7 | <MainUnitHasCreateFormStatements Value="False"/> 8 | <MainUnitHasTitleStatement Value="False"/> 9 | </Flags> 10 | <SessionStorage Value="InProjectDir"/> 11 | <MainUnit Value="0"/> 12 | <Title Value="noiseremoval"/> 13 | <UseAppBundle Value="False"/> 14 | <ResourceType Value="res"/> 15 | </General> 16 | <i18n> 17 | <EnableI18N LFM="False"/> 18 | </i18n> 19 | <VersionInfo> 20 | <StringTable ProductVersion=""/> 21 | </VersionInfo> 22 | <BuildModes Count="1"> 23 | <Item1 Name="Default" Default="True"/> 24 | </BuildModes> 25 | <PublishOptions> 26 | <Version Value="2"/> 27 | </PublishOptions> 28 | <RunParams> 29 | <local> 30 | <FormatVersion Value="1"/> 31 | </local> 32 | </RunParams> 33 | <RequiredPackages Count="1"> 34 | <Item1> 35 | <PackageName Value="PascalAudioSuite"/> 36 | </Item1> 37 | </RequiredPackages> 38 | <Units Count="1"> 39 | <Unit0> 40 | <Filename Value="noiseremoval.lpr"/> 41 | <IsPartOfProject Value="True"/> 42 | </Unit0> 43 | </Units> 44 | </ProjectOptions> 45 | <CompilerOptions> 46 | <Version Value="11"/> 47 | <Target> 48 | <Filename Value="noiseremoval"/> 49 | </Target> 50 | <SearchPaths> 51 | <IncludeFiles Value="$(ProjOutDir)"/> 52 | <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> 53 | </SearchPaths> 54 | </CompilerOptions> 55 | <Debugging> 56 | <Exceptions Count="3"> 57 | <Item1> 58 | <Name Value="EAbort"/> 59 | </Item1> 60 | <Item2> 61 | <Name Value="ECodetoolError"/> 62 | </Item2> 63 | <Item3> 64 | <Name Value="EFOpenError"/> 65 | </Item3> 66 | </Exceptions> 67 | </Debugging> 68 | </CONFIG> 69 | -------------------------------------------------------------------------------- /examples/noiseremoval/noiseremoval.lpr: -------------------------------------------------------------------------------- 1 | program noiseremoval; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | {$DEFINE USE_STEREO} 6 | 7 | uses 8 | {$IFDEF UNIX} 9 | cmem, 10 | cthreads, 11 | {$ENDIF} 12 | Classes, pa_noiseremoval, pa_pulse_simple, pa_dec_oggvorbis, pa_stream, pa_base, sysutils; 13 | 14 | const 15 | {$IFDEF USE_STEREO} 16 | NoiseSampleFile = 'noisestereo.ogg'; 17 | FileName = 'noisyaudiostereo.ogg'; 18 | {$ELSE} 19 | NoiseSampleFile = 'noise.ogg'; 20 | FileName = 'noisyaudio.ogg'; 21 | {$ENDIF} 22 | 23 | function GetNoiseStream(out ChannelCount: Integer): TMemoryStream; 24 | var 25 | StreamDest: TPAStreamDestination; 26 | ogg: TPAOggVorbisDecoderSource; 27 | 28 | begin 29 | ogg := TPAOggVorbisDecoderSource.Create; 30 | ogg.Stream := TFileStream.Create(NoiseSampleFile, fmOpenRead or fmShareDenyNone); 31 | 32 | Result := TMemoryStream.Create; 33 | StreamDest := TPAStreamDestination.Create(Result); 34 | StreamDest.DataSource := ogg; 35 | 36 | // first extract audiodata. 37 | ogg.StartData; 38 | //while ogg.Working do 39 | // sleep(0); 40 | sleep(1000); 41 | 42 | ChannelCount:=ogg.Channels; 43 | ogg.Stream.Free; 44 | ogg.Free; 45 | 46 | while StreamDest.Working do 47 | sleep(1); 48 | 49 | StreamDest.Free; 50 | 51 | Result.Position:=0; 52 | end; 53 | 54 | var 55 | RawNoiseStream: TMemoryStream; 56 | ogg: TPAOggVorbisDecoderSource; 57 | noise: TPANoiseRemovalLink; 58 | pulse: TPAPulseDestination; 59 | Channels: TChannelArray; 60 | ChanCount: Integer; 61 | i: Integer; 62 | begin 63 | // open ogg file to be cleaned 64 | ogg := TPAOggVorbisDecoderSource.Create; 65 | ogg.Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone); 66 | 67 | // create pulse destination to listen to audio 68 | pulse := TPAPulseDestination.Create; 69 | pulse.DataSource := ogg; 70 | 71 | // play unfiltered audio 72 | ogg.StartData; 73 | 74 | //wait to finish 75 | while pulse.Working do 76 | sleep(1); 77 | 78 | pulse.free; 79 | ogg.Free; 80 | // sleep(1000); 81 | ogg := TPAOggVorbisDecoderSource.Create; 82 | ogg.Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone); 83 | ogg.InitValues; // important! sets channels etc for when noise profile requests it 84 | 85 | // create noise filter 86 | noise := TPaNoiseRemovalLink.Create; 87 | noise.DataSource := ogg; 88 | 89 | pulse := TPAPulseDestination.Create; 90 | pulse.DataSource := noise; 91 | 92 | // set the noise profile 93 | RawNoiseStream := GetNoiseStream(ChanCount); 94 | 95 | Channels := SplitChannels(PSingle(RawNoiseStream.Memory), RawNoiseStream.Size div SizeOf(Single), ChanCount); 96 | RawNoiseStream.Free; 97 | for i := 0 to Length(Channels)-1 do 98 | noise.SetNoiseProfile(i, @Channels[i][0], Length(Channels[i])); 99 | 100 | // filter audio and play to pulse 101 | ogg.StartData; 102 | 103 | //wait to finish 104 | while ogg.Working or pulse.Working do 105 | sleep(1); 106 | 107 | pulse.DataSource := ogg; 108 | 109 | pulse.free; 110 | noise.free; 111 | ogg.Free; 112 | end. 113 | 114 | -------------------------------------------------------------------------------- /examples/noiseremoval/noisestereo.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewd207/PascalAudio/5a9a73576dfc9eb96451ee5c0b132ba77c7d8002/examples/noiseremoval/noisestereo.ogg -------------------------------------------------------------------------------- /examples/noiseremoval/noisyaudio.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewd207/PascalAudio/5a9a73576dfc9eb96451ee5c0b132ba77c7d8002/examples/noiseremoval/noisyaudio.ogg -------------------------------------------------------------------------------- /examples/noiseremoval/noisyaudiostereo.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewd207/PascalAudio/5a9a73576dfc9eb96451ee5c0b132ba77c7d8002/examples/noiseremoval/noisyaudiostereo.ogg -------------------------------------------------------------------------------- /examples/noiseremoval2/noiseremoval2.lpi: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <CONFIG> 3 | <ProjectOptions> 4 | <Version Value="9"/> 5 | <General> 6 | <Flags> 7 | <MainUnitHasCreateFormStatements Value="False"/> 8 | <MainUnitHasTitleStatement Value="False"/> 9 | </Flags> 10 | <SessionStorage Value="InProjectDir"/> 11 | <MainUnit Value="0"/> 12 | <Title Value="noiseremoval2"/> 13 | <UseAppBundle Value="False"/> 14 | <ResourceType Value="res"/> 15 | </General> 16 | <i18n> 17 | <EnableI18N LFM="False"/> 18 | </i18n> 19 | <VersionInfo> 20 | <StringTable ProductVersion=""/> 21 | </VersionInfo> 22 | <BuildModes Count="1"> 23 | <Item1 Name="Default" Default="True"/> 24 | </BuildModes> 25 | <PublishOptions> 26 | <Version Value="2"/> 27 | </PublishOptions> 28 | <RunParams> 29 | <local> 30 | <FormatVersion Value="1"/> 31 | </local> 32 | </RunParams> 33 | <RequiredPackages Count="2"> 34 | <Item1> 35 | <PackageName Value="PascalAudioSuite"/> 36 | </Item1> 37 | <Item2> 38 | <PackageName Value="PascalAudioIO"/> 39 | </Item2> 40 | </RequiredPackages> 41 | <Units Count="1"> 42 | <Unit0> 43 | <Filename Value="noiseremoval2.pas"/> 44 | <IsPartOfProject Value="True"/> 45 | </Unit0> 46 | </Units> 47 | </ProjectOptions> 48 | <CompilerOptions> 49 | <Version Value="11"/> 50 | <Target> 51 | <Filename Value="noiseremoval2"/> 52 | </Target> 53 | <SearchPaths> 54 | <IncludeFiles Value="$(ProjOutDir)"/> 55 | <OtherUnitFiles Value="/home/andrew/programming/groupprojects/uos/src/"/> 56 | <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> 57 | </SearchPaths> 58 | </CompilerOptions> 59 | <Debugging> 60 | <Exceptions Count="3"> 61 | <Item1> 62 | <Name Value="EAbort"/> 63 | </Item1> 64 | <Item2> 65 | <Name Value="ECodetoolError"/> 66 | </Item2> 67 | <Item3> 68 | <Name Value="EFOpenError"/> 69 | </Item3> 70 | </Exceptions> 71 | </Debugging> 72 | </CONFIG> 73 | -------------------------------------------------------------------------------- /examples/noiseremoval2/noiseremoval2.pas: -------------------------------------------------------------------------------- 1 | program noiseremoval2; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | uses 6 | {$IFDEF UNIX} cthreads, cmem,{$ENDIF} 7 | Classes, sysutils, noiseremovalmultichannel, 8 | pa_base, pa_dec_oggvorbis, pa_stream, pa_pulse_simple; 9 | 10 | const 11 | NoiseSampleFile = '../noiseremoval/noisestereo.ogg'; 12 | NoisyFile = '../noiseremoval/noisyaudiostereo.ogg'; 13 | 14 | function GetRawAudio(AFile: String): TMemoryStream; 15 | var 16 | StreamDest: TPAStreamDestination; 17 | ogg: TPAOggVorbisDecoderSource; 18 | begin 19 | // extract raw float32 data from 20 | ogg := TPAOggVorbisDecoderSource.Create; 21 | ogg.Stream := TFileStream.Create(AFile, fmOpenRead or fmShareDenyNone); 22 | Result := TMemoryStream.Create; 23 | StreamDest := TPAStreamDestination.Create(Result); 24 | StreamDest.DataSource := ogg; 25 | ogg.StartData; 26 | sleep(1000); 27 | ogg.Stream.Free; 28 | ogg.Free; 29 | while StreamDest.Working do 30 | sleep(1); 31 | StreamDest.Free; 32 | Result.Position:=0; 33 | end; 34 | 35 | procedure PlayAudio(AData: PSingle; ASamplesCount: Integer); 36 | var 37 | pulse: TPAPulseDestination; 38 | streamsource: TPAStreamSource; 39 | mem: TMemoryStream; 40 | begin 41 | Mem := TMemoryStream.Create; 42 | Mem.Write(AData^, ASamplesCount*SizeOf(Single)); 43 | Mem.Position:=0; 44 | 45 | streamsource:=TPAStreamSource.Create; 46 | streamsource.Stream := Mem; 47 | streamsource.Channels:=2; 48 | streamsource.Format:=afFloat32; 49 | streamsource.SamplesPerSecond:=44100; 50 | 51 | pulse := TPAPulseDestination.Create; 52 | pulse.DataSource := streamsource; 53 | 54 | streamsource.StartData; 55 | 56 | while pulse.Working do 57 | Sleep(1); 58 | 59 | streamsource.Free; 60 | pulse.Free; 61 | mem.Free; 62 | end; 63 | 64 | var 65 | RawNoise: TMemoryStream; 66 | RawAudio: TMemoryStream; 67 | FilteredAudio: PSingle; 68 | FilteredSamples: Integer; 69 | 70 | type 71 | 72 | { TFooNoiseRemoval } 73 | 74 | TFooNoiseRemoval = class(TNoiseRemovalMultiChannel) 75 | OutStream: TStream; 76 | procedure WriteData(ASender: TObject; AData: PSingle; ASampleCount: Integer) ; 77 | end; 78 | 79 | 80 | function FilterNoise(ANoiseSample, ANoisyAudio: TMemoryStream; out Samples: Integer): PSingle; 81 | var 82 | NoiseRemoval: TFooNoiseRemoval; 83 | begin 84 | Result := nil; 85 | NoiseRemoval := TFooNoiseRemoval.Create(2, 44100); 86 | NoiseRemoval.OutStream := TMemoryStream.Create; 87 | NoiseRemoval.WriteProc := @NoiseRemoval.WriteData; 88 | 89 | NoiseRemoval.ReadNoiseProfile(PSingle(ANoiseSample.Memory), ANoiseSample.Size div SizeOf(Single)); 90 | 91 | // while not out of data do 92 | // begin 93 | { we'll process the whole stream in one go.} 94 | NoiseRemoval.ProcessNoise(PSingle(ANoisyAudio.Memory), ANoisyAudio.Size div SizeOf(Single)); 95 | // end; 96 | NoiseRemoval.Flush; // output any remaining data 97 | 98 | Result:=GetMem(NoiseRemoval.OutStream.Size); 99 | Samples := NoiseRemoval.OutStream.Size div SizeOf(Single); 100 | NoiseRemoval.OutStream.Position:=0; 101 | NoiseRemoval.OutStream.Read(Result^, NoiseRemoval.OutStream.Size); 102 | NoiseRemoval.OutStream.Free; 103 | end; 104 | 105 | { TFooNoiseRemoval } 106 | 107 | procedure TFooNoiseRemoval.WriteData(ASender: TObject; AData: PSingle; 108 | ASampleCount: Integer); 109 | begin 110 | OutStream.Write(AData^, ASampleCount*SizeOf(Single)); 111 | end; 112 | 113 | begin 114 | RawNoise := GetRawAudio(NoiseSampleFile); 115 | RawAudio := GetRawAudio(NoisyFile); 116 | 117 | FilteredAudio:=FilterNoise(RawNoise, RawAudio, FilteredSamples); 118 | RawNoise.Free; 119 | RawAudio.Free; 120 | 121 | PlayAudio(FilteredAudio, FilteredSamples); 122 | Freemem(FilteredAudio); 123 | end. 124 | 125 | -------------------------------------------------------------------------------- /examples/playogg/playogg.lpi: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <CONFIG> 3 | <ProjectOptions> 4 | <Version Value="10"/> 5 | <General> 6 | <Flags> 7 | <MainUnitHasCreateFormStatements Value="False"/> 8 | <MainUnitHasTitleStatement Value="False"/> 9 | </Flags> 10 | <SessionStorage Value="InProjectDir"/> 11 | <MainUnit Value="0"/> 12 | <Title Value="playogg"/> 13 | <UseAppBundle Value="False"/> 14 | <ResourceType Value="res"/> 15 | </General> 16 | <i18n> 17 | <EnableI18N LFM="False"/> 18 | </i18n> 19 | <BuildModes Count="2"> 20 | <Item1 Name="Default" Default="True"/> 21 | <Item2 Name="Windowsx64"> 22 | <CompilerOptions> 23 | <Version Value="11"/> 24 | <Target> 25 | <Filename Value="playogg"/> 26 | </Target> 27 | <SearchPaths> 28 | <IncludeFiles Value="$(ProjOutDir)"/> 29 | <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> 30 | </SearchPaths> 31 | <CodeGeneration> 32 | <TargetCPU Value="x86_64"/> 33 | <TargetOS Value="win64"/> 34 | </CodeGeneration> 35 | </CompilerOptions> 36 | </Item2> 37 | </BuildModes> 38 | <PublishOptions> 39 | <Version Value="2"/> 40 | </PublishOptions> 41 | <RunParams> 42 | <local> 43 | <FormatVersion Value="1"/> 44 | <LaunchingApplication Use="True"/> 45 | </local> 46 | </RunParams> 47 | <RequiredPackages Count="1"> 48 | <Item1> 49 | <PackageName Value="PascalAudioSuite"/> 50 | </Item1> 51 | </RequiredPackages> 52 | <Units Count="1"> 53 | <Unit0> 54 | <Filename Value="playogg.lpr"/> 55 | <IsPartOfProject Value="True"/> 56 | </Unit0> 57 | </Units> 58 | </ProjectOptions> 59 | <CompilerOptions> 60 | <Version Value="11"/> 61 | <Target> 62 | <Filename Value="playogg"/> 63 | </Target> 64 | <SearchPaths> 65 | <IncludeFiles Value="$(ProjOutDir)"/> 66 | <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> 67 | </SearchPaths> 68 | </CompilerOptions> 69 | <Debugging> 70 | <Exceptions Count="3"> 71 | <Item1> 72 | <Name Value="EAbort"/> 73 | </Item1> 74 | <Item2> 75 | <Name Value="ECodetoolError"/> 76 | </Item2> 77 | <Item3> 78 | <Name Value="EFOpenError"/> 79 | </Item3> 80 | </Exceptions> 81 | </Debugging> 82 | </CONFIG> 83 | -------------------------------------------------------------------------------- /examples/playogg/playogg.lpr: -------------------------------------------------------------------------------- 1 | program playogg; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | uses 6 | {$IFDEF UNIX} 7 | cthreads, 8 | {$ENDIF} 9 | Classes, pa_dec_oggvorbis, 10 | pa_base, 11 | {$IFDEF UNIX} 12 | pa_pulse_simple, 13 | {$else} 14 | pa_mmdevice, 15 | {$ENDIF} 16 | sysutils; 17 | 18 | var 19 | ogg: TPAOggVorbisDecoderSource; 20 | Dest : TPAAudioDestination; 21 | FileName: String; 22 | begin 23 | FileName := ParamStr(1); 24 | if (FileName = '') or not FileExists(FileName) then 25 | begin 26 | WriteLn(' Usage: ',ExtractFileName(ParamStr(0)),' ''file.ogg'''); 27 | Exit; 28 | end; 29 | // create ogg decoder 30 | ogg := TPAOggVorbisDecoderSource.Create(TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone), True); 31 | 32 | // create audio out destination 33 | {$IFDEF UNIX} 34 | Dest := TPAPulseDestination.Create; 35 | {$else} 36 | Dest := TPAMMDestination.Create; 37 | {$ENDIF} 38 | 39 | // assign ogg as source of data 40 | Dest.DataSource := ogg; 41 | 42 | // start the chain from the first link. 43 | ogg.StartData; 44 | 45 | sleep(1000); 46 | //ogg.Position:=222; 47 | // while decoding sleep 48 | while Dest.Working do 49 | Sleep(1); 50 | Dest.Terminate; 51 | Dest.WaitFor; 52 | Dest.free; 53 | ogg.free; 54 | end. 55 | 56 | -------------------------------------------------------------------------------- /examples/playopus/playopus.lpi: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <CONFIG> 3 | <ProjectOptions> 4 | <Version Value="12"/> 5 | <General> 6 | <Flags> 7 | <MainUnitHasCreateFormStatements Value="False"/> 8 | <MainUnitHasTitleStatement Value="False"/> 9 | <MainUnitHasScaledStatement Value="False"/> 10 | </Flags> 11 | <SessionStorage Value="InProjectDir"/> 12 | <Title Value="playopus"/> 13 | <UseAppBundle Value="False"/> 14 | <ResourceType Value="res"/> 15 | </General> 16 | <BuildModes> 17 | <Item Name="Default" Default="True"/> 18 | </BuildModes> 19 | <PublishOptions> 20 | <Version Value="2"/> 21 | <UseFileFilters Value="True"/> 22 | </PublishOptions> 23 | <RunParams> 24 | <FormatVersion Value="2"/> 25 | </RunParams> 26 | <RequiredPackages> 27 | <Item> 28 | <PackageName Value="PascalAudioSuite"/> 29 | </Item> 30 | </RequiredPackages> 31 | <Units> 32 | <Unit> 33 | <Filename Value="playopus.lpr"/> 34 | <IsPartOfProject Value="True"/> 35 | </Unit> 36 | </Units> 37 | </ProjectOptions> 38 | <CompilerOptions> 39 | <Version Value="11"/> 40 | <Target> 41 | <Filename Value="playopus"/> 42 | </Target> 43 | <SearchPaths> 44 | <IncludeFiles Value="$(ProjOutDir)"/> 45 | <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> 46 | </SearchPaths> 47 | </CompilerOptions> 48 | <Debugging> 49 | <Exceptions> 50 | <Item> 51 | <Name Value="EAbort"/> 52 | </Item> 53 | <Item> 54 | <Name Value="ECodetoolError"/> 55 | </Item> 56 | <Item> 57 | <Name Value="EFOpenError"/> 58 | </Item> 59 | </Exceptions> 60 | </Debugging> 61 | </CONFIG> 62 | -------------------------------------------------------------------------------- /examples/playopus/playopus.lpr: -------------------------------------------------------------------------------- 1 | program playopus; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | uses 6 | {$IFDEF UNIX} 7 | cthreads, 8 | {$ENDIF} 9 | Classes, pa_ogg_opus, 10 | pa_base, 11 | {$IFDEF UNIX} 12 | pa_pulse_simple, 13 | {$else} 14 | pa_mmdevice, 15 | {$ENDIF} 16 | sysutils; 17 | 18 | var 19 | opus: TPAOggOpusDecoderSource; 20 | Dest : TPAAudioDestination; 21 | FileName: String; 22 | lPos: Integer; 23 | lMaxPos: Int64; 24 | begin 25 | FileName := ParamStr(1); 26 | if (FileName = '') or not FileExists(FileName) then 27 | begin 28 | WriteLn(' Usage: ',ExtractFileName(ParamStr(0)),' ''file.opus'''); 29 | Exit; 30 | end; 31 | // create opus decoder 32 | opus := TPAOggOpusDecoderSource.Create(TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone), True); 33 | 34 | // create audio out destination 35 | {$IFDEF UNIX} 36 | Dest := TPAPulseDestination.Create; 37 | {$else} 38 | Dest := TPAMMDestination.Create; 39 | {$ENDIF} 40 | 41 | // assign ogg/opus as source of data 42 | Dest.DataSource := opus; 43 | 44 | // start the chain from the first link. 45 | opus.StartData; 46 | 47 | sleep(1000); 48 | // while decoding sleep 49 | 50 | 51 | lMaxPos := Trunc(opus.MaxPosition); 52 | lPos := 0; 53 | 54 | opus.Position := 44; 55 | 56 | while Dest.Working do 57 | begin 58 | Sleep(100); 59 | if Trunc(opus.Position) <> lPos then 60 | begin 61 | 62 | lPos := Trunc(opus.Position); 63 | Write(#13); // output next on the same line.. 64 | Write(Format('%d:%.2d / %d:%.2d', [lPos div 60, lpos mod 60, lMaxPos div 60, lMaxPos mod 60])); 65 | 66 | 67 | 68 | //WriteLn(lPos,'/',lMaxPos); 69 | end; 70 | end; 71 | WriteLn; 72 | Dest.Terminate; 73 | Dest.WaitFor; 74 | Dest.free; 75 | opus.free; 76 | end. 77 | 78 | -------------------------------------------------------------------------------- /examples/smartplay/smartplay.lpi: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <CONFIG> 3 | <ProjectOptions> 4 | <Version Value="10"/> 5 | <General> 6 | <Flags> 7 | <MainUnitHasCreateFormStatements Value="False"/> 8 | <MainUnitHasTitleStatement Value="False"/> 9 | </Flags> 10 | <SessionStorage Value="InProjectDir"/> 11 | <MainUnit Value="0"/> 12 | <Title Value="smartplay"/> 13 | <UseAppBundle Value="False"/> 14 | <ResourceType Value="res"/> 15 | <XPManifest> 16 | <TextName Value="CompanyName.ProductName.AppName"/> 17 | <TextDesc Value="Your application description."/> 18 | </XPManifest> 19 | </General> 20 | <VersionInfo> 21 | <StringTable ProductVersion=""/> 22 | </VersionInfo> 23 | <BuildModes Count="1"> 24 | <Item1 Name="Default" Default="True"/> 25 | </BuildModes> 26 | <PublishOptions> 27 | <Version Value="2"/> 28 | </PublishOptions> 29 | <RunParams> 30 | <local> 31 | <FormatVersion Value="1"/> 32 | </local> 33 | </RunParams> 34 | <RequiredPackages Count="1"> 35 | <Item1> 36 | <PackageName Value="PascalAudioSuite"/> 37 | </Item1> 38 | </RequiredPackages> 39 | <Units Count="1"> 40 | <Unit0> 41 | <Filename Value="smartplay.lpr"/> 42 | <IsPartOfProject Value="True"/> 43 | </Unit0> 44 | </Units> 45 | </ProjectOptions> 46 | <CompilerOptions> 47 | <Version Value="11"/> 48 | <Target> 49 | <Filename Value="smartplay"/> 50 | </Target> 51 | <SearchPaths> 52 | <IncludeFiles Value="$(ProjOutDir)"/> 53 | <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> 54 | </SearchPaths> 55 | </CompilerOptions> 56 | <Debugging> 57 | <Exceptions Count="3"> 58 | <Item1> 59 | <Name Value="EAbort"/> 60 | </Item1> 61 | <Item2> 62 | <Name Value="ECodetoolError"/> 63 | </Item2> 64 | <Item3> 65 | <Name Value="EFOpenError"/> 66 | </Item3> 67 | </Exceptions> 68 | </Debugging> 69 | </CONFIG> 70 | -------------------------------------------------------------------------------- /examples/smartplay/smartplay.lpr: -------------------------------------------------------------------------------- 1 | program smartplay; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | uses 6 | {$IFDEF UNIX} 7 | cthreads, 8 | {$ENDIF} 9 | Classes, 10 | pa_base, 11 | pa_register, 12 | pa_stream, 13 | pa_pulse_simple, 14 | pa_flac, 15 | pa_dec_oggvorbis, 16 | pa_wav; 17 | 18 | var 19 | ReaderClass: TPAStreamSourceClass; 20 | Reader: TPAStreamSource; 21 | DevOutClass: TPAAudioDestinationClass; 22 | DevOut: TPAAudioDestination; 23 | 24 | begin 25 | ReaderClass := PARegisteredGetDecoderClass(ParamStr(1), False); 26 | DevOutClass := PARegisteredGetDeviceOut(''); // empty name just gets first device 27 | 28 | Reader := ReaderClass.Create(TFileStream.Create(ParamStr(1), fmOpenRead), True); 29 | DevOut := DevOutClass.Create; 30 | DevOut.DataSource := Reader; 31 | 32 | WriteLn('ReaderClassname = ', Reader.ClassName); 33 | WriteLn('DevOutClassname = ', DevOutClass.ClassName); 34 | 35 | Reader.StartData; 36 | 37 | while Reader.Working or DevOut.Working do 38 | CheckSynchronize; 39 | end. 40 | 41 | -------------------------------------------------------------------------------- /pascalaudioio/flac_callbacks.inc: -------------------------------------------------------------------------------- 1 | {%mainunit flac_classes.inc} 2 | 3 | {* libFLAC - Free Lossless Audio Codec library 4 | * Copyright (C) 2000-2009 Josh Coalson 5 | * Copyright (C) 2011-2013 Xiph.Org Foundation 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * - Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * - Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * - Neither the name of the Xiph.org Foundation nor the names of its 19 | * contributors may be used to endorse or promote products derived from 20 | * this software without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR 26 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 29 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 31 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | *} 34 | {$IFDEF FLAC_INTF_TYPES} 35 | type 36 | PFlacIOHandle = pointer; 37 | 38 | TFlacIOCallbackRead = function (ptr: Pointer; size, num_elems: csize_t; handle: PFlacIOHandle): csize_t; cdecl; 39 | TFlacIOCallbackWrite = function (ptr: Pointer; size, num_elems: csize_t; handle: PFlacIOHandle): csize_t; cdecl; 40 | TFlacIOCallbackSeek = function (handle: PFlacIOHandle; offset: Int64; whence: cint): cint; cdecl; 41 | TFlacIOCallbackTell = function (handle: PFlacIOHandle): Int64; cdecl; 42 | TFlacIOCallbackEof = function (handle: PFlacIOHandle): cint; cdecl; 43 | TFlacIOCallbackClose = TFlacIOCallbackEof; 44 | 45 | PFlacIOCallbacks = ^TFlacIOCallbacks; 46 | TFlacIOCallbacks = record 47 | read: TFlacIOCallbackRead; 48 | write: TFlacIOCallbackWrite; 49 | seek: TFlacIOCallbackSeek; 50 | tell: TFlacIOCallbackTell; 51 | eof: TFlacIOCallbackEof; 52 | close: TFlacIOCallbackClose; 53 | end; 54 | 55 | {$ENDIF} 56 | 57 | {$IFDEF FLAC_INTF} 58 | const 59 | SEEK_SET = 0; 60 | SEEK_CUR = 1; 61 | SEEK_END = 2; 62 | 63 | {$ENDIF} 64 | 65 | 66 | {$IFDEF FLAC_IMPL} 67 | 68 | 69 | 70 | {$ENDIF} 71 | 72 | 73 | -------------------------------------------------------------------------------- /pascalaudioio/flac_decode.inc: -------------------------------------------------------------------------------- 1 | {%mainuint flac_classes.pas} 2 | 3 | {* libFLAC - Free Lossless Audio Codec library 4 | * Copyright (C) 2000-2009 Josh Coalson 5 | * Copyright (C) 2011-2013 Xiph.Org Foundation 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * - Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * - Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * - Neither the name of the Xiph.org Foundation nor the names of its 19 | * contributors may be used to endorse or promote products derived from 20 | * this software without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR 26 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 29 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 31 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | *} 34 | {$IFDEF FLAC_INTF_TYPES} 35 | 36 | type 37 | TFlacStreamDecoderState = ( 38 | fsdsSearchForMetadata = 0, 39 | fsdsReadMetadata, 40 | fsdsSearchForFrameSync, 41 | fsdsReadFrame, 42 | fsdsEndOfStream, 43 | fsdsOggError, 44 | fsdsSeekError, 45 | fsdsDecoderAborted, 46 | fsdsMemoryAllocationError, 47 | fsdsUninitialized 48 | ); 49 | 50 | TFlacStreamDecoderInitStatus = ( 51 | fsdisOk, 52 | fsdisUnsupportedContainer, 53 | fsdisInvalidCallbacks, 54 | fsdisMemoryAllocationError, 55 | fsdisErrorOpeningFile, 56 | fsdisAlreadyInitialized 57 | ); 58 | 59 | TFlacStreamDecoderReadStatus = ( 60 | fsdrsContinue = 0, 61 | fsdrsEndOfStream, 62 | fsdrsReadAbort 63 | ); 64 | 65 | TFlacStreamDecoderSeekStatus = ( 66 | fsdssOk, 67 | fsdssError, 68 | fsdssUnsupported 69 | ); 70 | 71 | TFlacStreamDecoderTellStatus = ( 72 | fsdtsOK, 73 | fsdtsError, 74 | fsdtsUnsupported 75 | ); 76 | 77 | TFlacStreamDecoderLengthStatus = ( 78 | fsdlsOk, 79 | fsdlsError, 80 | fsdlsUnsupported 81 | ); 82 | 83 | TFlacStreamDecoderWriteStatus = ( 84 | fsdwsContinue, 85 | fsdwsAbort 86 | ); 87 | 88 | TFlacStreamDecoderErrorStatus = ( 89 | fsdesLostSync, 90 | fsdesBadHeader, 91 | fsdesFrameCrcMismatch, 92 | fsdesUnparseableStream 93 | ); 94 | {$ENDIF} 95 | 96 | {$IFDEF FLAC_INTF} 97 | type 98 | PFlacStreamDecoderStruct = ^TFlacStreamDecoderStruct; 99 | TFlacStreamDecoderStruct = record 100 | prot: pointer; 101 | priv: pointer; 102 | end; 103 | 104 | TFlacStreamDecoderReadCB = function (Decoder: PFlacStreamDecoderStruct; Buffer: PByte; bytes: pcsize_t; client_data: pointer): TFlacStreamDecoderReadStatus; cdecl; 105 | TFlacStreamDecoderSeekCB = function (Decoder: PFlacStreamDecoderStruct; abs_byte_offset: QWord; client_data: pointer): TFlacStreamDecoderSeekStatus; cdecl; 106 | TFlacStreamDecoderTellCB = function (Decoder: PFlacStreamDecoderStruct; abs_byte_offset: PQWord; client_data: pointer): TFlacStreamDecoderTellStatus; cdecl; 107 | TFlacStreamDecoderLengthCB = function (Decoder: PFlacStreamDecoderStruct; stream_length: PQWord; client_data: pointer): TFlacStreamDecoderLengthStatus; cdecl; 108 | TFlacStreamDecoderEofCB = function (Decoder: PFlacStreamDecoderStruct; client_data: pointer): TFlacBool; cdecl; 109 | TFlacStreamDecoderWriteCB = function (Decoder: PFlacStreamDecoderStruct; frame: PFlacFrame; buffer: PPLongInt; client_data: pointer): TFlacStreamDecoderWriteStatus; cdecl; 110 | TFlacStreamDecoderMetadataCB = procedure (Decoder: PFlacStreamDecoderStruct; metadata: PFlacStreamMetadataStruct; client_data: pointer); cdecl; 111 | TFlacStreamDecoderErrorCB = procedure (Decoder: PFlacStreamDecoderStruct; status: TFlacStreamDecoderErrorStatus; client_data: pointer); cdecl; 112 | 113 | function FLAC__stream_decoder_new: PFlacStreamDecoderStruct; cdecl; external; 114 | procedure FLAC__stream_decoder_delete(decoder: PFlacStreamDecoderStruct); cdecl; external; 115 | function FLAC__stream_decoder_set_ogg_serial_number(decoder: PFlacStreamDecoderStruct; serial_number: clong): TFlacBool; cdecl; external; 116 | function FLAC__stream_decoder_set_md5_checking(decoder: PFlacStreamDecoderStruct; AValue: TFlacBool): TFlacBool; cdecl; external; 117 | function FLAC__stream_decoder_set_metadata_respond(decoder: PFlacStreamDecoderStruct; AType: TFlacMetadataType): TFlacBool; cdecl; external; 118 | //function FLAC__stream_decoder_set_metadata_respond_application(decoder: PFlacStreamDecoderStruct; id: array[0..3] of Byte): TFlacBool; cdecl; external; 119 | function FLAC__stream_decoder_set_metadata_ignore(decoder: PFlacStreamDecoderStruct; AType: TFlacMetadataType): TFlacBool; cdecl; external; 120 | //function FLAC__stream_decoder_set_metadata_ignore_application(decoder: PFlacStreamDecoderStruct; id: array[0..3] of Byte): TFlacBool; cdecl; external; 121 | function FLAC__stream_decoder_set_metadata_ignore_all(decoder: PFlacStreamDecoderStruct): TFlacBool; cdecl; external; 122 | function FLAC__stream_decoder_get_state(decoder: PFlacStreamDecoderStruct): TFlacStreamDecoderState; cdecl; external; 123 | function FLAC__stream_decoder_get_resolved_state_string(decoder: PFlacStreamDecoderStruct): PChar; cdecl; external; 124 | function FLAC__stream_decoder_get_md5_checking(decoder: PFlacStreamDecoderStruct): TFlacBool; cdecl; external; 125 | function FLAC__stream_decoder_get_total_samples(decoder: PFlacStreamDecoderStruct): QWord; cdecl; external; 126 | function FLAC__stream_decoder_get_channels(decoder: PFlacStreamDecoderStruct): cunsigned; cdecl; external; 127 | function FLAC__stream_decoder_get_channel_assignment(decoder: PFlacStreamDecoderStruct): TFlacChannelAssignment; cdecl; external; 128 | function FLAC__stream_decoder_get_bits_per_sample(decoder: PFlacStreamDecoderStruct): cunsigned; cdecl; external; 129 | function FLAC__stream_decoder_get_sample_rate(decoder: PFlacStreamDecoderStruct): cunsigned; cdecl; external; 130 | function FLAC__stream_decoder_get_blocksize(decoder: PFlacStreamDecoderStruct): cunsigned; cdecl; external; 131 | function FLAC__stream_decoder_get_decode_position(decoder: PFlacStreamDecoderStruct; position: PQword): TFlacBool; cdecl; external; 132 | function FLAC__stream_decoder_init_stream( 133 | decoder: PFlacStreamDecoderStruct; 134 | read_cb: TFlacStreamDecoderReadCB; 135 | seek_cb: TFlacStreamDecoderSeekCB; 136 | tell_cb: TFlacStreamDecoderTellCB; 137 | length_cb: TFlacStreamDecoderLengthCB; 138 | eof_cb: TFlacStreamDecoderEofCB; 139 | write_cb: TFlacStreamDecoderWriteCB; 140 | metadata_cb: TFlacStreamDecoderMetadataCB; 141 | error_cb: TFlacStreamDecoderErrorCB; 142 | userdata: pointer 143 | ): TFlacStreamDecoderInitStatus; cdecl; external; 144 | 145 | function FLAC__stream_decoder_init_ogg_stream(decoder: PFlacStreamDecoderStruct; 146 | read_cb: TFlacStreamDecoderReadCB; 147 | seek_cb: TFlacStreamDecoderSeekCB; 148 | tell_cb: TFlacStreamDecoderTellCB; 149 | length_cb: TFlacStreamDecoderLengthCB; 150 | eof_cb: TFlacStreamDecoderEofCB; 151 | write_cb: TFlacStreamDecoderWriteCB; 152 | metadata_cb: TFlacStreamDecoderMetadataCB; 153 | error_cb: TFlacStreamDecoderErrorCB; 154 | userdata: pointer 155 | ): TFlacStreamDecoderInitStatus; cdecl; external; 156 | // skipped a bunch of variants of decoder_init_X. for FILE and filename 157 | 158 | function FLAC__stream_decoder_flush(decoder: PFlacStreamDecoderStruct): TFlacBool; cdecl; external; 159 | function FLAC__stream_decoder_reset(decoder: PFlacStreamDecoderStruct): TFlacBool; cdecl; external; 160 | function FLAC__stream_decoder_process_single(decoder: PFlacStreamDecoderStruct): TFlacBool; cdecl; external; 161 | function FLAC__stream_decoder_process_until_end_of_metadata(decoder: PFlacStreamDecoderStruct): TFlacBool; cdecl; external; 162 | function FLAC__stream_decoder_process_until_end_of_stream(decoder: PFlacStreamDecoderStruct): TFlacBool; cdecl; external; 163 | function FLAC__stream_decoder_skip_single_frame(decoder: PFlacStreamDecoderStruct): TFlacBool; cdecl; external; 164 | function FLAC__stream_decoder_seek_absolute(decoder: PFlacStreamDecoderStruct; sample: QWord): TFlacBool; cdecl external; 165 | 166 | 167 | 168 | 169 | {$ENDIF} 170 | 171 | {$IFDEF FLAC_IMPL} 172 | {$ENDIF} 173 | -------------------------------------------------------------------------------- /pascalaudioio/ladspa.pas: -------------------------------------------------------------------------------- 1 | {* ladspa.pas 2 | 3 | Linux Audio Developer's Simple Plugin API Version 1.1[LGPL]. 4 | Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis, 5 | Stefan Westerfeld. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public License 9 | as published by the Free Software Foundation; either version 2.1 of 10 | the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 20 | USA. *} 21 | unit ladspa; 22 | 23 | {$mode objfpc}{$H+} 24 | {$packrecords c} 25 | {$calling c} 26 | 27 | interface 28 | 29 | uses 30 | Classes, SysUtils, ctypes; 31 | 32 | type 33 | 34 | PLADSPA_Data = ^LADSPA_Data; 35 | LADSPA_Data = cfloat; 36 | 37 | LADSPA_Properties = cint; 38 | const 39 | LADSPA_PROPERTY_REALTIME = $1; 40 | LADSPA_PROPERTY_INPLACE_BROKEN = $2; 41 | LADSPA_PROPERTY_HARD_RT_CAPABLE = $4; 42 | 43 | //#define LADSPA_IS_REALTIME(x) ((x) & LADSPA_PROPERTY_REALTIME) 44 | //#define LADSPA_IS_INPLACE_BROKEN(x) ((x) & LADSPA_PROPERTY_INPLACE_BROKEN) 45 | //#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE) 46 | 47 | type 48 | PLADSPA_PortDescriptor = ^LADSPA_PortDescriptor; 49 | LADSPA_PortDescriptor = cint; 50 | const 51 | LADSPA_PORT_INPUT = $1; 52 | LADSPA_PORT_OUTPUT = $2; 53 | LADSPA_PORT_CONTROL = $4; 54 | LADSPA_PORT_AUDIO = $8; 55 | //#define LADSPA_IS_PORT_INPUT(x) ((x) & LADSPA_PORT_INPUT) 56 | //#define LADSPA_IS_PORT_OUTPUT(x) ((x) & LADSPA_PORT_OUTPUT) 57 | //#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL) 58 | //#define LADSPA_IS_PORT_AUDIO(x) ((x) & LADSPA_PORT_AUDIO) 59 | 60 | type 61 | LADSPA_PortRangeHintDescriptor = cint; 62 | const 63 | LADSPA_HINT_BOUNDED_BELOW = $1; 64 | LADSPA_HINT_BOUNDED_ABOVE = $2; 65 | LADSPA_HINT_TOGGLED = $4; 66 | LADSPA_HINT_SAMPLE_RATE = $8; 67 | LADSPA_HINT_LOGARITHMIC = $10; 68 | LADSPA_HINT_INTEGER = $20; 69 | LADSPA_HINT_DEFAULT_MASK = $3C0; 70 | LADSPA_HINT_DEFAULT_NONE = 0; 71 | LADSPA_HINT_DEFAULT_MINIMUM = $40; 72 | LADSPA_HINT_DEFAULT_LOW = $80; 73 | LADSPA_HINT_DEFAULT_MIDDLE = $C0; 74 | LADSPA_HINT_DEFAULT_HIGH = $100; 75 | LADSPA_HINT_DEFAULT_MAXIMUM = $140; 76 | LADSPA_HINT_DEFAULT_0 = $200; 77 | LADSPA_HINT_DEFAULT_1 = $240; 78 | LADSPA_HINT_DEFAULT_100 = $280; 79 | LADSPA_HINT_DEFAULT_440 = $2C0; 80 | { 81 | #define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW) 82 | #define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE) 83 | #define LADSPA_IS_HINT_TOGGLED(x) ((x) & LADSPA_HINT_TOGGLED) 84 | #define LADSPA_IS_HINT_SAMPLE_RATE(x) ((x) & LADSPA_HINT_SAMPLE_RATE) 85 | #define LADSPA_IS_HINT_LOGARITHMIC(x) ((x) & LADSPA_HINT_LOGARITHMIC) 86 | #define LADSPA_IS_HINT_INTEGER(x) ((x) & LADSPA_HINT_INTEGER) 87 | 88 | #define LADSPA_IS_HINT_HAS_DEFAULT(x) ((x) & LADSPA_HINT_DEFAULT_MASK) 89 | #define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ 90 | == LADSPA_HINT_DEFAULT_MINIMUM) 91 | #define LADSPA_IS_HINT_DEFAULT_LOW(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ 92 | == LADSPA_HINT_DEFAULT_LOW) 93 | #define LADSPA_IS_HINT_DEFAULT_MIDDLE(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ 94 | == LADSPA_HINT_DEFAULT_MIDDLE) 95 | #define LADSPA_IS_HINT_DEFAULT_HIGH(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ 96 | == LADSPA_HINT_DEFAULT_HIGH) 97 | #define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ 98 | == LADSPA_HINT_DEFAULT_MAXIMUM) 99 | #define LADSPA_IS_HINT_DEFAULT_0(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ 100 | == LADSPA_HINT_DEFAULT_0) 101 | #define LADSPA_IS_HINT_DEFAULT_1(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ 102 | == LADSPA_HINT_DEFAULT_1) 103 | #define LADSPA_IS_HINT_DEFAULT_100(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ 104 | == LADSPA_HINT_DEFAULT_100) 105 | #define LADSPA_IS_HINT_DEFAULT_440(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ 106 | == LADSPA_HINT_DEFAULT_440) 107 | } 108 | 109 | type 110 | PLADSPA_PortRangeHint = ^LADSPA_PortRangeHint; 111 | LADSPA_PortRangeHint = record 112 | HintDescriptor: LADSPA_PortRangeHintDescriptor; 113 | LowerBound: LADSPA_Data; 114 | UpperBound: LADSPA_Data; 115 | end; 116 | 117 | LADSPA_Handle = pointer; 118 | PLADSPA_Descriptor = ^LADSPA_Descriptor; 119 | LADSPA_Descriptor = record 120 | UniqueID: cunsigned; 121 | Label_: PChar; 122 | Properties: LADSPA_Properties; //count 123 | Name: PChar; 124 | Maker: PChar; 125 | Copyright: PChar; 126 | PortCount: cunsigned; 127 | PortDescriptors: PLADSPA_PortDescriptor; 128 | PortNames: PPChar; 129 | PortRangeHints: PLADSPA_PortRangeHint; 130 | ImplementationData: Pointer; 131 | instantiate: function (Descriptor: PLADSPA_Descriptor; SampleRate: cunsigned): LADSPA_Handle; 132 | connect_port: procedure (Instance: LADSPA_Handle; Port: cunsigned; DataLocation: PLADSPA_Data); 133 | activate: procedure (Instance: LADSPA_Handle); 134 | run: procedure (Instance: LADSPA_Handle; SampleCount: cunsigned); 135 | run_adding: procedure (Instance: LADSPA_Handle; SampleCount: cunsigned); 136 | set_run_adding_gain: procedure (Instance: LADSPA_Handle; Gain: LADSPA_Data); 137 | deactivate: procedure (Instance: LADSPA_Handle); 138 | cleanup: procedure (Instance: LADSPA_Handle); 139 | end; 140 | 141 | 142 | LADSPA_DescriptorFunction = function(Index : cunsigned) : PLADSPA_Descriptor; 143 | const 144 | LADSPA_DescriptorExport = 'ladspa_descriptor'; 145 | 146 | 147 | 148 | implementation 149 | 150 | end. 151 | 152 | -------------------------------------------------------------------------------- /pascalaudioio/mp4codec.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of the PascalAudio project. 3 | 4 | Copyright (c) 2020 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and license.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit mp4codec; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, quicktimeatoms; 22 | 23 | type 24 | TFourCC = quicktimeatoms.TAtomName; 25 | 26 | TMP4CodecClass = class of TMP4Codec; 27 | 28 | { TMP4Codec } 29 | 30 | TMP4Codec = class 31 | protected 32 | FAtom: TAtom; 33 | public 34 | procedure Filter(AData: PByte; ASize: Integer); virtual; 35 | constructor Create(AAtom: TAtom); virtual; 36 | property Atom: TAtom read FAtom; 37 | 38 | end; 39 | 40 | procedure MP4RegisterCodec(ACodec: TMP4CodecClass; AFourCC: TFourCC); 41 | function MP4LookupCodec(AFourCC: TFourCC; out ACodecClass: TMP4CodecClass): Boolean; 42 | 43 | operator := (const A: String): TFourCC; 44 | 45 | implementation 46 | uses 47 | fgl; 48 | 49 | type 50 | TMP4CodecMap = specialize TFPGMap<Cardinal{TFourCC}, TMP4CodecClass>; 51 | 52 | var 53 | gCodecList: TMP4CodecMap; 54 | 55 | procedure MP4RegisterCodec(ACodec: TMP4CodecClass; AFourCC: TFourCC); 56 | begin 57 | gCodecList.AddOrSetData(AFourCC, ACodec); 58 | end; 59 | 60 | function MP4LookupCodec(AFourCC: TFourCC; out ACodecClass: TMP4CodecClass): Boolean; 61 | var 62 | lIndex: Integer; 63 | begin 64 | Result := gCodecList.Find(AFourCC, lIndex); 65 | if Result then 66 | ACodecClass:=gCodecList.Data[lIndex]; 67 | end; 68 | 69 | // this already exists for TAtomName but this is duplicated so quicktimeatoms doesn't have to be used 70 | operator:=(const A: String): TFourCC; 71 | begin 72 | if Length(A) <> 4 then 73 | raise Exception.Create('FourCC codes are 4 chars long!'); 74 | Result.Chars := A; 75 | end; 76 | 77 | { TMP4Codec } 78 | 79 | procedure TMP4Codec.Filter(AData: PByte; ASize: Integer); 80 | begin 81 | // does nothing. 82 | end; 83 | 84 | constructor TMP4Codec.Create(AAtom: TAtom); 85 | begin 86 | FAtom := AAtom; 87 | end; 88 | 89 | initialization 90 | gCodecList := TMP4CodecMap.Create; 91 | gCodecList.Sorted:=True; 92 | 93 | finalization 94 | gCodecList.Free; 95 | 96 | end. 97 | 98 | -------------------------------------------------------------------------------- /pascalaudioio/mp4codec_mp4a.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of the PascalAudio project. 3 | 4 | Copyright (c) 2020 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and license.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit mp4codec_mp4a; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, mp4codec; 22 | 23 | type 24 | 25 | { Tmp4aCodec } 26 | 27 | Tmp4aCodec = class(TMP4Codec) 28 | procedure Filter(AData: PByte; ASize: Integer); override; 29 | 30 | end; 31 | 32 | implementation 33 | 34 | { Tmp4aCodec } 35 | 36 | procedure Tmp4aCodec.Filter(AData: PByte; ASize: Integer); 37 | begin 38 | // do nothing atm. maybe decode data here 39 | end; 40 | 41 | initialization 42 | MP4RegisterCodec(Tmp4aCodec, 'mp4a'); 43 | 44 | end. 45 | 46 | -------------------------------------------------------------------------------- /pascalaudioio/noiseremovalmultichannel.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of Pascal Audio IO package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the file COPYING.modifiedLGPL, included in this distribution, 7 | for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | 15 | unit noiseremovalmultichannel; 16 | 17 | {$mode objfpc}{$H+} 18 | 19 | interface 20 | 21 | uses 22 | Classes, SysUtils, paio_channelhelper, audacity_noiseremoval; 23 | 24 | type 25 | 26 | TNoiseWriteProc = audacity_noiseremoval.TNoiseWriteProc; 27 | 28 | { TNoiseRemovalChannel } 29 | 30 | TNoiseRemovalChannel = class(TNoiseRemoval, IPAIODataIOInterface) 31 | HasProfile: Boolean; 32 | ProfileComplete: Boolean; 33 | procedure WriteDataIO(ASender: IPAIODataIOInterface; AData: PSingle; ASamples: Integer); 34 | end; 35 | 36 | { TNoiseRemovalMultiChannel } 37 | 38 | TNoiseRemovalMultiChannel = class(IPAIODataIOInterface) 39 | private 40 | FChannels, 41 | FSampleRate: Integer; 42 | FHelper: TPAIOChannelHelper; 43 | FNoise: array of TNoiseRemovalChannel; 44 | FWriteProc: TNoiseWriteProc; 45 | //IPAIODataIOInterface 46 | procedure WriteDataIO(ASender: IPAIODataIOInterface; AData: PSingle; ASamples: Integer); 47 | procedure DataWrite(ASender: TObject; AData: PSingle; ASampleCount: Integer); 48 | public 49 | constructor Create(AChannels: Integer; ASampleRate: Integer); 50 | destructor Destroy; override; 51 | procedure ReadNoiseProfile(AData: PSingle; ASamples: Integer); 52 | procedure ProcessNoise(AData: PSingle; ASamples: Integer); 53 | procedure Flush; 54 | property WriteProc: TNoiseWriteProc read FWriteProc write FWriteProc; 55 | end; 56 | 57 | implementation 58 | 59 | { TMultiChannelNoiseRemoval } 60 | 61 | procedure TNoiseRemovalMultiChannel.WriteDataIO(ASender: IPAIODataIOInterface; AData: PSingle; ASamples: Integer); 62 | begin 63 | if Assigned(FWriteProc) then 64 | FWriteProc(Self, AData, ASamples); 65 | end; 66 | 67 | procedure TNoiseRemovalMultiChannel.DataWrite(ASender: TObject; AData: PSingle; ASampleCount: Integer); 68 | begin 69 | (FHelper as IPAIODataIOInterface).WriteDataIO(ASender as IPAIODataIOInterface, AData, ASampleCount); 70 | end; 71 | 72 | constructor TNoiseRemovalMultiChannel.Create(AChannels: Integer; 73 | ASampleRate: Integer); 74 | var 75 | i: Integer; 76 | begin 77 | FChannels:=AChannels; 78 | FSampleRate:=ASampleRate; 79 | FHelper := TPAIOChannelHelper.Create(Self); 80 | SetLength(FNoise, AChannels); 81 | for i := 0 to High(FNoise) do 82 | begin 83 | FNoise[i] := TNoiseRemovalChannel.Create; 84 | FNoise[i].WriteProc:=@DataWrite; 85 | FNoise[i].Init(ASampleRate); 86 | FHelper.Outputs.Add(FNoise[i] as IPAIODataIOInterface); 87 | end; 88 | end; 89 | 90 | destructor TNoiseRemovalMultiChannel.Destroy; 91 | var 92 | i: Integer; 93 | begin 94 | for i := 0 to High(FNoise) do 95 | begin 96 | FNoise[i].Free; 97 | end; 98 | SetLength(FNoise, 0); 99 | FHelper.Free; 100 | end; 101 | 102 | procedure TNoiseRemovalMultiChannel.ReadNoiseProfile(AData: PSingle; 103 | ASamples: Integer); 104 | var 105 | i: Integer; 106 | begin 107 | FHelper.Write(AData, ASamples); 108 | for i := 0 to High(FNoise) do 109 | begin 110 | FNoise[i].ProfileComplete:=True; 111 | FNoise[i].Process(nil, 0, True, False); 112 | FNoise[i].HasProfile:=True; 113 | FNoise[i].Init(FSampleRate); 114 | end; 115 | end; 116 | 117 | procedure TNoiseRemovalMultiChannel.ProcessNoise(AData: PSingle; 118 | ASamples: Integer); 119 | begin 120 | FHelper.Write(AData, ASamples); 121 | end; 122 | 123 | procedure TNoiseRemovalMultiChannel.Flush; 124 | var 125 | i: Integer; 126 | begin 127 | for i := 0 to High(FNoise) do 128 | FNoise[i].Flush; 129 | end; 130 | 131 | procedure TNoiseRemovalChannel.WriteDataIO(ASender: IPAIODataIOInterface; 132 | AData: PSingle; ASamples: Integer); 133 | begin 134 | Process(AData, ASamples, not HasProfile, not HasProfile); 135 | end; 136 | 137 | 138 | end. 139 | 140 | 141 | -------------------------------------------------------------------------------- /pascalaudioio/pa_ringbuffer.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of the PascalAudio project. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and license.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_ringbuffer; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils; 22 | 23 | type 24 | 25 | { TRingBuffer } 26 | 27 | TRingBuffer = class 28 | private 29 | FMem: PByte; 30 | FWritePos: Integer; 31 | FReadPos: Integer; 32 | FUsedSpace: Integer; 33 | FTotalSpace: Integer; 34 | function GetFreeSpace: Integer; 35 | public 36 | constructor Create(ASize: Integer); 37 | destructor Destroy; override; 38 | function Write(const ASource; ASize: Integer): Integer; 39 | function Read(var ADest; ASize: Integer): Integer; 40 | property FreeSpace: Integer read GetFreeSpace; 41 | property UsedSpace: Integer read FUsedSpace; 42 | 43 | end; 44 | 45 | implementation 46 | 47 | { TRingBuffer } 48 | 49 | function TRingBuffer.GetFreeSpace: Integer; 50 | begin 51 | Result := FTotalSpace-FUsedSpace; 52 | end; 53 | 54 | constructor TRingBuffer.Create(ASize: Integer); 55 | begin 56 | FMem:=Getmem(ASize); 57 | FTotalSpace:=ASize; 58 | end; 59 | 60 | destructor TRingBuffer.Destroy; 61 | begin 62 | Freemem(FMem); 63 | inherited Destroy; 64 | end; 65 | 66 | function Min(A,B: Integer): Integer; 67 | begin 68 | if A < B then Exit(A); 69 | Result := B; 70 | end; 71 | 72 | function TRingBuffer.Write(const ASource; ASize: Integer): Integer; 73 | var 74 | EOB: Integer; // end of buffer 75 | WSize: Integer; 76 | WTotal: Integer = 0; 77 | begin 78 | if FUsedSpace = 0 then 79 | begin 80 | // give the best chance of not splitting the data at buffer end. 81 | FWritePos:=0; 82 | FReadPos:=0; 83 | end; 84 | if ASize > FreeSpace then 85 | raise Exception.Create('Ring buffer overflow'); 86 | Result := ASize; 87 | Inc(FUsedSpace, ASize); 88 | while ASize > 0 do 89 | begin 90 | EOB := FTotalSpace - FWritePos; 91 | WSize := Min(ASize, EOB); 92 | Move(PByte(@ASource)[WTotal], FMem[FWritePos], WSize); 93 | Inc(FWritePos, WSize); 94 | Dec(ASize, WSize); 95 | 96 | if FWritePos >= FTotalSpace then 97 | FWritePos:= 0; 98 | end; 99 | end; 100 | 101 | function TRingBuffer.Read(var ADest; ASize: Integer): Integer; 102 | var 103 | EOB: Integer; // end of buffer 104 | RSize: Integer; 105 | RTotal: Integer = 0; 106 | begin 107 | if ASize > UsedSpace then 108 | raise Exception.Create('Ring buffer underflow'); 109 | ASize := Min(ASize, UsedSpace); 110 | Result := ASize; 111 | 112 | Dec(FUsedSpace, ASize); 113 | while ASize > 0 do 114 | begin 115 | EOB := FTotalSpace - FReadPos; 116 | RSize := Min(EOB, ASize); 117 | Move(FMem[FReadPos], PByte(@ADest)[RTotal],RSize); 118 | Dec(ASize, RSize); 119 | Inc(FReadPos, RSize); 120 | if FReadPos >= FTotalSpace then 121 | FReadPos:=0; 122 | end; 123 | end; 124 | 125 | end. 126 | 127 | -------------------------------------------------------------------------------- /pascalaudioio/paio_channelhelper.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of Pascal Audio IO package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the file COPYING.modifiedLGPL, included in this distribution, 7 | for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit paio_channelhelper; 15 | 16 | {$mode objfpc}{$H+} 17 | {$interfaces corba} 18 | 19 | interface 20 | 21 | uses 22 | Classes, SysUtils, paio_types, pa_ringbuffer; 23 | 24 | type 25 | IPAIODataIOInterface = interface 26 | ['IPAIODataIOInterface'] 27 | procedure WriteDataIO(ASender: IPAIODataIOInterface; AData: PSingle; ASamples: Integer); 28 | end; 29 | 30 | { TPAIOChannelHelper } 31 | 32 | TPAIOChannelHelper = class(IPAIODataIOInterface) 33 | private 34 | FOutputs: TList; 35 | FTarget: IPAIODataIOInterface; // where we will send plexed data. 36 | FBuffers: TChannelArray; 37 | FPos: array of Integer; 38 | // called by the individual channel objects. 39 | procedure WriteDataIO(ASender: IPAIODataIOInterface; AData: PSingle; ASamples: Integer); 40 | procedure AllocateBuffers; 41 | procedure SendDataToTarget; 42 | public 43 | constructor Create(APlexedTarget: IPAIODataIOInterface); 44 | destructor Destroy; override; 45 | property Outputs: TList read FOutputs;// of IPAIOSplitterJoinerInterface. Each is a channel in order. 46 | procedure Write(AData: PSingle; ASamples: Integer); // this expects interleaved data. 47 | end; 48 | 49 | 50 | implementation 51 | uses 52 | paio_utils; 53 | 54 | { TPAIOChannelHelper } 55 | 56 | procedure TPAIOChannelHelper.WriteDataIO(ASender: IPAIODataIOInterface; AData: PSingle; ASamples: Integer); 57 | var 58 | BufIndex: Integer; 59 | BufSize, WCount: Integer; 60 | Written: Integer = 0; 61 | begin 62 | BufIndex := FOutputs.IndexOf(Pointer(ASender)); 63 | 64 | if BufIndex = -1 then 65 | raise Exception.Create('Trying to write data from an unknown instance'); 66 | 67 | AllocateBuffers; 68 | 69 | BufSize := Length(FBuffers[0]); 70 | 71 | While ASamples > 0 do 72 | begin 73 | WCount := Min(BufSize-FPos[BufIndex], ASamples); 74 | Move(AData[Written], FBuffers[BufIndex][0], WCount*SizeOf(Single)); 75 | Inc(Written, WCount); 76 | Dec(ASamples, WCount); 77 | Inc(FPos[BufIndex], WCount); 78 | 79 | if BufIndex = High(FBuffers) then 80 | SendDataToTarget; 81 | end; 82 | end; 83 | 84 | procedure TPAIOChannelHelper.AllocateBuffers; 85 | begin 86 | if Length(FBuffers) <> FOutputs.Count then 87 | begin 88 | SetLength(FBuffers, 0); 89 | FBuffers := NewChannelArray(FOutputs.Count, AUDIO_BUFFER_SIZE*2); 90 | SetLength(FPos, FOutputs.Count); 91 | end; 92 | end; 93 | 94 | procedure TPAIOChannelHelper.SendDataToTarget; 95 | var 96 | Plexed: TSingleArray; 97 | HighestCount: Integer = 0; 98 | i: Integer; 99 | begin 100 | for i := 0 to High(FPos) do 101 | if FPos[i] > HighestCount then 102 | HighestCount:=FPos[i]; 103 | Plexed := JoinChannels(FBuffers, HighestCount); 104 | 105 | FTarget.WriteDataIO(Self, @Plexed[0], Length(Plexed)); 106 | 107 | for i := 0 to High(FPos) do 108 | Dec(FPos[i], HighestCount); 109 | end; 110 | 111 | constructor TPAIOChannelHelper.Create(APlexedTarget: IPAIODataIOInterface); 112 | begin 113 | FOutputs := TList.Create; 114 | FTarget := APlexedTarget; 115 | end; 116 | 117 | destructor TPAIOChannelHelper.Destroy; 118 | begin 119 | FOutputs.Free; 120 | inherited Destroy; 121 | end; 122 | 123 | procedure TPAIOChannelHelper.Write(AData: PSingle; ASamples: Integer); 124 | var 125 | Channels: TChannelArray; 126 | i: Integer; 127 | Pos: Integer = 0; 128 | WCount: Integer; 129 | begin 130 | AllocateBuffers; 131 | Channels := SplitChannels(AData, ASamples, Outputs.Count); 132 | while ASamples > 0 do 133 | begin 134 | WCount := Min(1024, ASamples div Outputs.Count); 135 | for i := 0 to Outputs.Count-1 do 136 | begin 137 | IPAIODataIOInterface(Outputs.Items[i]).WriteDataIO(Self, @Channels[i][Pos], WCount); 138 | end; 139 | Dec(ASamples, WCount * Outputs.Count); 140 | Inc(Pos, WCount); 141 | end; 142 | end; 143 | 144 | end. 145 | 146 | -------------------------------------------------------------------------------- /pascalaudioio/paio_messagequeue.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of Pascal Audio IO package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | 15 | { 16 | This implements a threadsafe message queue using a critical section and a 17 | TSimpleEvent. It's pretty generic and could be used in most anything. The 18 | messages are class based so data in a message can be freed easily. 19 | } 20 | 21 | unit paio_messagequeue; 22 | 23 | {$mode objfpc}{$H+} 24 | 25 | interface 26 | 27 | uses 28 | Classes, SysUtils, syncobjs; 29 | 30 | type 31 | TPAIOMessage = class 32 | private 33 | FNext: TPAIOMessage; 34 | FMsg: Integer; 35 | FSimpleData: Variant; 36 | public 37 | constructor Create(AMsg: Integer); 38 | property Message: Integer read FMsg; 39 | property Data: Variant read FSimpleData write FSimpleData; 40 | end; 41 | 42 | 43 | // A threadsafe message queue 44 | TPAIOMessageQueue = class 45 | private 46 | FLock: TRTLCriticalSection; 47 | FFirst: TPAIOMessage; 48 | FLast: TPAIOMessage; 49 | FHasMessage: TSimpleEvent; 50 | procedure FreeMessages; 51 | public 52 | constructor Create; 53 | destructor Destroy; override; 54 | procedure PostMessage(AMessage: TPAIOMessage); // append message to end of the queue 55 | procedure PostMessage(AMsg: Integer; AData: Variant); 56 | procedure PostMessage(AMsg: Integer); 57 | function PopMessage: TPAIOMessage; // remove message from start of the queue 58 | function HasMessage: Boolean; 59 | procedure InsertMessage(AMsg: Integer); // inserts message to the start of the queue to be used in the queue 60 | procedure InsertMessage(AMsg: TPAIOMessage); // inserts message to the start of the queue to be used in the queue 61 | procedure InsertBefore(AMessages: array of Integer; AMsgObject: TPAIOMessage); // message will be inserted before messages of [types] 62 | function WaitMessage(ATimeout: Integer; var AMsg: TPAIOMessage): TWaitResult; 63 | function WaitMessage(ATimeout: Integer): TWaitResult; 64 | end; 65 | 66 | implementation 67 | 68 | { TPAIOMessage } 69 | 70 | constructor TPAIOMessage.Create(AMsg: Integer); 71 | begin 72 | FMsg:=AMsg; 73 | end; 74 | 75 | { TPAIOMessageQueue } 76 | 77 | procedure TPAIOMessageQueue.FreeMessages; 78 | var 79 | Tmp: TPAIOMessage; 80 | begin 81 | EnterCriticalsection(FLock); 82 | try 83 | while Assigned(FFirst) do 84 | begin 85 | Tmp := FFirst; 86 | FFirst := FFirst.FNext; 87 | Tmp.Free; 88 | end; 89 | finally 90 | LeaveCriticalsection(FLock); 91 | end; 92 | end; 93 | 94 | constructor TPAIOMessageQueue.Create; 95 | begin 96 | InitCriticalSection(FLock); 97 | FHasMessage := TSimpleEvent.Create; 98 | end; 99 | 100 | destructor TPAIOMessageQueue.Destroy; 101 | begin 102 | FreeMessages; 103 | FHasMessage.Free; 104 | DoneCriticalsection(FLock); 105 | inherited Destroy; 106 | end; 107 | 108 | procedure TPAIOMessageQueue.PostMessage(AMessage: TPAIOMessage); 109 | begin 110 | EnterCriticalsection(FLock); 111 | try 112 | if not Assigned(FLast) then 113 | begin 114 | FFirst := AMessage; 115 | FLast := AMessage; 116 | end 117 | else 118 | begin 119 | FLast.FNext := AMessage; 120 | FLast := AMessage; 121 | end; 122 | 123 | finally 124 | FHasMessage.SetEvent; 125 | LeaveCriticalsection(FLock); 126 | end; 127 | end; 128 | 129 | procedure TPAIOMessageQueue.PostMessage(AMsg: Integer; AData: Variant); 130 | var 131 | Msg: TPAIOMessage; 132 | begin 133 | Msg := TPAIOMessage.Create(AMsg); 134 | MSg.Data:=AData; 135 | PostMessage(Msg); 136 | end; 137 | 138 | procedure TPAIOMessageQueue.PostMessage(AMsg: Integer); 139 | var 140 | Msg: TPAIOMessage; 141 | begin 142 | Msg := TPAIOMessage.Create(AMsg); 143 | PostMessage(Msg); 144 | end; 145 | 146 | 147 | function TPAIOMessageQueue.PopMessage: TPAIOMessage; 148 | begin 149 | Result := nil; 150 | EnterCriticalsection(FLock); 151 | try 152 | if Assigned(FFirst) then 153 | begin 154 | Result := FFirst; 155 | FFirst := FFirst.FNext; 156 | Result.FNext := nil; 157 | if FFirst = nil then 158 | begin 159 | FLast := nil; 160 | FHasMessage.ResetEvent; 161 | end; 162 | end; 163 | finally 164 | LeaveCriticalsection(FLock); 165 | end; 166 | end; 167 | 168 | function TPAIOMessageQueue.HasMessage: Boolean; 169 | begin 170 | Result := FFirst <> nil; 171 | end; 172 | 173 | procedure TPAIOMessageQueue.InsertMessage(AMsg: Integer); 174 | begin 175 | InsertMessage(TPAIOMessage.Create(AMsg)); 176 | end; 177 | 178 | procedure TPAIOMessageQueue.InsertMessage(AMsg: TPAIOMessage); 179 | begin 180 | EnterCriticalsection(FLock); 181 | try 182 | if not Assigned(FFirst) then 183 | begin 184 | FFirst := AMsg; 185 | FLast := AMsg; 186 | end 187 | else 188 | begin 189 | AMsg.FNext := FFirst; 190 | FFirst := AMsg; 191 | end; 192 | finally 193 | FHasMessage.SetEvent; 194 | LeaveCriticalsection(FLock); 195 | end; 196 | end; 197 | 198 | operator in (A: Integer; L: Array of Integer): Boolean; 199 | var 200 | i: Integer; 201 | begin 202 | Result := False; 203 | for i in L do 204 | if i = A then 205 | Exit(True); 206 | 207 | end; 208 | 209 | procedure TPAIOMessageQueue.InsertBefore(AMessages: array of Integer; AMsgObject: TPAIOMessage); 210 | var 211 | Prev: TPAIOMessage = nil; 212 | Current: TPAIOMessage; 213 | begin 214 | if not Assigned(FFirst) then 215 | begin 216 | PostMessage(AMsgObject); 217 | Exit; 218 | end; 219 | EnterCriticalsection(FLock); 220 | try 221 | Current := FFirst; 222 | repeat 223 | if Current.Message in AMessages then 224 | begin 225 | AMsgObject.FNext := Current; 226 | if Assigned(Prev) then 227 | Prev.FNext := AMsgObject 228 | else 229 | FFirst := AMsgObject; 230 | Exit; 231 | end; 232 | Prev := Current; 233 | Current := Current.FNext; 234 | until Current = nil; 235 | if Assigned(FLast) then 236 | FLast.FNext := AMsgObject 237 | else 238 | begin 239 | FFirst := AMsgObject; 240 | FLast := AMsgObject; 241 | end; 242 | finally 243 | LeaveCriticalsection(FLock); 244 | end; 245 | end; 246 | 247 | function TPAIOMessageQueue.WaitMessage(ATimeout: Integer; var AMsg: TPAIOMessage): TWaitResult; 248 | begin 249 | Result := FHasMessage.WaitFor(ATimeout); 250 | if Result = wrSignaled then 251 | AMsg := PopMessage 252 | else 253 | AMsg := nil; 254 | end; 255 | 256 | function TPAIOMessageQueue.WaitMessage(ATimeout: Integer): TWaitResult; 257 | begin 258 | Result := FHasMessage.WaitFor(ATimeout); 259 | end; 260 | 261 | end. 262 | 263 | -------------------------------------------------------------------------------- /pascalaudioio/paio_ogg_container.pas: -------------------------------------------------------------------------------- 1 | unit paio_ogg_container; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils; 9 | 10 | type 11 | 12 | 13 | { TOggPageHeader } 14 | 15 | POggPageHeader = ^TOggPageHeader; 16 | TOggPageHeader = object 17 | private 18 | function GetisFirstOfLogicalBitstream: Boolean; 19 | function GetisFreshPacket: Boolean; 20 | function GetisLastPageOfLogicalBitstream: Boolean; 21 | public 22 | Signature: array[0..3] of Char; // OggS 23 | Version: Byte; 24 | Flags: Byte; 25 | Position: Int64; // The position after the page has been processed. The page end sample 26 | Serial: DWord; 27 | PageIndex: DWord; 28 | Checksum: DWord; 29 | SegmentsCount: Byte; 30 | SegmentSize: array of Byte; // dynamic. max 255 31 | // The Following are not stored in the header but are determined when the header is read. 32 | _PacketCount: Integer; 33 | _DataSize: Integer; 34 | _LastPacketNeedsNextPage: Boolean; 35 | constructor ReadFromStream(AStream: TStream; AFillOtherData: Boolean); 36 | property isFreshPacket: Boolean read GetisFreshPacket; 37 | property isFirstPageOfLogicalBitstream: Boolean read GetisFirstOfLogicalBitstream; 38 | property isLastPageOfLogicalBitstream: Boolean read GetisLastPageOfLogicalBitstream; 39 | function HeaderSize: Integer; 40 | function SegmentDataSize(out PacketCount: Integer): Integer; // total size of all segment data 41 | function SegmentDataSize: Integer; 42 | function Size: Integer; // header + data size 43 | end; 44 | 45 | 46 | 47 | implementation 48 | 49 | { TOggPageHeader } 50 | 51 | function TOggPageHeader.GetisFirstOfLogicalBitstream: Boolean; 52 | begin 53 | Result := Flags and 2 = 2; 54 | end; 55 | 56 | function TOggPageHeader.GetisFreshPacket: Boolean; 57 | begin 58 | Result := Flags and 1 = 0; 59 | end; 60 | 61 | function TOggPageHeader.GetisLastPageOfLogicalBitstream: Boolean; 62 | begin 63 | Result := Flags and 4 = 4; 64 | end; 65 | 66 | constructor TOggPageHeader.ReadFromStream(AStream: TStream; 67 | AFillOtherData: Boolean); 68 | var 69 | i: Byte; 70 | begin 71 | AStream.Read(Signature, 4); 72 | Version :=AStream.ReadByte; 73 | Flags := AStream.ReadByte; 74 | Position:=LEtoN(AStream.ReadQWord); 75 | Serial:=LEtoN(AStream.ReadDWord); 76 | PageIndex:=LEtoN(AStream.ReadDWord); 77 | Checksum:=LEtoN(AStream.ReadDWord); 78 | SegmentsCount := AStream.ReadByte; 79 | SetLength(SegmentSize, SegmentsCount); 80 | AStream.Read(SegmentSize[0], SegmentsCount); 81 | 82 | _PacketCount := 0; 83 | _DataSize := 0; 84 | _LastPacketNeedsNextPage := False; 85 | 86 | if AFillOtherData then 87 | begin 88 | _LastPacketNeedsNextPage:= ((SegmentsCount > 0) and (SegmentSize[SegmentsCount-1] = 255)); 89 | for i := 0 to SegmentsCount-1 do 90 | begin 91 | _DataSize+=SegmentSize[i]; 92 | if SegmentSize[i] < 255 then 93 | Inc(_PacketCount); 94 | end; 95 | // packets are not otherwise counted if they don't have a value < 255. 96 | if _LastPacketNeedsNextPage then 97 | Inc(_PacketCount); 98 | end; 99 | end; 100 | 101 | function TOggPageHeader.HeaderSize: Integer; 102 | begin 103 | Result := 26 + (Length(SegmentSize)); 104 | end; 105 | 106 | function TOggPageHeader.SegmentDataSize(out PacketCount: Integer): Integer; 107 | var 108 | i: Integer; 109 | begin 110 | Result := 0; 111 | PacketCount:=0; 112 | 113 | for i := 0 to SegmentsCount-1 do 114 | begin 115 | Result += SegmentSize[i]; 116 | if SegmentSize[i] < 255 then 117 | Inc(PacketCount); 118 | end; 119 | end; 120 | 121 | function TOggPageHeader.SegmentDataSize: Integer; 122 | var 123 | lDummy: Integer; 124 | begin 125 | Result := SegmentDataSize(lDummy); 126 | end; 127 | 128 | function TOggPageHeader.Size: Integer; 129 | var 130 | lDummy: Integer; 131 | begin 132 | Result := HeaderSize + SegmentDataSize(lDummy); 133 | end; 134 | 135 | end. 136 | 137 | -------------------------------------------------------------------------------- /pascalaudioio/paio_types.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of Pascal Audio IO package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the file COPYING.modifiedLGPL, included in this distribution, 7 | for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit paio_types; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils; 22 | 23 | const 24 | AUDIO_BUFFER_SIZE = 8192; 25 | AUDIO_BUFFER_FLOAT_SAMPLES = AUDIO_BUFFER_SIZE div 4; 26 | 27 | type 28 | PPSingle = ^PSingle; 29 | TSingleArray = array of Single; 30 | TChannelArray = array of TSingleArray; 31 | 32 | implementation 33 | 34 | end. 35 | 36 | -------------------------------------------------------------------------------- /pascalaudioio/paio_utils.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of Pascal Audio IO package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the file COPYING.modifiedLGPL, included in this distribution, 7 | for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit paio_utils; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, paio_types; 22 | 23 | 24 | 25 | function NewChannelArray(AChannels: Integer; ASamplesPerChannel: Integer): TChannelArray; 26 | function SplitChannels(AData: PSingle; ASamples: Integer; AChannels: Integer): TChannelArray; 27 | function JoinChannels(AChannelData: TChannelArray; ASamples: Integer = -1): TSingleArray; 28 | function JoinChannels(AChannelData: PPSingle; AChannels: Integer; ASamples: Integer): TSingleArray; 29 | 30 | function Min(A,B: Integer): Integer; 31 | function Max(A,B: Integer): Integer; 32 | 33 | implementation 34 | 35 | function Min(A,B: Integer): Integer; 36 | begin 37 | if A < B then Exit(A); 38 | Result := B; 39 | end; 40 | 41 | function Max(A,B: Integer): Integer; 42 | begin 43 | if A > B then Exit(A); 44 | Result := B; 45 | end; 46 | 47 | function NewChannelArray(AChannels: Integer; ASamplesPerChannel: Integer): TChannelArray; 48 | var 49 | i: Integer; 50 | begin 51 | SetLength(Result, AChannels); 52 | for i := 0 to AChannels-1 do 53 | SetLength(Result[i], ASamplesPerChannel); 54 | end; 55 | 56 | // Samples is total samples not samples per channel. 57 | // So Samples = 1000 if 2 Channels have 500 each 58 | function SplitChannels(AData: PSingle; ASamples: Integer; AChannels: Integer): TChannelArray; 59 | var 60 | SamplesPerChannel: Integer; 61 | i, j: Integer; 62 | begin 63 | SamplesPerChannel:=ASamples div AChannels; 64 | //SetLength(Result, AChannels); 65 | Result := NewChannelArray(AChannels, SamplesPerChannel); 66 | for i := 0 to AChannels-1 do 67 | begin 68 | //SetLength(Result[i], SamplesPerChannel); 69 | for j := 0 to SamplesPerChannel-1 do 70 | begin 71 | Result[i][j] := AData[j*AChannels+i]; 72 | end; 73 | end; 74 | end; 75 | 76 | function JoinChannels(AChannelData: TChannelArray; ASamples: Integer): TSingleArray; 77 | var 78 | i: Integer; 79 | j: Integer; 80 | Samples: Integer; 81 | begin 82 | if Length(AChannelData) > 0 then 83 | begin 84 | if ASamples <> -1 then 85 | Samples := ASamples 86 | else 87 | Samples := Length(AChannelData[0]); 88 | 89 | SetLength(Result, Length(AChannelData) * Samples); 90 | for i := 0 to High(AChannelData) do 91 | for j := 0 to Samples-1 do 92 | Result[j*Length(AChannelData)+i] := AChannelData[i][j]; 93 | end 94 | else 95 | SetLength(Result, 0); 96 | end; 97 | 98 | function JoinChannels(AChannelData: PPSingle; AChannels: Integer; 99 | ASamples: Integer): TSingleArray; 100 | var 101 | i: Integer; 102 | j: Integer; 103 | begin 104 | if ASamples > 0 then 105 | begin 106 | SetLength(Result, AChannels * ASamples); 107 | for i := 0 to AChannels-1 do 108 | for j := 0 to ASamples-1 do 109 | Result[j*AChannels+i] := AChannelData[i][j]; 110 | end 111 | else 112 | SetLength(Result, 0); 113 | 114 | end; 115 | 116 | 117 | end. 118 | 119 | -------------------------------------------------------------------------------- /pascalaudioio/paio_vorbis_comment.pas: -------------------------------------------------------------------------------- 1 | unit paio_vorbis_comment; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils; 9 | 10 | type 11 | 12 | { TVorbisComments } 13 | 14 | TVorbisComments = class 15 | private 16 | FUserComments: TStrings; 17 | FVendor: String; 18 | public 19 | constructor Create; 20 | destructor Destroy; override; 21 | procedure LoadFromStream(AStream: TStream); 22 | procedure SaveToStream(AStream: TStream); 23 | published 24 | property Vendor: String read FVendor write FVendor; 25 | property UserComments: TStrings read FUserComments; 26 | end; 27 | 28 | implementation 29 | 30 | { TVorbisComments } 31 | 32 | constructor TVorbisComments.Create; 33 | begin 34 | FUserComments := TStringList.Create; 35 | end; 36 | 37 | destructor TVorbisComments.Destroy; 38 | begin 39 | FUserComments.Free; 40 | inherited Destroy; 41 | end; 42 | 43 | procedure TVorbisComments.SaveToStream(AStream: TStream); 44 | var 45 | s: String; 46 | begin 47 | AStream.WriteDWord(NtoLE(DWord(Length(FVendor)))); 48 | AStream.Write(FVendor[1], Length(FVendor)); 49 | AStream.WriteDWord(NtoLE(DWord(FUserComments.Count))); 50 | for s in FUserComments do 51 | begin 52 | AStream.WriteDWord(NtoLE(DWord(Length(s)))); 53 | AStream.Write(s[1], Length(s)); 54 | end; 55 | end; 56 | 57 | procedure TVorbisComments.LoadFromStream(AStream: TStream); 58 | var 59 | lLength, lCount: DWord; 60 | lItem: String; 61 | begin 62 | lLength := LEtoN(AStream.ReadDWord); 63 | SetLength(FVendor, lLength); 64 | AStream.Read(FVendor[1], lLength); 65 | 66 | lCount := LEtoN(AStream.ReadDWord); 67 | while lCount > 0 do 68 | begin 69 | lLength := LEtoN(AStream.ReadDWord); 70 | SetLength(lItem, lLength); 71 | AStream.Read(lItem[1], lLength); 72 | FUserComments.Add(lItem); 73 | Dec(lCount); 74 | end; 75 | // possibly a framing bit after that but we don't try to read it here. 76 | end; 77 | 78 | end. 79 | 80 | -------------------------------------------------------------------------------- /pascalaudioio/pascalaudioio.lpk: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <CONFIG> 3 | <Package Version="5"> 4 | <Name Value="PascalAudioIO"/> 5 | <Type Value="RunAndDesignTime"/> 6 | <CompilerOptions> 7 | <Version Value="11"/> 8 | <SearchPaths> 9 | <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> 10 | </SearchPaths> 11 | </CompilerOptions> 12 | <Files> 13 | <Item> 14 | <Filename Value="ladspa.pas"/> 15 | <UnitName Value="ladspa"/> 16 | </Item> 17 | <Item> 18 | <Filename Value="samplerate.pas"/> 19 | <UnitName Value="samplerate"/> 20 | </Item> 21 | <Item> 22 | <Filename Value="resample.pas"/> 23 | <UnitName Value="resample"/> 24 | </Item> 25 | <Item> 26 | <Filename Value="pa_ringbuffer.pas"/> 27 | <UnitName Value="pa_ringbuffer"/> 28 | </Item> 29 | <Item> 30 | <Filename Value="ladspa_classes.pas"/> 31 | <UnitName Value="ladspa_classes"/> 32 | </Item> 33 | <Item> 34 | <Filename Value="audacity_noiseremoval.pas"/> 35 | <UnitName Value="audacity_noiseremoval"/> 36 | </Item> 37 | <Item> 38 | <Filename Value="audacity_realfftf.pas"/> 39 | <UnitName Value="audacity_realfftf"/> 40 | </Item> 41 | <Item> 42 | <Filename Value="ogghfobject.pas"/> 43 | <UnitName Value="OggHfObject"/> 44 | </Item> 45 | <Item> 46 | <Filename Value="flac_classes.pas"/> 47 | <UnitName Value="flac_classes"/> 48 | </Item> 49 | <Item> 50 | <Filename Value="paio_types.pas"/> 51 | <UnitName Value="paio_types"/> 52 | </Item> 53 | <Item> 54 | <Filename Value="bs2b.pas"/> 55 | <UnitName Value="bs2b"/> 56 | </Item> 57 | <Item> 58 | <Filename Value="paio_channelhelper.pas"/> 59 | <UnitName Value="paio_channelhelper"/> 60 | </Item> 61 | <Item> 62 | <Filename Value="paio_utils.pas"/> 63 | <UnitName Value="paio_utils"/> 64 | </Item> 65 | <Item> 66 | <Filename Value="noiseremovalmultichannel.pas"/> 67 | <UnitName Value="noiseremovalmultichannel"/> 68 | </Item> 69 | <Item> 70 | <Filename Value="flac_encode.inc"/> 71 | <Type Value="Binary"/> 72 | </Item> 73 | <Item> 74 | <Filename Value="flac_metadata.inc"/> 75 | <Type Value="Binary"/> 76 | </Item> 77 | <Item> 78 | <Filename Value="flac_format.inc"/> 79 | <Type Value="Binary"/> 80 | </Item> 81 | <Item> 82 | <Filename Value="flac_callbacks.inc"/> 83 | <Type Value="Binary"/> 84 | </Item> 85 | <Item> 86 | <Filename Value="flac_decode.inc"/> 87 | <Type Value="Binary"/> 88 | </Item> 89 | <Item> 90 | <Filename Value="paio_messagequeue.pas"/> 91 | <UnitName Value="paio_messagequeue"/> 92 | </Item> 93 | <Item> 94 | <Filename Value="paio_faad2.pas"/> 95 | <UnitName Value="paio_faad2"/> 96 | </Item> 97 | <Item> 98 | <Filename Value="paio_mmdevice.pas"/> 99 | <UnitName Value="paio_mmdevice"/> 100 | </Item> 101 | <Item> 102 | <Filename Value="mp4codec.pas"/> 103 | <UnitName Value="mp4codec"/> 104 | </Item> 105 | <Item> 106 | <Filename Value="mp4codec_mp4a.pas"/> 107 | <UnitName Value="mp4codec_mp4a"/> 108 | </Item> 109 | <Item> 110 | <Filename Value="quicktimeatoms.pas"/> 111 | <UnitName Value="quicktimeatoms"/> 112 | </Item> 113 | <Item> 114 | <Filename Value="quicktimecontainer.pas"/> 115 | <UnitName Value="quicktimecontainer"/> 116 | </Item> 117 | <Item> 118 | <Filename Value="paio_opus.pas"/> 119 | <UnitName Value="paio_opus"/> 120 | </Item> 121 | <Item> 122 | <Filename Value="paio_ogg_container.pas"/> 123 | <UnitName Value="paio_ogg_container"/> 124 | </Item> 125 | <Item> 126 | <Filename Value="paio_vorbis_comment.pas"/> 127 | <UnitName Value="paio_vorbis_comment"/> 128 | </Item> 129 | <Item> 130 | <Filename Value="paio_ogg_opus.pas"/> 131 | <UnitName Value="paio_ogg_opus"/> 132 | </Item> 133 | </Files> 134 | <RequiredPkgs> 135 | <Item> 136 | <PackageName Value="FCL"/> 137 | </Item> 138 | </RequiredPkgs> 139 | <UsageOptions> 140 | <UnitPath Value="$(PkgOutDir)"/> 141 | </UsageOptions> 142 | <PublishOptions> 143 | <Version Value="2"/> 144 | </PublishOptions> 145 | </Package> 146 | </CONFIG> 147 | -------------------------------------------------------------------------------- /pascalaudioio/pascalaudioio.pas: -------------------------------------------------------------------------------- 1 | { This file was automatically created by Lazarus. Do not edit! 2 | This source is only used to compile and install the package. 3 | } 4 | 5 | unit PascalAudioIO; 6 | 7 | {$warn 5023 off : no warning about unused units} 8 | interface 9 | 10 | uses 11 | ladspa, samplerate, resample, pa_ringbuffer, ladspa_classes, 12 | audacity_noiseremoval, audacity_realfftf, OggHfObject, flac_classes, 13 | paio_types, bs2b, paio_channelhelper, paio_utils, noiseremovalmultichannel, 14 | paio_messagequeue, paio_faad2, paio_mmdevice, mp4codec, mp4codec_mp4a, 15 | quicktimeatoms, quicktimecontainer, paio_opus, paio_ogg_container, 16 | paio_vorbis_comment, paio_ogg_opus, LazarusPackageIntf; 17 | 18 | implementation 19 | 20 | procedure Register; 21 | begin 22 | end; 23 | 24 | initialization 25 | RegisterPackage('PascalAudioIO', @Register); 26 | end. 27 | -------------------------------------------------------------------------------- /pascalaudioio/quicktimecontainer.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of the PascalAudio project. 3 | 4 | Copyright (c) 2020 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and license.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit quicktimecontainer; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, quicktimeatoms; 22 | 23 | type 24 | 25 | 26 | TTopLevelAtoms = set of (tla_ftyp, tla_moov, tla_mdat); 27 | 28 | { TQuicktimeContainer } 29 | 30 | TQuicktimeContainer = class 31 | function AtomLoaded(Sender: TAtomList; AAtom: TAtom): Boolean; 32 | private 33 | FPresentAtoms: TTopLevelAtoms; 34 | FAtoms: TAtomList; 35 | FOwnsStream: Boolean; 36 | FStream: TStream; 37 | function GetIsValidFile: Boolean; 38 | procedure LoadTopLevelAtoms; 39 | public 40 | constructor Create(AFilename: String); 41 | constructor Create(AStream: TStream; AOwnsStream: Boolean); 42 | property Atoms: TAtomList read FAtoms; 43 | property Stream: TStream read FStream; 44 | destructor Destroy; override; 45 | property IsValidFile: Boolean read GetIsValidFile; 46 | end; 47 | 48 | // audiodata: 'moov/trak/mdia/mdhd' // samplerate and other stuff too. not channels 49 | // audiodata: 'moov/trak:%d/mdia/minf/stbl/stsd' 50 | // chaptrak: 'moov/trak/tref/chap' 51 | // tags: 'moov/udta/meta/ilst' 52 | // chapframes:'moov/trak:%d/mdia/minf/stbl/stts' // %d is the trak from chaptrak in track 1 53 | // chapters: 'moov/trak:%d/udta/meta/ilst' // %d is the trak from chaptrak in track 1 54 | // 55 | 56 | 57 | implementation 58 | 59 | { TQuicktimeContainer } 60 | 61 | function TQuicktimeContainer.AtomLoaded(Sender: TAtomList; AAtom: TAtom): Boolean; 62 | begin 63 | Result := True; 64 | if FPresentAtoms <> [tla_ftyp, tla_moov, tla_mdat] then 65 | begin 66 | if AAtom.AtomName = 'ftyp' then 67 | Include(FPresentAtoms, tla_ftyp) 68 | else if AAtom.AtomName = 'moov' then 69 | Include(FPresentAtoms, tla_moov) 70 | else if AAtom.AtomName = 'mdat' then 71 | Include(FPresentAtoms, tla_mdat); 72 | if (Sender.Count = 0) and not (tla_ftyp in FPresentAtoms) then 73 | Result := False; 74 | end; 75 | end; 76 | 77 | procedure TQuicktimeContainer.LoadTopLevelAtoms; 78 | var 79 | Atom: TAtom; 80 | begin 81 | FStream.Position:=0; 82 | FAtoms.LoadAtoms(FStream, @AtomLoaded, FStream.Size); 83 | end; 84 | 85 | function TQuicktimeContainer.GetIsValidFile: Boolean; 86 | begin 87 | Result := FPresentAtoms = [tla_ftyp, tla_moov, tla_mdat]; 88 | end; 89 | 90 | constructor TQuicktimeContainer.Create(AFilename: String); 91 | var 92 | F: TFileStream; 93 | begin 94 | F := TFileStream.Create(AFilename, fmOpenRead or fmShareDenyNone); 95 | Create(F, True); 96 | end; 97 | 98 | constructor TQuicktimeContainer.Create(AStream: TStream; AOwnsStream: Boolean); 99 | begin 100 | FOwnsStream := AOwnsStream; 101 | FStream := AStream; 102 | 103 | FAtoms := TAtomList.Create(nil, Self); 104 | LoadTopLevelAtoms; 105 | end; 106 | 107 | destructor TQuicktimeContainer.Destroy; 108 | begin 109 | if FOwnsStream then 110 | FStream.Free; 111 | if Assigned(FAtoms) then 112 | FAtoms.Free; 113 | inherited Destroy; 114 | end; 115 | 116 | end. 117 | 118 | 119 | -------------------------------------------------------------------------------- /pascalaudioio/resample.pas: -------------------------------------------------------------------------------- 1 | { libresample which this file links to is LGPLv2.1 } 2 | { 3 | This unit is part of the PascalAudio project. 4 | 5 | Copyright (c) 2016 by Andrew Haines. 6 | 7 | See the files COPYING.modifiedLGPL and license.txt, included in this 8 | distribution, for details about the license. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | } 14 | 15 | unit resample; 16 | 17 | {$mode objfpc}{$H+} 18 | 19 | {$linklib resample} 20 | 21 | interface 22 | 23 | uses 24 | ctypes, pa_ringbuffer, paio_types; 25 | 26 | type 27 | { TResampleChannel } 28 | 29 | TResampleChannel = class 30 | private 31 | FResample: Pointer; 32 | FOutBuffer: TRingBuffer; 33 | public 34 | constructor Create; 35 | destructor Destroy; override; 36 | function WriteData(AData: PSingle; ASampleCount: Integer; AFactor: Double; AIsLastData: Boolean): Integer; 37 | function ReadData(var ADest; ASize: Integer): Integer; 38 | function ReadSingle: Single; 39 | end; 40 | 41 | { TResampleHelper } 42 | 43 | TResampleHelper = class 44 | Channels: array of TResampleChannel; 45 | //function PlexOutBuffers(out ASamples: Integer): PSingle; // the caller frees this result 46 | function Write(const AData: PSingle; ASampleCount: Integer; AFactor: Double; AIsLastData: Boolean): TSingleArray; 47 | 48 | constructor Create(AChannels: Integer); 49 | destructor Destroy; override; 50 | 51 | end; 52 | 53 | // libresample functions. LGPL 54 | function resample_open(highQuality: cint; minFactor, maxFactor: cdouble): Pointer cdecl; external; 55 | 56 | function resample_dup(handle: pointer): Pointer cdecl; external; 57 | 58 | function resample_get_filter_width(handle: pointer): cint cdecl; external; 59 | 60 | function resample_process(handle: pointer; 61 | factor: cdouble; 62 | inBuffer: pcfloat; 63 | inBufferLen: cint; 64 | lastFlag: cint; 65 | inBufferUsed: pcint; 66 | outBuffer: pcfloat; 67 | outBufferLen: cint): cint cdecl; external; 68 | 69 | procedure resample_close(handle: pointer) cdecl; external; 70 | 71 | 72 | 73 | implementation 74 | uses 75 | sysutils; 76 | 77 | { TResampleChannel } 78 | 79 | constructor TResampleChannel.Create; 80 | begin 81 | FResample := resample_open(1, 0.1, 7.0); 82 | FOutBuffer := TRingBuffer.Create(AUDIO_BUFFER_SIZE*7); 83 | end; 84 | 85 | destructor TResampleChannel.Destroy; 86 | begin 87 | inherited Destroy; 88 | FOutBuffer.Free; 89 | resample_close(FResample); 90 | end; 91 | 92 | function TResampleChannel.WriteData(AData: PSingle; ASampleCount: Integer; 93 | AFactor: Double; AIsLastData: Boolean): Integer; 94 | var 95 | WrittenSamples: cint; 96 | InPos: Integer; 97 | InBufUsed: cint; 98 | OutPos: Integer; 99 | OutBuffer: array[0..AUDIO_BUFFER_SIZE*5] of Single; 100 | begin 101 | InPos := 0; 102 | OutPos := 0; 103 | Result := 0; 104 | repeat 105 | WrittenSamples := resample_process(FResample, 106 | AFactor, 107 | @AData[InPos], 108 | ASampleCount-InPos, 109 | Ord(AIsLastData), 110 | @InBufUsed, 111 | @OutBuffer[0], 112 | Length(OutBuffer)); 113 | Inc(InPos, InBufUsed); 114 | //WriteLn('InnerLoop InPos = ', InPos, ' InUsed = ' , InBufUsed, ' Written = ', WrittenSamples, ' SampleCount = ', ASampleCount); 115 | if WrittenSamples > 0 then 116 | begin 117 | Inc(OutPos, WrittenSamples); 118 | 119 | Inc(Result, FOutBuffer.Write(OutBuffer[0], WrittenSamples*SizeOf(Single))div 4); 120 | end; 121 | 122 | until (WrittenSamples = 0) or (InPos = ASampleCount); 123 | //Result := OutPos 124 | end; 125 | 126 | function TResampleChannel.ReadData(var ADest; ASize: Integer): Integer; 127 | begin 128 | Result := FOutBuffer.Read(ADest, ASize); 129 | end; 130 | 131 | function TResampleChannel.ReadSingle: Single; 132 | begin 133 | FOutBuffer.Read(Result, SizeOf(Single)); 134 | end; 135 | 136 | 137 | {function TResampleHelper.PlexOutBuffers(out ASamples: Integer): PSingle; 138 | var 139 | i: Integer; 140 | j: Integer; 141 | begin 142 | ASize := Sizeof(Single) * OutBuffersLength[0] ; 143 | Result := GetMem(ASize); 144 | for i := 0 to OutBuffersLength[0]-1 do 145 | begin 146 | for j := 0 to ChannelCount-1 do 147 | Result[i*j] := OutBuffers[j][i]; 148 | end; 149 | end;} 150 | 151 | function TResampleHelper.Write(const AData: PSingle; ASampleCount: Integer; 152 | AFactor: Double; AIsLastData: Boolean): TSingleArray; 153 | var 154 | i: Integer; 155 | j: Integer; 156 | OutChannel: array of Single; 157 | SamplesPerChannel: Integer; 158 | WrittenSamples: Integer; 159 | MaxSamples: Integer; // data available across all channels 160 | begin 161 | Result := nil; 162 | MaxSamples := 0; 163 | SamplesPerChannel := ASampleCount div Length(Channels); 164 | SetLength(OutChannel, SamplesPerChannel); 165 | for i := 0 to High(Channels) do begin 166 | for j := 0 to SamplesPerChannel -1 do // BytesPerSample(2) / AChannels 167 | OutChannel[j] := AData[j*Length(Channels)+i]; // to float 168 | WrittenSamples := Channels[i].WriteData(@OutChannel[0], SamplesPerChannel, AFactor, AIsLastData); 169 | //WriteLn(Format('Channel(%d) Wrote %d samples ',[i+1, WrittenSamples])); 170 | if (MaxSamples = 0) or (WrittenSamples < MaxSamples) then 171 | MaxSamples := WrittenSamples; 172 | end; 173 | 174 | SetLength(Result, MaxSamples*Length(Channels)); 175 | //Result := Getmem(SizeOf(Single)*MaxSamples*Length(Channels)); 176 | for i := 0 to High(Channels) do 177 | for j := 0 to MaxSamples-1 do 178 | Result[j*Length(Channels)+i] := Channels[i].ReadSingle; 179 | 180 | end; 181 | 182 | constructor TResampleHelper.Create(AChannels: Integer); 183 | var 184 | i: Integer; 185 | begin 186 | SetLength(Channels, AChannels); 187 | for i := 0 to High(Channels) do 188 | Channels[i] := TResampleChannel(TResampleChannel.Create); 189 | end; 190 | 191 | destructor TResampleHelper.Destroy; 192 | var 193 | i: Integer; 194 | begin 195 | for i := 0 to High(Channels) do 196 | begin 197 | Channels[i].Free; 198 | end; 199 | inherited Destroy; 200 | end; 201 | 202 | end. 203 | 204 | -------------------------------------------------------------------------------- /pascalaudioio/samplerate.pas: -------------------------------------------------------------------------------- 1 | {libsamplerate which this file links to is GPL} 2 | (* 3 | ** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com> 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | /* 21 | ** This code is part of Secret Rabbit Code aka libsamplerate. A commercial 22 | ** use license for this code is available, please see: 23 | ** http://www.mega-nerd.com/SRC/procedure.html 24 | *) 25 | 26 | (* 27 | ** API documentation is available here: 28 | ** http://www.mega-nerd.com/SRC/api.html 29 | *) 30 | 31 | unit samplerate; 32 | 33 | {$mode objfpc}{$H+} 34 | {$packrecords c} 35 | {$linklib samplerate} 36 | 37 | interface 38 | 39 | uses 40 | ctypes; 41 | 42 | type 43 | 44 | //* Opaque data type SRC_STATE. */ 45 | PSRC_STATE = ^SRC_STATE; 46 | SRC_STATE = record 47 | end; 48 | 49 | //* SRC_DATA is used to pass data to src_simple() and src_process(). */ 50 | PSRC_DATA = ^SRC_DATA; 51 | SRC_DATA = record 52 | data_in, 53 | data_out: pcfloat; 54 | 55 | input_frames, 56 | output_frames: clong; 57 | input_frames_used, 58 | output_frames_gen: clong; 59 | 60 | end_of_input: cint; 61 | 62 | src_ratio: cdouble; 63 | end; 64 | 65 | 66 | //* SRC_CB_DATA is used with callback based API. */ 67 | SRC_CB_DATA = record 68 | frames : clong; 69 | data_in: pcfloat; 70 | end; 71 | 72 | (* 73 | ** User supplied callback function type for use with src_callback_new() 74 | ** and src_callback_read(). First parameter is the same pointer that was 75 | ** passed into src_callback_new(). Second parameter is pointer to a 76 | ** pointer. The user supplied callback function must modify *data to 77 | ** point to the start of the user supplied float array. The user supplied 78 | ** function must return the number of frames that **data points to. 79 | *) 80 | ppcfloat = ^pcfloat; 81 | src_callback_t = function(cb_data: pointer; data: ppcfloat): clong; cdecl; 82 | 83 | (* 84 | ** Standard initialisation function : return an anonymous pointer to the 85 | ** internal state of the converter. Choose a converter from the enums below. 86 | ** Error returned in *error. 87 | *) 88 | 89 | function src_new (converter_type: cint; channels: cint; error: pcint): PSRC_STATE; cdecl; external; 90 | 91 | (* 92 | ** Initilisation for callback based API : return an anonymous pointer to the 93 | ** internal state of the converter. Choose a converter from the enums below. 94 | ** The cb_data pointer can point to any data or be set to NULL. Whatever the 95 | ** value, when processing, user supplied function "func" gets called with 96 | ** cb_data as first parameter. 97 | *) 98 | 99 | function src_callback_new (func: src_callback_t; converter_type: cint; channels: cint; 100 | error: pcint;cb_data: pointer): PSRC_STATE; cdecl; external; 101 | 102 | (* 103 | ** Cleanup all internal allocations. 104 | ** Always returns NULL. 105 | *) 106 | 107 | function src_delete (state: PSRC_STATE): PSRC_STATE; cdecl; external; 108 | 109 | (* 110 | ** Standard processing function. 111 | ** Returns non zero on error. 112 | *) 113 | 114 | function src_process (state: PSRC_STATE; data: PSRC_DATA): cint; cdecl; external; 115 | 116 | (* 117 | ** Callback based processing function. Read up to frames worth of data from 118 | ** the converter int *data and return frames read or -1 on error. 119 | *) 120 | function src_callback_read (state: PSRC_STATE; src_ratio: cdouble; frames: clong; data: pcfloat): clong; cdecl; external; 121 | 122 | (* 123 | ** Simple interface for performing a single conversion from input buffer to 124 | ** output buffer at a fixed conversion ratio. 125 | ** Simple interface does not require initialisation as it can only operate on 126 | ** a single buffer worth of audio. 127 | *) 128 | function src_simple (data: PSRC_DATA; converter_type: cint; channels: cint ): cint; cdecl; external; 129 | 130 | (* 131 | ** This library contains a number of different sample rate converters, 132 | ** numbered 0 through N. 133 | ** 134 | ** Return a string giving either a name or a more full description of each 135 | ** sample rate converter or NULL if no sample rate converter exists for 136 | ** the given value. The converters are sequentially numbered from 0 to N. 137 | *) 138 | 139 | function src_get_name (converter_type: cint): PChar; cdecl; external; 140 | function src_get_description (converter_type: cint): PChar; cdecl; external; 141 | function src_get_version: PChar; cdecl; external; 142 | 143 | (* 144 | ** Set a new SRC ratio. This allows step responses 145 | ** in the conversion ratio. 146 | ** Returns non zero on error. 147 | *) 148 | 149 | function src_set_ratio (state: PSRC_STATE; new_ratio: cdouble): cint; cdecl; external; 150 | 151 | (* 152 | ** Reset the internal SRC state. 153 | ** Does not modify the quality settings. 154 | ** Does not free any memory allocations. 155 | ** Returns non zero on error. 156 | *) 157 | 158 | function src_reset (state: PSRC_STATE): cint; cdecl; external; 159 | 160 | (* 161 | ** Return TRUE if ratio is a valid conversion ratio, FALSE 162 | ** otherwise. 163 | *) 164 | 165 | function src_is_valid_ratio (ratio: cdouble ): cint; cdecl; external; 166 | 167 | (* 168 | ** Return an error number. 169 | *) 170 | 171 | function src_error (state: PSRC_STATE): cint; cdecl; external; 172 | 173 | (* 174 | ** Convert the error number into a string. 175 | *) 176 | function src_strerror (error: cint): PChar; cdecl; external; 177 | 178 | (* 179 | ** The following enums can be used to set the interpolator type 180 | ** using the function src_set_converter(). 181 | *) 182 | 183 | const 184 | SRC_SINC_BEST_QUALITY = 0; 185 | SRC_SINC_MEDIUM_QUALITY = 1; 186 | SRC_SINC_FASTEST = 2; 187 | SRC_ZERO_ORDER_HOLD = 3; 188 | SRC_LINEAR = 4; 189 | 190 | 191 | (* 192 | ** Extra helper functions for converting from short to float and 193 | ** back again. 194 | *) 195 | 196 | procedure src_short_to_float_array (const_in: pcshort; out_data: pcfloat; len: cint) cdecl; external; 197 | procedure src_float_to_short_array (const_in: pcfloat; out_data: pcshort; len: cint); cdecl; external; 198 | 199 | procedure src_int_to_float_array (const_in: pcint; out_data: pcfloat; len: cint); cdecl; external; 200 | procedure src_float_to_int_array (const_in: pcfloat; out_data: pcint; len: cint); cdecl; external; 201 | 202 | 203 | implementation 204 | 205 | end. 206 | 207 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_binaural.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_binaural; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, pa_base, bs2b; 22 | 23 | type 24 | { TPABinauralLink } 25 | 26 | TPABinauralLink = class(TPAAudioLink, IPAAudioInformation) 27 | private 28 | Fbs2bLevel: LongWord; 29 | Fbs2bLevelCutFreq: LongWord; 30 | Fbs2bLevelFeed: LongWord; 31 | FInstance: Pbs2bd; 32 | FInited: Boolean; 33 | procedure InitData; 34 | protected 35 | function InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; override; 36 | procedure SignalDestinationsDone; override; 37 | function GetFormat: TPAAudioFormat; override; 38 | public 39 | constructor Create; override; 40 | destructor Destroy; override; 41 | property Level: LongWord read Fbs2bLevel write Fbs2bLevel; 42 | property LevelCutFreq: LongWord read Fbs2bLevelCutFreq write Fbs2bLevelCutFreq; 43 | property LeveLFeed: LongWord read Fbs2bLevelFeed write Fbs2bLevelFeed; 44 | end; 45 | 46 | implementation 47 | 48 | { TPABinauralLink } 49 | 50 | procedure TPABinauralLink.InitData; 51 | begin 52 | FInstance:=Tbs2bd.Open; 53 | FInstance^.SampleRate:=SamplesPerSecond; 54 | FInstance^.Level:=Level; 55 | FInstance^.LevelCutFreq:=LevelCutFreq; 56 | FInstance^.LevelFeed:=LeveLFeed; 57 | end; 58 | 59 | function TPABinauralLink.InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; 60 | var 61 | Data: PSingle; 62 | Samples: Integer; 63 | B: PAudioBuffer; 64 | begin 65 | if not FInited then 66 | InitData; 67 | 68 | Samples := SizeOf(Single) div ACount div 2; 69 | 70 | B := BufferPool.GetBufferFromPool(True); 71 | 72 | B^.Format:=Format; 73 | Move(AData, B^.Data, ACount); 74 | B^.UsedData:=ACount; 75 | B^.IsEndOfData:=AIsLastData; 76 | 77 | 78 | 79 | FInstance^.CrossFeed_f(@B^.Data, Samples); 80 | WriteToDestinations(B);//} 81 | { 82 | 83 | FInstance^.CrossFeed_f(PSingle(@Data), Samples); 84 | WriteToBuffer(Data, ACount, AIsLastData);//} 85 | 86 | 87 | 88 | Result := ACount; 89 | 90 | if AIsLastData then FInstance^.Clear; 91 | end; 92 | 93 | procedure TPABinauralLink.SignalDestinationsDone; 94 | begin 95 | inherited SignalDestinationsDone; 96 | end; 97 | 98 | function TPABinauralLink.GetFormat: TPAAudioFormat; 99 | begin 100 | Result := afFloat32; 101 | end; 102 | 103 | constructor TPABinauralLink.Create; 104 | begin 105 | inherited Create; 106 | end; 107 | 108 | destructor TPABinauralLink.Destroy; 109 | begin 110 | if FInited then 111 | FInstance^.Close; 112 | inherited Destroy; 113 | end; 114 | 115 | end. 116 | 117 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_cdaudio.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_cdaudio; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | {$IFDEF UNIX} 20 | uses 21 | Classes, SysUtils, 22 | pa_base, 23 | lincd, cdrom, 24 | discid 25 | ; 26 | 27 | type 28 | 29 | { TPAAudioCDSource } 30 | 31 | TPAAudioCDSource = class(TPAAudioSource) 32 | public 33 | type 34 | TTrackInfo = TTocEntry; 35 | TTracks = array of TTrackInfo; 36 | private 37 | FDevice: String; 38 | FFirstTrack: Integer; 39 | FHandle: Integer; 40 | FInited: Boolean; 41 | FLastTrack: Integer; 42 | FTrackCount: Integer; 43 | FTracks: TTracks; 44 | FFirstFrame: Integer; 45 | FFrameIndex: Integer; 46 | FLastFrame: Integer; 47 | function GetCDDBId: Integer; 48 | function GetTrack(AIndex: Integer): TTrackInfo; 49 | function Inited: Boolean; 50 | procedure DeInit; 51 | procedure SetFirstTrack(AValue: Integer); 52 | procedure SetLastTrack(AValue: Integer); 53 | procedure SetTrackCount(AValue: Integer); 54 | protected 55 | function InternalOutputToDestination: Boolean; override; 56 | public 57 | constructor Create; override; 58 | destructor Destroy; override; 59 | property Device: String read FDevice write FDevice; 60 | property Track[AIndex: Integer]: TTrackInfo read GetTrack; 61 | property TrackCount: Integer read FTrackCount write SetTrackCount; 62 | property FirstTrack: Integer read FFirstTrack write SetFirstTrack; 63 | property LastTrack: Integer read FLastTrack write SetLastTrack; 64 | property CDDBId: Integer read GetCDDBId; 65 | end; 66 | 67 | {$ENDIF} 68 | 69 | implementation 70 | {$IFDEF UNIX} 71 | uses 72 | BaseUnix, Unix; 73 | 74 | { TPAAudioCDSource } 75 | 76 | function TPAAudioCDSource.Inited: Boolean; 77 | var 78 | TocHeader: Tcdrom_tochdr; 79 | TocEntry: Tcdrom_tocentry; 80 | i: Integer; 81 | t: Integer=0; 82 | begin 83 | if FInited then 84 | Exit(True); 85 | 86 | Result := False; 87 | 88 | // check if device not set or device doesn't exist 89 | if not ((FDevice <> '') and FileExists(FDevice)) then 90 | Exit; 91 | 92 | FHandle := FpOpen(FDevice, Open_RdOnly or Open_NonBlock); 93 | if FHandle < 0 then 94 | Exit; 95 | 96 | if FpIOCtl(FHandle, CDROMREADTOCHDR, @TocHeader)<>0 then 97 | Exit; 98 | 99 | if TocHeader.cdth_trk1-TocHeader.cdth_trk0 > 0 then 100 | begin 101 | SetLength(FTracks, TocHeader.cdth_trk1-TocHeader.cdth_trk0 +1); 102 | for i := TocHeader.cdth_trk0 to TocHeader.cdth_trk1 do 103 | begin 104 | TocEntry.cdte_track := i; 105 | TocEntry.cdte_format := CDROM_MSF; 106 | FpIOCtl(FHandle, CDROMREADTOCENTRY, @TocEntry); 107 | FTracks[t].min:=TocEntry.cdte_addr.msf.minute; 108 | FTracks[t].sec:=TocEntry.cdte_addr.msf.second; 109 | FTracks[t].frame:=((TocEntry.cdte_addr.msf.minute*60) + TocEntry.cdte_addr.msf.second) * 75 + TocEntry.cdte_addr.msf.frame; 110 | if i = TocHeader.cdth_trk0 then 111 | FFirstFrame:=FTracks[t].frame; 112 | Inc(t); 113 | end; 114 | // now set the last tracks end position as the end of the cd 115 | TocEntry.cdte_track := $AA; 116 | TocEntry.cdte_format := CDROM_MSF; 117 | FpIOCtl(FHandle, CDROMREADTOCENTRY, @TocEntry); 118 | FTracks[t].min:=TocEntry.cdte_addr.msf.minute; 119 | FTracks[t].sec:=TocEntry.cdte_addr.msf.second; 120 | FTracks[t].frame:=((TocEntry.cdte_addr.msf.minute*60) + TocEntry.cdte_addr.msf.second) * 75 + TocEntry.cdte_addr.msf.frame; 121 | FLastFrame := FTracks[t].frame; 122 | end; 123 | 124 | FFrameIndex:=FFirstFrame; 125 | 126 | FInited:=True; 127 | 128 | Channels:=2; 129 | Format:=afS16; 130 | SamplesPerSecond:=44100; 131 | 132 | if FirstTrack = 0 then 133 | FirstTrack:=1; 134 | if LastTrack = 0 then 135 | LastTrack :=TrackCount; 136 | 137 | FirstTrack:=FFirstTrack; 138 | LastTrack:=FLastTrack; 139 | 140 | 141 | Result := True; 142 | end; 143 | 144 | procedure TPAAudioCDSource.DeInit; 145 | begin 146 | if not FInited then 147 | Exit; 148 | FInited:=False; 149 | if FHandle >= 0 then 150 | FpClose(FHandle); 151 | FHandle:=-1; 152 | SetLength(FTracks, 0); 153 | end; 154 | 155 | procedure TPAAudioCDSource.SetFirstTrack(AValue: Integer); 156 | begin 157 | FFirstTrack:=AValue; 158 | if not Inited then 159 | Exit; 160 | FFirstFrame:=FTracks[AValue-1].frame; 161 | FFrameIndex:=FFirstFrame; 162 | end; 163 | 164 | procedure TPAAudioCDSource.SetLastTrack(AValue: Integer); 165 | begin 166 | FLastTrack:=AValue; 167 | if not Inited then 168 | Exit; 169 | 170 | FLastFrame:=FTracks[AValue].frame; 171 | end; 172 | 173 | procedure TPAAudioCDSource.SetTrackCount(AValue: Integer); 174 | begin 175 | if FTrackCount=AValue then Exit; 176 | FTrackCount:=Length(FTracks); 177 | end; 178 | 179 | function TPAAudioCDSource.GetTrack(AIndex: Integer): TTrackInfo; 180 | begin 181 | if not Inited then 182 | Exit; 183 | Result := FTracks[AIndex]; 184 | end; 185 | 186 | function TPAAudioCDSource.GetCDDBId: Integer; 187 | begin 188 | Result := 0; 189 | if not Inited then 190 | Exit; 191 | 192 | Result := CDDBDiscID(FTracks, Length(FTracks)); 193 | end; 194 | 195 | function FrameToCDAddr(AFrame: Integer): Tcdrom_addr; 196 | var 197 | Secs: Integer; 198 | begin 199 | Secs := AFrame div 75; 200 | with Result.msf do 201 | begin 202 | frame:= AFrame mod 75; 203 | minute:= Secs div 60; 204 | second:= Secs mod 60; 205 | 206 | WriteLn(Format('%d:%d.%d',[minute,second,frame])); 207 | end; 208 | end; 209 | 210 | function Min(A,B: Integer): Integer; 211 | begin 212 | if A < B then 213 | Exit(A); 214 | Result := B; 215 | end; 216 | 217 | function TPAAudioCDSource.InternalOutputToDestination: Boolean; 218 | const 219 | FRAME_SIZE = 2352; 220 | var 221 | ra: Tcdrom_read_audio; 222 | buffer: array[0..3] of array [0..FRAME_SIZE-1] of byte; 223 | RCount: Integer; 224 | res: BaseUnix.cint; 225 | begin 226 | Result := False; 227 | if not Inited then 228 | Exit; 229 | 230 | RCount := Min(FLastFrame-FFrameIndex, 4); 231 | 232 | ra.addr := FrameToCDAddr(FFrameIndex); 233 | ra.addr_format:=CDROM_MSF; 234 | ra.nframes:=RCount; 235 | ra.buf:=@buffer; 236 | 237 | if RCount > 0 then 238 | begin 239 | res := FpIOCtl(FHandle,CDROMREADAUDIO,@ra); 240 | if Res = 0 then 241 | begin 242 | Inc(FFrameIndex, ra.nframes); 243 | WriteToBuffer(buffer[0], ra.nframes*FRAME_SIZE, FFrameIndex < FLastFrame); 244 | Result := True; 245 | end; 246 | 247 | end; 248 | 249 | 250 | end; 251 | 252 | constructor TPAAudioCDSource.Create; 253 | begin 254 | inherited Create; 255 | FHandle:=-1; 256 | end; 257 | 258 | destructor TPAAudioCDSource.Destroy; 259 | begin 260 | DeInit; 261 | inherited Destroy; 262 | end; 263 | {$ENDIF} 264 | end. 265 | 266 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_dec_oggvorbis.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_dec_oggvorbis; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, 22 | pa_base, 23 | pa_register, 24 | pa_stream, 25 | paio_messagequeue, 26 | ctypes, 27 | OggHfObject; 28 | 29 | type 30 | { TPAOggVorbisDecoderSource } 31 | 32 | TPAOggVorbisDecoderSource = class(TPAStreamSource, IPAPlayable, IPAStream) 33 | private 34 | FInited: Boolean; 35 | Fogg: TOggDecFloat; 36 | function InitOgg: Boolean; 37 | procedure DeInitOgg; 38 | protected 39 | procedure SetStream(AValue: TStream); override; 40 | function InternalOutputToDestination: Boolean; override; 41 | procedure SignalDestinationsDone; override; 42 | procedure HandleMessage(var AMsg: TPAIOMessage); override; 43 | // IPAPlayable 44 | function CanSeek: Boolean; 45 | function GetPosition: Double; 46 | procedure SetPosition(AValue: Double); 47 | function GetMaxPosition: Double; 48 | 49 | public 50 | constructor Create(AStream: TStream; AOwnsStream: Boolean); override; 51 | procedure InitValues; 52 | //IPAPlayable 53 | procedure Play; 54 | procedure Pause; 55 | procedure Stop; 56 | //IStreamSource 57 | property Stream; 58 | //IPAPlayable 59 | property Position: Double read GetPosition write SetPosition; 60 | property MaxPosition: Double read GetMaxPosition; 61 | end; 62 | 63 | implementation 64 | 65 | { TPAOggVorbisDecoderSource } 66 | 67 | procedure TPAOggVorbisDecoderSource.SetStream(AValue: TStream); 68 | begin 69 | if FStream=AValue then Exit; 70 | if FStream <> nil then 71 | begin 72 | StopData; 73 | DeInitOgg; 74 | end; 75 | inherited SetStream(AValue); 76 | 77 | end; 78 | 79 | function TPAOggVorbisDecoderSource.InitOgg: Boolean; 80 | begin 81 | Result := False; 82 | if FStream = nil then 83 | Exit; 84 | 85 | if FInited then 86 | Exit; 87 | 88 | Fogg := TOggDecFloat.TryCreate(FStream, False); 89 | Channels:=Fogg.Info^.channels; 90 | SamplesPerSecond:=FOgg.Info^.rate; 91 | Format:=afFloat32; 92 | FInited:=True; 93 | Result := True; 94 | end; 95 | 96 | procedure TPAOggVorbisDecoderSource.DeInitOgg; 97 | begin 98 | if not FInited then 99 | Exit; 100 | FInited := False; 101 | FreeAndNil(FOgg); 102 | end; 103 | 104 | function TPAOggVorbisDecoderSource.InternalOutputToDestination: Boolean; 105 | var 106 | ChannelData: PPSingle; 107 | ReadSamples: Integer; 108 | PlexedData: TSingleArray; 109 | BitStream: Integer = 0; 110 | begin 111 | Result := False; 112 | if not FInited then 113 | if not InitOgg then 114 | Exit; 115 | 116 | ReadSamples := FOgg.ReadFloat(ChannelData, AUDIO_BUFFER_FLOAT_SAMPLES div FChannels, @BitStream); 117 | 118 | Result := ReadSamples > 0; 119 | 120 | if Result then 121 | begin 122 | PlexedData := JoinChannels(ChannelData, FChannels, ReadSamples); 123 | WriteToBuffer(PlexedData[0], Length(PlexedData)*SizeOf(Single), ReadSamples<=0); 124 | end 125 | else 126 | SignalDestinationsDone; 127 | end; 128 | 129 | procedure TPAOggVorbisDecoderSource.SignalDestinationsDone; 130 | begin 131 | inherited SignalDestinationsDone; 132 | end; 133 | 134 | procedure TPAOggVorbisDecoderSource.HandleMessage(var AMsg: TPAIOMessage); 135 | begin 136 | case AMsg.Message of 137 | PAM_Seek: 138 | if FInited then 139 | Fogg.TimePosition:=AMsg.Data; 140 | end; 141 | end; 142 | 143 | function TPAOggVorbisDecoderSource.CanSeek: Boolean; 144 | begin 145 | Result := True; 146 | end; 147 | 148 | function TPAOggVorbisDecoderSource.GetPosition: Double; 149 | begin 150 | if not FInited then 151 | Exit(0); 152 | Result := Fogg.TimePosition; 153 | 154 | end; 155 | 156 | procedure TPAOggVorbisDecoderSource.SetPosition(AValue: Double); 157 | begin 158 | if not FInited then 159 | Exit; 160 | 161 | FMsgQueue.PostMessage(PAM_Seek, AValue); 162 | end; 163 | 164 | function TPAOggVorbisDecoderSource.GetMaxPosition: Double; 165 | begin 166 | if not FInited then 167 | Exit(0); 168 | 169 | Result := Fogg.TimeLength; 170 | end; 171 | 172 | constructor TPAOggVorbisDecoderSource.Create(AStream: TStream; AOwnsStream: Boolean); 173 | begin 174 | inherited Create(AStream, AOwnsStream); 175 | Format:=afFloat32; 176 | end; 177 | 178 | procedure TPAOggVorbisDecoderSource.InitValues; 179 | var 180 | Tmp: TOggDecFloat; 181 | begin 182 | Tmp := TOggDecFloat.TryCreate(FStream, False); 183 | Channels:=Tmp.Info^.channels; 184 | SamplesPerSecond:=Tmp.Info^.rate; 185 | Format:=afFloat32; 186 | Tmp.Free; 187 | FStream.Position:=0; 188 | end; 189 | 190 | procedure TPAOggVorbisDecoderSource.Play; 191 | begin 192 | 193 | end; 194 | 195 | procedure TPAOggVorbisDecoderSource.Pause; 196 | begin 197 | 198 | end; 199 | 200 | procedure TPAOggVorbisDecoderSource.Stop; 201 | begin 202 | 203 | end; 204 | 205 | initialization 206 | PARegister(partDecoder, TPAOggVorbisDecoderSource, 'OGG/Vorbis', '.ogg', 'OggS', 4); 207 | 208 | end. 209 | 210 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_enc_oggvorbis.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_enc_oggvorbis; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, 22 | pa_base, 23 | pa_stream, 24 | pa_register, 25 | ogg, vorbis, 26 | ctypes; 27 | 28 | type 29 | 30 | { TPAOggVorbisEncoderLink } 31 | 32 | TPAOggVorbisEncoderLink = class(TPAStreamDestination) 33 | private 34 | FBigEndian: Boolean; 35 | FOggStream: ogg_stream_state; 36 | FInfo: vorbis_info; 37 | FDSPState: vorbis_dsp_state; 38 | FComment: vorbis_comment; 39 | FBlock: vorbis_block; 40 | FInited: Boolean; 41 | FQuality: cfloat; 42 | FSerialNumber: cint; 43 | FWrittenBytes: Qword; 44 | procedure InitEncoder; 45 | procedure WritePage(ForceFlush: Boolean); 46 | procedure FinishEncode; 47 | protected 48 | function InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; override; 49 | procedure EndOfData; override; 50 | procedure SetStream(AValue: TStream); override; 51 | 52 | public 53 | constructor Create(AStream: TStream; AOwnsStream: Boolean); override; 54 | function GetWrittenSeconds: QWord; 55 | procedure AddComment(TagName: Utf8String; Content: Utf8String); 56 | property Quality: cfloat read FQuality write FQuality; 57 | property SerialNumber: cint read FSerialNumber write FSerialNumber; 58 | property BigEndian: Boolean read FBigEndian write FBigEndian; 59 | end; 60 | 61 | implementation 62 | 63 | { TPAOggVorbisEncoderLink } 64 | 65 | procedure TPAOggVorbisEncoderLink.InitEncoder; 66 | var 67 | Pkt: ogg_packet; 68 | PktComment: ogg_packet; 69 | PktCode: ogg_packet; 70 | begin 71 | if FInited then 72 | Exit; 73 | 74 | vorbis_info_init(FInfo); 75 | vorbis_encode_setup_vbr(FInfo, Channels, SamplesPerSecond, 1 / Quality); 76 | vorbis_encode_setup_init(FInfo); 77 | vorbis_analysis_init(FDSPState, FInfo); 78 | vorbis_block_init(FDSPState, FBlock); 79 | ogg_stream_init(FOggStream, SerialNumber); 80 | 81 | vorbis_analysis_headerout(FDSPState, FComment, Pkt, PktComment, PktCode); 82 | ogg_stream_packetin(FOggStream, Pkt); 83 | ogg_stream_packetin(FOggStream, PktComment); 84 | ogg_stream_packetin(FOggStream, PktCode); 85 | 86 | WritePage(True); 87 | FInited:=True; 88 | end; 89 | 90 | procedure TPAOggVorbisEncoderLink.WritePage(ForceFlush: Boolean); 91 | var 92 | Page: ogg_page; 93 | w: Boolean; 94 | begin 95 | repeat 96 | if ForceFlush then 97 | w := ogg_stream_flush(FOggStream, Page) <> 0 98 | else 99 | w := ogg_stream_pageout(FOggStream, Page) <> 0; 100 | if w then 101 | begin 102 | //WriteLn('ogg writing to destinations'); 103 | //WriteToBuffer(Page.header^,Page.header_len, False); 104 | //WriteToBuffer(Page.body^,Page.body_len, False); 105 | FStream.Write(Page.header^,Page.header_len); 106 | FStream.Write(Page.body^,Page.body_len); 107 | //WriteLn('ogg wrote to destinations'); 108 | end 109 | else 110 | break; 111 | until False; 112 | end; 113 | 114 | type 115 | Tvorbis_analysisHack = function(var block: vorbis_block; op: pOgg_packet): cint; cdecl; 116 | 117 | 118 | procedure TPAOggVorbisEncoderLink.FinishEncode; 119 | var 120 | op: ogg_packet; 121 | begin 122 | //WriteLn('Finishing encode'); 123 | vorbis_analysis_wrote(FDSPState, 0); 124 | while vorbis_analysis_blockout(FDSPState, FBlock) = 1 do 125 | begin 126 | if Tvorbis_analysisHack(@vorbis_analysis)(FBlock, @op) = 0 then 127 | ogg_stream_packetin(FOggStream, op); 128 | end; 129 | // Flush ogg data to Destinations 130 | WritePage(True); 131 | 132 | ogg_stream_clear(FOggStream); 133 | vorbis_block_clear(FBlock); 134 | vorbis_dsp_clear(FDSPState); 135 | vorbis_info_clear(FInfo); 136 | vorbis_comment_clear(FComment); 137 | 138 | FInited:=False; 139 | end; 140 | 141 | function TPAOggVorbisEncoderLink.InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; 142 | var 143 | buffer: ppcfloat; 144 | i: Integer; 145 | j: Integer; 146 | samples: Integer; 147 | op: ogg_packet; 148 | res: Integer; 149 | begin 150 | if not FInited then 151 | InitEncoder; 152 | //WriteLn('ogg process data. Ended=',FDataIsEnded); 153 | // move to end; 154 | Result := ACount; 155 | 156 | Inc(FWrittenBytes, ACount); 157 | 158 | Samples := ACount div (BytesPerSample(afFloat32) * Channels); 159 | if samples > 0 then 160 | begin 161 | // split the channels into contigous data from interleaved. 162 | buffer := vorbis_analysis_buffer(FDSPState, samples); 163 | for j := 0 to Channels-1 do 164 | for i := 0 to samples-1 do 165 | buffer[j][i] := PSingle(@AData)[i*channels+j]; 166 | 167 | vorbis_analysis_wrote(FDSPState, samples); 168 | 169 | while vorbis_analysis_blockout(FDSPState, FBlock) = 1 do 170 | begin 171 | Tvorbis_analysisHack(@vorbis_analysis)(FBlock, nil); 172 | vorbis_bitrate_addblock(FBlock); 173 | while vorbis_bitrate_flushpacket(FDSPState, op) > 0 do 174 | ogg_stream_packetin(FOggStream, op); 175 | end; 176 | 177 | end; 178 | WritePage(False or AIsLastData); 179 | {if AIsLastData then 180 | FinishEncode;} 181 | end; 182 | 183 | procedure TPAOggVorbisEncoderLink.EndOfData; 184 | begin 185 | FinishEncode; 186 | inherited EndOfData; 187 | end; 188 | 189 | procedure TPAOggVorbisEncoderLink.SetStream(AValue: TStream); 190 | begin 191 | inherited SetStream(AValue); 192 | if Assigned(AValue) then 193 | begin 194 | FSerialNumber:=1; 195 | vorbis_comment_init(FComment); 196 | end; 197 | end; 198 | 199 | constructor TPAOggVorbisEncoderLink.Create(AStream: TStream; AOwnsStream: Boolean); 200 | begin 201 | inherited Create(AStream, AOwnsStream); 202 | FFormat:=afRaw; 203 | end; 204 | 205 | function TPAOggVorbisEncoderLink.GetWrittenSeconds: QWord; 206 | begin 207 | if FWrittenBytes = 0 then 208 | Exit(0); 209 | Result := FWrittenBytes div BytesPerSample(DefaultAudioFormat) div Channels div SamplesPerSecond; 210 | end; 211 | 212 | procedure TPAOggVorbisEncoderLink.AddComment(TagName: Utf8String; Content: Utf8String); 213 | begin 214 | vorbis_comment_add_tag(FComment,PChar(TagName),PChar(Content)); 215 | end; 216 | 217 | initialization 218 | PARegister(partEncoder, TPAOggVorbisEncoderLink, 'OGG/Vorbis', '.ogg', 'OggS'); 219 | end. 220 | 221 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_flac.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_flac; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, pa_base, flac_classes, pa_register, pa_stream, paio_messagequeue; 22 | 23 | type 24 | TPAFlacSource = class (TPAStreamSource, IPAStream, IPAPlayable) 25 | protected 26 | procedure SetStream(AValue: TStream); override; 27 | procedure HandleMessage(var AMsg: TPAIOMessage); override; 28 | private 29 | FFlac: TFlacStreamDecoder; 30 | FIgnore: Boolean; 31 | function HandleData(Sender: TFlacStreamDecoder; ASamples: Integer; AChannels: Integer; AChannelData: PPLongInt): Boolean; 32 | // IPAPlayable 33 | function CanSeek: Boolean; 34 | function GetPosition: Double; 35 | procedure SetPosition(AValue: Double); 36 | function GetMaxPosition: Double; 37 | 38 | procedure InternalSetPosition(APosition: Double); 39 | protected 40 | function InternalOutputToDestination: Boolean; override; 41 | public 42 | procedure Play; 43 | procedure Pause; 44 | procedure Stop; 45 | property Position: Double read GetPosition write SetPosition; 46 | property MaxPosition: Double read GetMaxPosition; 47 | property Stream; 48 | end; 49 | 50 | implementation 51 | 52 | uses 53 | paio_types; 54 | 55 | { TPAFlacSource } 56 | 57 | procedure TPAFlacSource.SetStream(AValue: TStream); 58 | begin 59 | if Assigned(FFlac) then 60 | FreeAndNil(FFlac); 61 | 62 | inherited SetStream(AValue); 63 | 64 | if not Assigned(FStream) then 65 | Exit; 66 | 67 | FFlac := TFlacStreamDecoder.Create(FStream, False); 68 | FFlac.OnOutput:=@HandleData; 69 | FFlac.ProcessUntilEndOfMetadata; 70 | 71 | Channels:=FFlac.Channels; 72 | SamplesPerSecond:=FFlac.SampleRate; 73 | case FFlac.BitsPerSample of 74 | 16: Format:=afS16; 75 | else 76 | raise Exception.Create('unsupported flac data type'); 77 | end; 78 | end; 79 | 80 | procedure TPAFlacSource.HandleMessage(var AMsg: TPAIOMessage); 81 | begin 82 | case AMsg.Message of 83 | PAM_Seek: 84 | begin 85 | InternalSetPosition(AMsg.Data); 86 | end; 87 | end; 88 | end; 89 | 90 | function TPAFlacSource.HandleData(Sender: TFlacStreamDecoder; ASamples: Integer; AChannels: Integer; AChannelData: PPLongInt): Boolean; 91 | var 92 | ChannelsData: array[0..7] of SmallInt; // 16 bit signed. max 8 channels 93 | i, j: Integer; 94 | Msg: TPAIOMessage; 95 | begin 96 | Result := True; 97 | if FIgnore then 98 | Exit; 99 | if FMsgQueue.HasMessage then 100 | begin 101 | Msg := FMsgQueue.PopMessage; 102 | 103 | case Msg.Message of 104 | PAM_Seek: 105 | begin 106 | InternalSetPosition(Msg.Data); 107 | Msg.Free; 108 | end 109 | else 110 | FMsgQueue.InsertBefore([Msg.Message], Msg); 111 | end; 112 | 113 | end; 114 | 115 | for i := 0 to ASamples-1 do 116 | begin 117 | // plex channels 118 | for j := 0 to AChannels-1 do 119 | begin 120 | ChannelsData[j] := AChannelData[j][i]; 121 | end; 122 | WriteToBuffer(ChannelsData[0],SizeOf(Smallint)*AChannels, False); 123 | end; 124 | end; 125 | 126 | function TPAFlacSource.CanSeek: Boolean; 127 | begin 128 | Result := True; 129 | end; 130 | 131 | function TPAFlacSource.GetPosition: Double; 132 | begin 133 | if not Assigned(FFlac) then 134 | Exit(0); 135 | Result := FFlac.DecodedSamplePosition / SamplesPerSecond; 136 | end; 137 | 138 | procedure TPAFlacSource.SetPosition(AValue: Double); 139 | begin 140 | FMsgQueue.PostMessage(PAM_Seek, AValue); 141 | end; 142 | 143 | function TPAFlacSource.GetMaxPosition: Double; 144 | begin 145 | REsult := FFlac.TotalSamples / SamplesPerSecond; 146 | end; 147 | 148 | procedure TPAFlacSource.InternalSetPosition(APosition: Double); 149 | begin 150 | FIgnore:=True; 151 | try 152 | FFlac.Flush; 153 | FFlac.SeekAbsolute(Trunc(APosition * SamplesPerSecond)); 154 | finally 155 | FIgnore:=False; 156 | end; 157 | end; 158 | 159 | function TPAFlacSource.InternalOutputToDestination: Boolean; 160 | begin 161 | Result := FFlac.ProcessSingle; 162 | if not Result or (FFlac.State = fsdsEndOfStream) then 163 | begin 164 | Result := False; 165 | FFlac.Flush; 166 | SignalDestinationsDone; 167 | end; 168 | end; 169 | 170 | procedure TPAFlacSource.Play; 171 | begin 172 | 173 | end; 174 | 175 | procedure TPAFlacSource.Pause; 176 | begin 177 | 178 | end; 179 | 180 | procedure TPAFlacSource.Stop; 181 | begin 182 | 183 | end; 184 | 185 | initialization 186 | PARegister(partDecoder, TPAFlacSource, 'FLAC', '.flac', 'fLaC', 4); 187 | end. 188 | 189 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_ladspa.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_ladspa; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, 22 | pa_base, ladspa_classes; 23 | 24 | type 25 | 26 | { TPALADSPALink } 27 | 28 | TPALADSPALink = class(TPAAudioLink, IPAAudioInformation) 29 | private 30 | FInstance: TLADSPAInstance; 31 | FInited: Boolean; 32 | procedure InitData; 33 | protected 34 | function InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; override; 35 | procedure SignalDestinationsDone; override; 36 | public 37 | constructor Create(AInstance: TLADSPAInstance); 38 | destructor Destroy; override; 39 | property LADSPA: TLADSPAInstance read FInstance; 40 | end; 41 | 42 | implementation 43 | 44 | { TPALADSPALink } 45 | 46 | procedure TPALADSPALink.InitData; 47 | begin 48 | FInited:=True; 49 | FInstance.Reset; // starting new data stream 50 | end; 51 | 52 | function TPALADSPALink.InternalProcessData(const AData; ACount: Int64; 53 | AIsLastData: Boolean): Int64; 54 | var 55 | ChannelData: TChannelArray; 56 | InputPorts: TLADSPAInstance.TPortArray; 57 | OutputPorts: TLADSPAInstance.TPortArray; 58 | i: Integer; 59 | OutputData: TChannelArray; 60 | PlexedData: TSingleArray; 61 | SamplesPerChannel: Integer; 62 | begin 63 | if not FInited then 64 | InitData; 65 | 66 | ChannelData := SplitChannels(PSingle(@AData), ACount div SizeOf(Single), Channels); 67 | SamplesPerChannel := Length(ChannelData[0]); 68 | InputPorts := FInstance.AudioInputs; 69 | OutputPorts := FInstance.AudioOutputs; 70 | 71 | SetLength(OutputData, Channels); 72 | 73 | for i := 0 to High(InputPorts) do 74 | InputPorts[i].SetValue(@ChannelData[i][0]); 75 | 76 | for i := 0 to High(OutputPorts) do 77 | begin 78 | // allocate output data the same size 79 | SetLength(OutputData[i], SamplesPerChannel); 80 | OutputPorts[i].SetValue(@OutputData[i][0]); 81 | end; 82 | 83 | FInstance.Run(SamplesPerChannel); 84 | 85 | PlexedData := JoinChannels(OutputData); 86 | 87 | WriteToBuffer(PlexedData[0], Length(PlexedData)*SizeOf(Single), AIsLastData); 88 | end; 89 | 90 | procedure TPALADSPALink.SignalDestinationsDone; 91 | begin 92 | inherited SignalDestinationsDone; 93 | end; 94 | 95 | constructor TPALADSPALink.Create(AInstance: TLADSPAInstance); 96 | begin 97 | Inherited Create; 98 | FInstance := AInstance; 99 | end; 100 | 101 | destructor TPALADSPALink.Destroy; 102 | begin 103 | inherited Destroy; 104 | end; 105 | 106 | end. 107 | 108 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_lists.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_lists; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils; 22 | 23 | type 24 | PPAFifoItem = ^TPAFifoItem; 25 | TPAFifoItem = record 26 | Item: Pointer; 27 | Next: PPAFifoItem; 28 | end; 29 | 30 | { TPAFifoList } 31 | 32 | TPAFifoList = class 33 | private 34 | FCrit: TRTLCriticalSection; 35 | FFirst: PPAFifoItem; 36 | FLast: PPAFifoItem; 37 | FCount: Integer; 38 | public 39 | procedure AddObject(AObject: TObject); 40 | function GetObject: TObject; 41 | procedure AddItem(AItem: Pointer); 42 | function GetItem: Pointer; 43 | function Count: Integer; 44 | function Contains(AItem: Pointer): Boolean; 45 | constructor Create; 46 | destructor Destroy; override; 47 | end; 48 | 49 | implementation 50 | 51 | { TPAFifoList } 52 | 53 | procedure TPAFifoList.AddObject(AObject: TObject); 54 | begin 55 | AddItem(Pointer(AObject)); 56 | end; 57 | 58 | function TPAFifoList.GetObject: TObject; 59 | begin 60 | Result:= TObject(GetItem); 61 | end; 62 | 63 | procedure TPAFifoList.AddItem(AItem: Pointer); 64 | var 65 | Tmp: PPAFifoItem; 66 | begin 67 | if AItem = nil then 68 | begin 69 | WriteLn('Added nil item to list!'); 70 | Exit; 71 | end; 72 | New(Tmp); 73 | Tmp^.Item:=AItem; 74 | Tmp^.Next:=nil; 75 | try 76 | EnterCriticalsection(FCrit); 77 | if FFirst = nil then 78 | FFirst := Tmp 79 | else 80 | FLast^.Next := Tmp; 81 | FLast := Tmp; 82 | Inc(FCount); 83 | finally 84 | LeaveCriticalsection(FCrit); 85 | end; 86 | end; 87 | 88 | function TPAFifoList.GetItem: Pointer; 89 | var 90 | Tmp: PPAFifoItem; 91 | begin 92 | Result := nil; 93 | try 94 | EnterCriticalsection(FCrit); 95 | if FFirst <> nil then 96 | begin 97 | Tmp := FFirst; 98 | Result := Tmp^.Item; 99 | FFirst := Tmp^.Next; 100 | 101 | // check if we just used the last item in the list 102 | if Tmp = FLast then 103 | FLast := nil; 104 | Dec(FCount); 105 | Dispose(Tmp); 106 | 107 | end; 108 | finally 109 | LeaveCriticalsection(FCrit); 110 | end; 111 | end; 112 | 113 | function TPAFifoList.Count: Integer; 114 | begin 115 | Result := FCount; 116 | end; 117 | 118 | function TPAFifoList.Contains(AItem: Pointer): Boolean; 119 | var 120 | Tmp: PPAFifoItem; 121 | begin 122 | Result := False; 123 | EnterCriticalsection(FCrit); 124 | try 125 | Tmp := FFirst; 126 | while Tmp <> nil do 127 | begin 128 | if Tmp^.Item = AItem then 129 | Exit(True); 130 | Tmp := Tmp^.Next; 131 | end; 132 | finally 133 | LeaveCriticalsection(FCrit); 134 | end; 135 | end; 136 | 137 | constructor TPAFifoList.Create; 138 | begin 139 | InitCriticalSection(FCrit); 140 | FFirst:=nil; 141 | FLast:=nil; 142 | end; 143 | 144 | destructor TPAFifoList.Destroy; 145 | var 146 | Tmp: PPAFifoItem; 147 | begin 148 | FLast:=nil; 149 | while FFirst <> nil do 150 | begin 151 | Tmp := FFirst^.Next; 152 | Dispose(FFirst); 153 | FFirst:=Tmp; 154 | end; 155 | 156 | DoneCriticalsection(FCrit); 157 | inherited Destroy; 158 | end; 159 | 160 | end. 161 | 162 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_m4a.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2019 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_m4a; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, 22 | pa_base, 23 | pa_register, 24 | pa_stream, 25 | paio_faad2, 26 | paio_messagequeue, 27 | quicktimecontainer, 28 | quicktimeatoms, 29 | mp4codec; 30 | 31 | type 32 | 33 | { TPAM4ADecoderSource } 34 | 35 | TPAM4ADecoderSource = class(TPAStreamSource, IPAPlayable, IPAStream) 36 | private 37 | FInited: Boolean; 38 | FDecoder: TAACDecoder; 39 | FContainer: TQuicktimeContainer; 40 | FSampleIndex: Integer; 41 | FSampleTable: array of LongWord; 42 | FChunkOffset: array of LongWord; 43 | FChunkTable: TstscAtom; 44 | FTimeToSample: TsttsAtom; 45 | FFrame: TNeAACDecFrameInfo; 46 | FBuffer: array[0..1023] of Byte; 47 | FSampleSize: Integer; 48 | FCodec: TMP4Codec; 49 | function ReadNextSample: Boolean; 50 | procedure InitAudio; 51 | procedure DeInitAudio; 52 | protected 53 | procedure SetStream(AValue: TStream); override; 54 | function InternalOutputToDestination: Boolean; override; 55 | procedure HandleMessage(var AMsg: TPAIOMessage); override; 56 | function GetPosition: Double; 57 | procedure SetPosition(AValue: Double); 58 | function GetMaxPosition: Double; 59 | public 60 | function CanSeek: Boolean; 61 | procedure Play; 62 | procedure Pause; 63 | procedure Stop; 64 | property Position: Double read GetPosition write SetPosition; 65 | property MaxPosition: Double read GetMaxPosition; 66 | constructor Create(AStream: TStream; AOwnsStream: Boolean=True); override; 67 | end; 68 | 69 | implementation 70 | 71 | { TPAM4ADecoderSource } 72 | 73 | function TPAM4ADecoderSource.ReadNextSample: Boolean; 74 | var 75 | i: Integer; 76 | lFirstIndex, lChunkIndex: DWord; 77 | lSamplesOffset: DWord = 0; 78 | begin 79 | Result := FSampleIndex < Length(FSampleTable); 80 | if not Result then 81 | Exit; 82 | //WriteLn('Sample index: ', FSampleIndex); 83 | FSampleSize := FSampleTable[FSampleIndex]; 84 | //WriteLn('Sample Size: ', FSampleSize); 85 | 86 | lChunkIndex := FChunkTable.SampleIndexToChunkIndex(1, FSampleIndex, lFirstIndex); 87 | 88 | for i := lFirstIndex to FSampleIndex-1 do 89 | Inc(lSamplesOffset, FSampleTable[i]); 90 | 91 | FContainer.Stream.Position:=FChunkOffset[lChunkIndex] + lSamplesOffset; 92 | FContainer.Stream.Read(FBuffer[0], FSampleSize); 93 | 94 | if Assigned(FCodec) then 95 | FCodec.Filter(@FBuffer[0], FSampleSize); 96 | Inc(FSampleIndex); 97 | {Write(Pred(FSampleIndex), ': '); 98 | for i := 0 to 7 do 99 | Write('0x'+HexStr(FBuffer[i], 2)+' '); 100 | WriteLn;} 101 | end; 102 | 103 | procedure TPAM4ADecoderSource.InitAudio; 104 | var 105 | lmp4aAtom: TSoundSampleDecriptionAtom; 106 | lesds: TesdsAtom; 107 | 108 | lBuf: Array[0..254] of Byte; 109 | lConfig: Word; 110 | lSize: Integer; 111 | lSampleTable: TstszAtom; 112 | lSampleOffSet: TstcoAtom; 113 | lDecoderConfig: PNeAACDecConfiguration; 114 | lmp4ASC: Tmp4AudioSpecificConfig; 115 | lstsdAtom: TstsdAtom; 116 | lCodecClass: TMP4CodecClass; 117 | begin 118 | if FInited then 119 | Exit; 120 | FInited:=True; 121 | FContainer := TQuicktimeContainer.Create(FStream, False); 122 | if not FContainer.IsValidFile then 123 | begin 124 | DeInitAudio; 125 | Exit; 126 | end; 127 | 128 | FChunkTable := TstscAtom(FContainer.Atoms.FindAtom('moov/trak/mdia/minf/stbl/stsc')); 129 | FTimeToSample := TsttsAtom(FContainer.Atoms.FindAtom('moov/trak/mdia/minf/stbl/stts')); 130 | 131 | lSampleTable := TstszAtom(FContainer.Atoms.FindAtom('moov/trak/mdia/minf/stbl/stsz')); 132 | SetLength(FSampleTable, lSampleTable.SampleCount); 133 | //Writeln(Length(FSampleTable), ' samples'); 134 | lSampleTable.CopySampleTable(@FSampleTable[0]); 135 | 136 | lSampleOffSet := TstcoAtom(FContainer.Atoms.FindAtom('moov/trak/mdia/minf/stbl/stco')); 137 | SetLength(FChunkOffset, lSampleOffSet.Count); 138 | lSampleOffSet.CopyTable(@FChunkOffset[0]); 139 | 140 | lstsdAtom := TstsdAtom(FContainer.Atoms.FindAtom('moov/trak/mdia/minf/stbl/stsd')); 141 | 142 | 143 | lmp4aAtom := TSoundSampleDecriptionAtom(lstsdAtom.Atoms.Atom[0]); 144 | //WriteLn(lmp4aAtom.ClassName); 145 | //WriteLn(lmp4aAtom.AtomName.Chars); 146 | 147 | if MP4LookupCodec(lmp4aAtom.AtomName, lCodecClass) then 148 | begin 149 | FCodec := lCodecClass.Create(lmp4aAtom); 150 | end; 151 | 152 | if lmp4aAtom = nil then 153 | begin 154 | DeInitAudio; 155 | Exit; 156 | end; 157 | 158 | lesds := TesdsAtom(lmp4aAtom.Atoms.FindAtom('esds')); 159 | lConfig:=lesds.DecoderConfig; 160 | 161 | 162 | 163 | FDecoder := TAACDecoder.Create; 164 | lDecoderConfig := FDecoder.Config; 165 | //WriteLn(FDecoder.GetErroMessage(FDecoder.LastResult));; 166 | lDecoderConfig^.outputFormat:=ord(FAAD_FMT_FLOAT); 167 | FDecoder.Config := lDecoderConfig; 168 | 169 | FDecoder.Init2(@lConfig, 2); 170 | 171 | FDecoder.AudioSpecificConfig(@lConfig, 2, @lmp4ASC); 172 | 173 | //WriteLn(FDecoder.Channels); 174 | Channels:=FDecoder.Channels; 175 | SamplesPerSecond:=FDecoder.SampleRate; 176 | 177 | end; 178 | 179 | procedure TPAM4ADecoderSource.DeInitAudio; 180 | begin 181 | if not FInited then 182 | Exit; 183 | FInited:=False; 184 | if Assigned(FContainer) then 185 | begin 186 | Freeandnil(FContainer); 187 | FreeAndNil(FDecoder); 188 | end; 189 | end; 190 | 191 | procedure TPAM4ADecoderSource.SetStream(AValue: TStream); 192 | begin 193 | if FStream=AValue then Exit; 194 | if FStream <> nil then 195 | begin 196 | StopData; 197 | DeInitAudio; 198 | end; 199 | inherited SetStream(AValue); 200 | end; 201 | 202 | function TPAM4ADecoderSource.InternalOutputToDestination: Boolean; 203 | var 204 | lRes: Pointer; 205 | lSampleBuffer: array[0..2047] of Byte; 206 | begin 207 | if not FInited then 208 | InitAudio; 209 | 210 | 211 | Result := ReadNextSample; 212 | 213 | if Result then 214 | begin 215 | lRes := FDecoder.Decode(@FFrame, @FBuffer[0], FSampleSize); 216 | if FFrame.error <> 0 then 217 | WriteLn('Error: ', FDecoder.GetErroMessage(FFrame.error)); 218 | 219 | if (FFrame.samples > 0) then 220 | begin 221 | WriteToBuffer(lRes^,FFrame.samples*4, False); 222 | end; 223 | { 224 | 225 | WriteLn(SysUtils.Format('Dec Bytes: %d, data := 0x%s Samples: %d', [FFrame.bytesconsumed, Hexstr(PtrUint(lRes), 16), FFrame.samples])); 226 | //if FFrame.; 227 | if (FFrame.error > 0) or (FFrame.bytesconsumed > 0) or (lRes <> nil) then 228 | begin 229 | 230 | writeln; 231 | end; } 232 | end; 233 | 234 | //sleep(1000); 235 | 236 | 237 | 238 | 239 | 240 | end; 241 | 242 | procedure TPAM4ADecoderSource.HandleMessage(var AMsg: TPAIOMessage); 243 | var 244 | lSample, lSampleIndex: Int64; 245 | lOffset: Integer; 246 | begin 247 | case AMsg.Message of 248 | PAM_Seek: 249 | if FInited then 250 | begin 251 | lSample := Trunc((SamplesPerSecond / Channels) * AMsg.Data); 252 | lSampleIndex := FTimeToSample.FindSampleIndex(lSample, lOffset); 253 | FSampleIndex:=lSampleIndex; 254 | end; 255 | end; 256 | end; 257 | 258 | function TPAM4ADecoderSource.GetPosition: Double; 259 | begin 260 | if not FInited then 261 | Exit(0); 262 | Result := FTimeToSample.FindSampleFromIndex(FSampleIndex) / SamplesPerSecond; 263 | end; 264 | 265 | procedure TPAM4ADecoderSource.SetPosition(AValue: Double); 266 | begin 267 | if not FInited then 268 | Exit; 269 | 270 | FMsgQueue.PostMessage(PAM_Seek, AValue); 271 | end; 272 | 273 | function TPAM4ADecoderSource.GetMaxPosition: Double; 274 | begin 275 | Result := FTimeToSample.TotalSamples / SamplesPerSecond; 276 | WriteLn('Channels: ', Channels, ' SPS: ', SamplesPerSecond); 277 | end; 278 | 279 | function TPAM4ADecoderSource.CanSeek: Boolean; 280 | begin 281 | Result := True; 282 | end; 283 | 284 | procedure TPAM4ADecoderSource.Play; 285 | begin 286 | 287 | end; 288 | 289 | procedure TPAM4ADecoderSource.Pause; 290 | begin 291 | 292 | end; 293 | 294 | procedure TPAM4ADecoderSource.Stop; 295 | begin 296 | 297 | end; 298 | 299 | constructor TPAM4ADecoderSource.Create(AStream: TStream; AOwnsStream: Boolean); 300 | begin 301 | inherited Create(AStream, AOwnsStream); 302 | Format:=afFloat32; 303 | end; 304 | 305 | initialization 306 | PARegister(partDecoder, TPAM4ADecoderSource, 'MP4audio', '.m4a', 'ftyp', 4, 4); // ftyp is at 4 offset 307 | 308 | end. 309 | 310 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_mmdevice.pas: -------------------------------------------------------------------------------- 1 | unit pa_mmdevice; 2 | 3 | {$mode objfpc}{$H+} 4 | {$ifdef windows} 5 | 6 | interface 7 | 8 | uses 9 | Classes, windows, SysUtils, paio_mmdevice, pa_base, pa_register; 10 | 11 | 12 | type 13 | 14 | { TPAMMDestination } 15 | 16 | TPAMMDestination = class(TPAAudioDestination) 17 | private 18 | FEnum: TMMDeviceEnumerator; 19 | FDevice: TMMDevice; 20 | FClient: TAudioClient; 21 | FRender: TAudioRenderClient; 22 | FMixFormat: PWaveFormatEx; 23 | FBufferSize: Integer; 24 | procedure Init; 25 | procedure DeInit; 26 | protected 27 | function InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; override; 28 | 29 | public 30 | constructor Create; override; 31 | destructor Destroy; override; 32 | end; 33 | 34 | implementation 35 | 36 | { TPAMMDestination } 37 | 38 | procedure TPAMMDestination.Init; 39 | var 40 | lFormat: TWaveFormatEx; 41 | begin 42 | if Assigned(FRender) then 43 | Exit; 44 | WriteLn('init'); 45 | 46 | FClient := FDevice.ActivateClient; 47 | FClient.Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 10000000, 0, FMixFormat); 48 | FBufferSize := FClient.BufferSize; 49 | 50 | FRender := FClient.GetRenderClient; 51 | WriteLn('init end'); 52 | end; 53 | 54 | procedure TPAMMDestination.DeInit; 55 | begin 56 | if not Assigned(FRender) then 57 | Exit; 58 | 59 | FClient.Stop; 60 | FreeAndNil(FRender); 61 | 62 | end; 63 | 64 | function TPAMMDestination.InternalProcessData(const AData; ACount: Int64; 65 | AIsLastData: Boolean): Int64; 66 | var 67 | lmmData: PByte; 68 | lFrameCount, lDuration: Integer; 69 | lSpaceAvailable, lGetAmount: DWORD; 70 | lDataSize: DWORD; 71 | lData: PByte; 72 | lStart: Boolean; 73 | begin 74 | Result := ACount; 75 | 76 | if not Assigned(FDevice) then 77 | Exit; 78 | 79 | lStart:=False; 80 | if not Assigned(FRender) then 81 | begin 82 | lStart := True; 83 | Init; 84 | end; 85 | 86 | lData := @AData; 87 | 88 | 89 | // WriteLn('loop'); 90 | while ACount > 0 do 91 | begin 92 | lFrameCount := ACount div BytesPerSample(Format) div Channels; 93 | if lStart then 94 | lSpaceAvailable:=FBufferSize 95 | else 96 | lSpaceAvailable := FClient.CurrentPadding; 97 | 98 | lGetAmount := lSpaceAvailable; 99 | if lSpaceAvailable > lFrameCount then 100 | lGetAmount := lFrameCount; 101 | 102 | if FRender.GetBuffer(lGetAmount, @lmmData) then 103 | begin 104 | lDataSize := lGetAmount * Channels * BytesPerSample(Format); 105 | Move(lData^, lmmData^, lDataSize); 106 | FRender.ReleaseBuffer(lGetAmount, 0); 107 | Inc(lData, lDataSize); 108 | Dec(ACount, lDataSize); 109 | if lStart then 110 | begin 111 | WriteLn('start'); 112 | FClient.start; 113 | lStart:=False; 114 | end; 115 | lDuration := lGetAmount * (1000 div SamplesPerSecond); 116 | Sleep(1);//lDuration div 2); 117 | end; 118 | end; 119 | //sleep(100); 120 | // WriteLn('loop end'); 121 | 122 | if AIsLastData then 123 | DeInit; 124 | end; 125 | 126 | constructor TPAMMDestination.Create; 127 | begin 128 | inherited Create; 129 | FEnum := TMMDeviceEnumerator.Create; 130 | FDevice := FEnum.GetDefaultAudioEndpoint(eRender, eMultimedia); 131 | FClient := FDevice.ActivateClient; 132 | FClient.GetMixFormat(FMixFormat); 133 | Channels := FMixFormat^.nChannels; 134 | SamplesPerSecond := FMixFormat^.nSamplesPerSec; 135 | Format:= afFloat32; 136 | end; 137 | 138 | destructor TPAMMDestination.Destroy; 139 | begin 140 | DeInit; 141 | FreeAndNil(FClient); 142 | FreeAndNil(FDevice); 143 | FreeAndNil(FEnum); 144 | inherited Destroy; 145 | Writeln('Destroyed Device'); 146 | end; 147 | 148 | initialization 149 | PARegister(partDeviceOut, TPAMMDestination, 'MMDevice'); 150 | 151 | {$else} 152 | interface 153 | implementation 154 | {$endif} 155 | end. 156 | 157 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_noiseremoval.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_noiseremoval; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, pa_base, audacity_noiseremoval, paio_channelhelper; 22 | 23 | type 24 | { TPANoiseRemovalLink } 25 | 26 | TPANoiseRemovalLink = class(TPAAudioLink, IPAAudioInformation, IPAIODataIOInterface) 27 | private 28 | FInited: Boolean; 29 | FNoise: array of TNoiseRemoval; 30 | FHelper: TPAIOChannelHelper; 31 | FIsLast: Boolean; 32 | procedure InitData; 33 | procedure NoiseRemovalDone(ASender: TObject; AData: PSingle; ASampleCount: Integer); 34 | procedure WriteDataIO(ASender: IPAIODataIOInterface; AData: PSingle; ASamples: Integer); 35 | protected 36 | function InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; override; 37 | procedure SignalDestinationsDone; override; 38 | function GetFormat: TPAAudioFormat; override; 39 | public 40 | constructor Create; override; 41 | destructor Destroy; override; 42 | 43 | // set this before we staret processing but after datasource is assigned. 44 | procedure SetNoiseProfile(AChannel: Integer; AData: PSingle; ASampleCount: Integer); 45 | end; 46 | 47 | implementation 48 | 49 | type 50 | 51 | { TPANoiseRemval } 52 | 53 | TPANoiseRemoval = class(TNoiseRemoval, IPAIODataIOInterface) 54 | procedure WriteDataIO(ASender: IPAIODataIOInterface; AData: PSingle; ASamples: Integer); 55 | end; 56 | 57 | { TPANoiseRemval } 58 | 59 | procedure TPANoiseRemoval.WriteDataIO(ASender: IPAIODataIOInterface; AData: PSingle; ASamples: Integer); 60 | begin 61 | Process(AData, ASamples, False); 62 | end; 63 | 64 | { TPANoiseRemovalLink } 65 | 66 | procedure TPANoiseRemovalLink.InitData; 67 | var 68 | i: Integer; 69 | begin 70 | if FInited then 71 | Exit; 72 | 73 | FInited := True; 74 | if Assigned(FHelper) then 75 | FreeAndNil(FHelper); 76 | FHelper := TPAIOChannelHelper.Create(Self); 77 | 78 | SetLength(FNoise, Channels); 79 | for i := 0 to Channels-1 do 80 | begin 81 | FNoise[i] := TPANoiseRemoval.Create; 82 | FHelper.Outputs.Add(FNoise[i] as IPAIODataIOInterface); 83 | //FNoise[i].Level:=30; //does nothing! 84 | //FNoise[i].Sensitivity:=20;//3.95; 85 | //FNoise[i].Sensitivity:=9.95; 86 | // FNoise[i].Gain:=-48; 87 | // FNoise[i].AttackDecayTime:=1000; 88 | FNoise[i].Init(SamplesPerSecond); 89 | FNoise[i].WriteProc:=@NoiseRemovalDone; 90 | end; 91 | 92 | end; 93 | 94 | procedure TPANoiseRemovalLink.NoiseRemovalDone(ASender: TObject; AData: PSingle; ASampleCount: Integer); 95 | begin 96 | (FHelper as IPAIODataIOInterface).WriteDataIO(ASender as IPAIODataIOInterface, AData, ASampleCount); 97 | end; 98 | 99 | procedure TPANoiseRemovalLink.WriteDataIO(ASender: IPAIODataIOInterface; AData: PSingle; ASamples: Integer); 100 | begin 101 | // write plexed output from FHelper to buffer. 102 | WriteToBuffer(AData^, ASamples*SizeOf(Single), FIsLast); 103 | end; 104 | 105 | function TPANoiseRemovalLink.InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; 106 | var 107 | i: Integer; 108 | begin 109 | FHelper.Write(PSingle(@AData), ACount div SizeOf(Single)); 110 | FIsLast:=AIsLastData; 111 | if FIsLast then 112 | for i := Low(FNoise) to High(FNoise) do 113 | FNoise[i].Flush; 114 | end; 115 | 116 | procedure TPANoiseRemovalLink.SignalDestinationsDone; 117 | begin 118 | inherited SignalDestinationsDone; 119 | end; 120 | 121 | function TPANoiseRemovalLink.GetFormat: TPAAudioFormat; 122 | begin 123 | Result:=afFloat32; 124 | end; 125 | 126 | constructor TPANoiseRemovalLink.Create; 127 | begin 128 | inherited Create; 129 | Format := afFloat32; 130 | end; 131 | 132 | destructor TPANoiseRemovalLink.Destroy; 133 | begin 134 | FHelper.Free; 135 | inherited Destroy; 136 | end; 137 | 138 | procedure TPANoiseRemovalLink.SetNoiseProfile(AChannel: Integer; AData: PSingle; 139 | ASampleCount: Integer); 140 | var 141 | Profile: TSingleArray; 142 | begin 143 | InitData; 144 | FNoise[AChannel].Process(AData, ASampleCount, True); 145 | 146 | Profile := FNoise[AChannel].NoiseProfile; 147 | // call init again because inited is false after Setting the noise profile. 148 | FNoise[AChannel].Init(SamplesPerSecond); 149 | end; 150 | 151 | end. 152 | 153 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_ogg_opus.pas: -------------------------------------------------------------------------------- 1 | unit pa_ogg_opus; 2 | 3 | {$mode objfpc}{$H+} 4 | 5 | interface 6 | 7 | uses 8 | Classes, SysUtils, 9 | pa_base, 10 | pa_register, 11 | pa_stream, 12 | paio_messagequeue, 13 | paio_ogg_opus; 14 | type 15 | 16 | { TPAOggOpusDecoderSource } 17 | 18 | TPAOggOpusDecoderSource = class(TPAStreamSource, IPAPlayable, IPAStream) 19 | private 20 | FInited: Boolean; 21 | FOpus: TOggOpusDecoder; 22 | FBuffer: array of Byte; 23 | function InitOpus: Boolean; 24 | procedure DeInitOpus; 25 | protected 26 | procedure SetStream(AValue: TStream); override; 27 | function InternalOutputToDestination: Boolean; override; 28 | procedure SignalDestinationsDone; override; 29 | procedure HandleMessage(var AMsg: TPAIOMessage); override; 30 | // IPAPlayable 31 | function CanSeek: Boolean; 32 | function GetPosition: Double; 33 | procedure SetPosition(AValue: Double); 34 | function GetMaxPosition: Double; 35 | function GetSamplesPerSecond: Integer; override; 36 | public 37 | constructor Create(AStream: TStream; AOwnsStream: Boolean); override; 38 | procedure InitValues; 39 | //IPAPlayable 40 | procedure Play; 41 | procedure Pause; 42 | procedure Stop; 43 | //IStreamSource 44 | property Stream; 45 | //IPAPlayable 46 | property Position: Double read GetPosition write SetPosition; 47 | property MaxPosition: Double read GetMaxPosition; 48 | 49 | end; 50 | 51 | implementation 52 | 53 | uses paio_opus; 54 | 55 | { TPAOggOpusDecoderSource } 56 | 57 | function TPAOggOpusDecoderSource.InitOpus: Boolean; 58 | begin 59 | Result := False; 60 | if FStream = nil then 61 | Exit; 62 | 63 | if FInited then 64 | Exit; 65 | 66 | try 67 | FOpus := TOggOpusDecoder.Create(FStream); 68 | except 69 | // invalid file 70 | FInited := False; 71 | FOpus := nil; // shouldn't be set...just to be clear 72 | end; 73 | FOpus.InitDecoder; 74 | Channels:=FOpus.Channels; 75 | 76 | SetLength(FBuffer, TOpusDecoder.GetSize(Channels)); 77 | 78 | SamplesPerSecond:=48000; 79 | Format:=afFloat32; 80 | FInited:=True; 81 | Result := True; 82 | end; 83 | 84 | procedure TPAOggOpusDecoderSource.DeInitOpus; 85 | begin 86 | if not FInited then 87 | Exit; 88 | FInited:=False; 89 | SetLength(FBuffer, 0); 90 | FreeAndNil(FOpus); 91 | end; 92 | 93 | procedure TPAOggOpusDecoderSource.SetStream(AValue: TStream); 94 | begin 95 | inherited SetStream(AValue); 96 | end; 97 | 98 | function TPAOggOpusDecoderSource.InternalOutputToDestination: Boolean; 99 | var 100 | ReadSamples: Integer; 101 | begin 102 | Result := False; 103 | if not FInited then 104 | if not InitOpus then 105 | Exit; 106 | 107 | ReadSamples := FOpus.DecodePacket(@FBuffer[0], Length(FBuffer), True); 108 | 109 | Result := ReadSamples > 0; 110 | 111 | if Result then 112 | begin 113 | WriteToBuffer(FBuffer[0], ReadSamples * SizeOf(Single) * Channels , ReadSamples<=0); 114 | end 115 | else 116 | SignalDestinationsDone; 117 | end; 118 | 119 | procedure TPAOggOpusDecoderSource.SignalDestinationsDone; 120 | begin 121 | inherited SignalDestinationsDone; 122 | end; 123 | 124 | procedure TPAOggOpusDecoderSource.HandleMessage(var AMsg: TPAIOMessage); 125 | var 126 | lPosition: QWord; 127 | begin 128 | case AMsg.Message of 129 | PAM_Seek: 130 | begin 131 | if FInited then 132 | begin 133 | lPosition:=QWord(AMsg.Data); 134 | // will constrain it 135 | FOpus.SamplePosition:=lPosition; 136 | end; 137 | end; 138 | end; 139 | end; 140 | 141 | function TPAOggOpusDecoderSource.CanSeek: Boolean; 142 | begin 143 | Result := True; 144 | end; 145 | 146 | function TPAOggOpusDecoderSource.GetPosition: Double; 147 | begin 148 | if not FInited then 149 | Result := 0.0 150 | else 151 | Result := FOpus.SamplePosition / SamplesPerSecond; 152 | end; 153 | 154 | procedure TPAOggOpusDecoderSource.SetPosition(AValue: Double); 155 | begin 156 | if not FInited then 157 | Exit; 158 | FMsgQueue.PostMessage(PAM_Seek, Trunc(AValue*SamplesPerSecond)); 159 | end; 160 | 161 | function TPAOggOpusDecoderSource.GetMaxPosition: Double; 162 | begin 163 | if not FInited then 164 | Result := 0.0 165 | else 166 | Result := FOpus.TotalSamples / SamplesPerSecond; 167 | end; 168 | 169 | function TPAOggOpusDecoderSource.GetSamplesPerSecond: Integer; 170 | begin 171 | // technically this can be multiple things. 48000, 24000, 12000 etc. 172 | Result:=48000; 173 | end; 174 | 175 | constructor TPAOggOpusDecoderSource.Create(AStream: TStream; 176 | AOwnsStream: Boolean); 177 | begin 178 | inherited Create(AStream, AOwnsStream); 179 | Format := afFloat32 180 | end; 181 | 182 | procedure TPAOggOpusDecoderSource.InitValues; 183 | var 184 | Tmp: TOggOpusDecoder; 185 | begin 186 | Tmp := TOggOpusDecoder.Create(Stream); 187 | try 188 | FChannels:=Tmp.Channels; 189 | Format:=afFloat32; 190 | finally 191 | Tmp.Free; 192 | end; 193 | end; 194 | 195 | procedure TPAOggOpusDecoderSource.Play; 196 | begin 197 | 198 | end; 199 | 200 | procedure TPAOggOpusDecoderSource.Pause; 201 | begin 202 | 203 | end; 204 | 205 | procedure TPAOggOpusDecoderSource.Stop; 206 | begin 207 | 208 | end; 209 | 210 | end. 211 | 212 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_process.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_process; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, pa_base, process; 22 | 23 | type 24 | 25 | { TPAProcessSource } 26 | 27 | TPAProcessSource = class; 28 | 29 | //useful to continue generating data for output from multiple commands 30 | TPAProcessEndedEvent = procedure (ASource: TPAProcessSource; var ARunAgain: Boolean)of Object; 31 | 32 | TPAProcessSource = class(TPAAudioSource, IPAAudioInformation) 33 | protected 34 | FProcess: TProcess; 35 | //used by DoProcessEnded; 36 | FRunAgain: Boolean; 37 | FAllowProcessAccess: Boolean; 38 | procedure RunProcess; 39 | procedure BeforeExecuteLoop; override; 40 | function InternalOutputToDestination: Boolean; override; 41 | private 42 | FOnProcessEnded: TPAProcessEndedEvent; 43 | function GetProcess: TProcess; 44 | // IPAAudioInformation 45 | procedure DoProcessEnded; 46 | public 47 | constructor Create; override; 48 | destructor Destroy; override; 49 | property Process: TProcess read GetProcess; // returns nil once started 50 | property OnProcessEnded: TPAProcessEndedEvent read FOnProcessEnded write FOnProcessEnded; 51 | end; 52 | 53 | implementation 54 | 55 | { TPAProcessSource } 56 | 57 | procedure TPAProcessSource.RunProcess; 58 | begin 59 | //WriteLn('Running Process'); 60 | FProcess.Execute; 61 | end; 62 | 63 | procedure TPAProcessSource.BeforeExecuteLoop; 64 | begin 65 | inherited BeforeExecuteLoop; 66 | RunProcess; 67 | end; 68 | 69 | function TPAProcessSource.InternalOutputToDestination: Boolean; 70 | var 71 | Buf: array[0..AUDIO_BUFFER_SIZE-1] of byte; 72 | RCount: Integer; 73 | begin 74 | while (FProcess.Stderr.NumBytesAvailable > 0) do 75 | begin 76 | // clear stderr 77 | RCount := FProcess.Stderr.NumBytesAvailable; 78 | if RCount > SizeOf(Buf) then 79 | RCount := SizeOf(Buf); 80 | FProcess.Stderr.Read(Buf, RCount); 81 | Write(StdErr,Copy(PChar(@Buf),0,RCount)); 82 | end; 83 | 84 | if FProcess.Running or (FProcess.Output.NumBytesAvailable > 0) then 85 | begin 86 | RCount := FProcess.Output.NumBytesAvailable; 87 | //WriteLn('Read ', RCount); 88 | if RCount > SizeOf(Buf) then 89 | RCount := SizeOf(Buf); 90 | RCount := FProcess.Output.Read(Buf, SizeOf(Buf)); 91 | if RCount > 0 then 92 | WriteToBuffer(Buf, RCount, False); 93 | end; 94 | 95 | // still data left so call again 96 | if (FProcess.Output.NumBytesAvailable > 0) then 97 | Exit(True); 98 | 99 | // process is still running so there might be more data 100 | if FProcess.Running then 101 | Exit(True) 102 | else 103 | ;//WriteLn('Exit Status = ', FProcess.ExitStatus); 104 | 105 | // last chance for more data. check to run process again 106 | if Assigned(FOnProcessEnded) then 107 | begin 108 | Synchronize(@DoProcessEnded); 109 | if FRunAgain then 110 | begin 111 | // if the callback want's us to run the process again with possibly new 112 | // commands, do it to keep generating data 113 | RunProcess; 114 | Exit(True); 115 | end; 116 | end; 117 | 118 | // there is no data and no running process so we're done 119 | Result := False; 120 | SignalDestinationsDone; 121 | end; 122 | 123 | function TPAProcessSource.GetProcess: TProcess; 124 | begin 125 | // FAllowProcessAccess is set and unset in DoProcessEnded to allow access safely when syncing 126 | 127 | if Working and (not FAllowProcessAccess) then 128 | Exit(nil); 129 | Result := FProcess; 130 | end; 131 | 132 | procedure TPAProcessSource.DoProcessEnded; 133 | begin 134 | // run from synchronize 135 | //WriteLn('Checking want more'); 136 | FAllowProcessAccess:=True; 137 | FRunAgain:=False; 138 | if Assigned(FOnProcessEnded) then 139 | FOnProcessEnded(Self, FRunAgain); 140 | FAllowProcessAccess:=False; 141 | end; 142 | 143 | constructor TPAProcessSource.Create; 144 | begin 145 | inherited Create; 146 | FProcess := TProcess.Create(nil); 147 | FProcess.Options:=[poUsePipes, poNoConsole]; 148 | end; 149 | 150 | destructor TPAProcessSource.Destroy; 151 | begin 152 | if FProcess.Running then 153 | FProcess.Terminate(0); 154 | FProcess.Free; 155 | inherited Destroy; 156 | end; 157 | 158 | end. 159 | 160 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_pulse_simple.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_pulse_simple; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | {$IFDEF USEPULSE} 20 | uses 21 | Classes, SysUtils, pa_base, pa_register, pulse_simple; 22 | 23 | 24 | type 25 | { TPAPulseDestination } 26 | 27 | TPAPulseDestination = class(TPAAudioDestination) 28 | private 29 | FPulse: PPASimple; 30 | FInited: Boolean; 31 | procedure Init; 32 | procedure DeInit; 33 | protected 34 | function InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; override; 35 | public 36 | constructor Create; override; 37 | destructor Destroy; override; 38 | end; 39 | {$ENDIF} 40 | 41 | implementation 42 | {$IFDEF USEPULSE} 43 | uses 44 | pulse_error, pulse_def, pulse_sample, ctypes; 45 | 46 | 47 | { TPAPulseDestination } 48 | 49 | procedure TPAPulseDestination.Init; 50 | var 51 | SS: TPASampleSpec; 52 | Info: IPAAudioInformation; 53 | error: cint; 54 | begin 55 | FInited:=True; 56 | Info := DataSource.GetSourceObject as IPAAudioInformation; 57 | SS.Init; 58 | SS.Channels:=Info.Channels; 59 | SS.Rate:=Info.SamplesPerSecond; 60 | SS.Format:=sfFloat32LE; 61 | 62 | //WriteLn('Pulse Channels = ', SS.Channels); 63 | //Writeln('Pulse Rate = ', SS.Rate); 64 | 65 | FPulse:=TPASimple.New(nil,PChar(ParamStr(0)),sdPLAYBACK, nil, 'test', @SS, nil, nil, @error); 66 | if error < 0 then 67 | WriteLn('Error initing pulse data ',pa_strerror(error)); 68 | end; 69 | 70 | procedure TPAPulseDestination.DeInit; 71 | begin 72 | FPulse^.Drain(nil); 73 | FPulse^.Free; 74 | FPulse := nil; 75 | FInited:=False; 76 | EndOfData; 77 | end; 78 | 79 | function TPAPulseDestination.InternalProcessData(const AData; ACount: Int64; 80 | AIsLastData: Boolean): Int64; 81 | var 82 | Error: cint; 83 | begin 84 | if not FInited then 85 | Init; 86 | Result := ACount; 87 | //WriteLn('Writing to pulse'); 88 | FPulse^.Write(@AData, ACount, @error); 89 | if error < 0 then 90 | WriteLn('Error Writin pulse data ',pa_strerror(error)); 91 | if AIsLastData then 92 | DeInit; 93 | end; 94 | 95 | constructor TPAPulseDestination.Create; 96 | begin 97 | BufferPool.AllocateBuffers(4); 98 | inherited Create; 99 | Format := afFloat32; 100 | end; 101 | 102 | 103 | destructor TPAPulseDestination.Destroy; 104 | begin 105 | inherited Destroy; 106 | end; 107 | initialization 108 | PARegister(partDeviceOut, TPAPulseDestination, 'PulseAudio'); 109 | {$ENDIF} 110 | end. 111 | 112 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_register.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_register; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, pa_base, pa_stream; 22 | 23 | type 24 | TPARegisterType = ( 25 | partDecoder, 26 | partEncoder, 27 | partDeviceIn, 28 | partDeviceOut, 29 | partFilter 30 | ); 31 | 32 | TFileMagic = array of char; 33 | const 34 | cPAFileMagicEmpty = #0#0#0#0; 35 | 36 | // generic register 37 | procedure PARegister(AType: TPARegisterType; AClass: TClass; AName: String; AExtension: String = ''; AMagic: String = cPAFileMagicEmpty; AMagicLen: Integer = 4; AMagicOffset: Integer = 0); 38 | function PARegisteredGet(AType: TPARegisterType; AName: String): TClass; 39 | 40 | // enumerate 41 | function PARegisteredGetList(AType: TPARegisterType; const AExtentions: TStrings = nil): TStrings; 42 | 43 | // specific registers. can be used instead of PARegister 44 | procedure PARegisterDecoder(ASource: TPAStreamSourceClass; AName: String; AExtension: String; AMagic: TFileMagic); 45 | procedure PARegisterEncoder(ASource: TPAStreamDestinationClass; AName: String; AExtension: String; AMagic: TFileMagic); 46 | procedure PARegisterDeviceOut(AOutput: TPAAudioDestinationClass; AName: String); 47 | procedure PARegisterDeviceIn(AInput: TPAAudioDestinationClass; AName: String); 48 | procedure PARegisterFilter(AFilter: TPAAudioLinkClass; AName: String); 49 | 50 | // decoders 51 | //function PARegisteredGetDecoderClass(AExtention: String): TPAStreamSourceClass; 52 | function PARegisteredGetDecoderClass(AMagic: TFileMagic): TPAStreamSourceClass; 53 | function PARegisteredGetDecoderClass(AStream: TStream): TPAStreamSourceClass; 54 | function PARegisteredGetDecoderClass(AFileName: String; AOnlyUseExtention: Boolean): TPAStreamSourceClass; 55 | 56 | // encoders 57 | function PARegisteredGetEncoderClass(AExtention: String): TPAStreamDestinationClass; 58 | function PARegisteredGetEncoderClass(AMagic: TFileMagic): TPAStreamDestinationClass; 59 | 60 | // devices 61 | function PARegisteredGetDeviceOut(AName: String): TPAAudioDestinationClass; 62 | function PARegisteredGetDeviceIn(AName: String): TPAAudioSourceClass; 63 | 64 | // filters 65 | function PARegisteredGetFilter(AName: String): TPAAudioLinkClass; 66 | 67 | implementation 68 | 69 | uses 70 | fgl; 71 | 72 | type 73 | TAudioClassEntry = class 74 | AudioClass: TClass; 75 | Name: String; 76 | Extention: String; 77 | Magic: TFileMagic; 78 | MagicLen: Integer; 79 | MagicOffset: Integer; 80 | end; 81 | 82 | TAudioClassList = specialize TFPGObjectList<TAudioClassEntry>; 83 | 84 | var 85 | EncoderList: TAudioClassList; 86 | DecoderList: TAudioClassList; 87 | DeviceOutList: TAudioClassList; 88 | DeviceInList: TAudioClassList; 89 | FilterList: TAudioClassList; 90 | 91 | procedure PARegisterDecoder(ASource: TPAStreamSourceClass; AName: String; AExtension: String; AMagic: TFileMagic); 92 | var 93 | lSource: TAudioClassEntry; 94 | begin 95 | lSource := TAudioClassEntry.Create; 96 | lSource.Name:=AName; 97 | lSource.Extention:=AExtension; 98 | lSource.Magic := AMagic; 99 | lSource.AudioClass:=ASource; 100 | DecoderList.Add(lSource); 101 | end; 102 | 103 | procedure PARegisterEncoder(ASource: TPAStreamDestinationClass; AName: String; AExtension: String; AMagic: TFileMagic); 104 | var 105 | lDest: TAudioClassEntry; 106 | begin 107 | lDest := TAudioClassEntry.Create; 108 | lDest.Name:=AName; 109 | lDest.Extention:=AExtension; 110 | lDest.Magic := AMagic; 111 | lDest.AudioClass:=ASource; 112 | EncoderList.Add(lDest); 113 | end; 114 | 115 | procedure PARegister(AType: TPARegisterType; AClass: TClass; AName: String; 116 | AExtension: String; AMagic: String; AMagicLen: Integer; AMagicOffset: Integer 117 | ); 118 | var 119 | Entry: TAudioClassEntry; 120 | List: TAudioClassList; 121 | begin 122 | case AType of 123 | partDecoder : List := DecoderList; 124 | partEncoder : List := EncoderList; 125 | partDeviceIn : List := DeviceInList; 126 | partDeviceOut : List := DeviceOutList; 127 | partFilter : List := FilterList; 128 | end; 129 | 130 | Entry := TAudioClassEntry.Create; 131 | Entry.Name:=AName; 132 | Entry.AudioClass:=AClass; 133 | Entry.Extention:=AExtension; 134 | Entry.Magic := @AMagic[1]; 135 | Entry.MagicLen:=AMagicLen; 136 | Entry.MagicOffset:=AMagicOffset; 137 | List.Add(Entry); 138 | end; 139 | 140 | procedure PARegisterDeviceOut(AOutput: TPAAudioDestinationClass; AName: String); 141 | begin 142 | PARegister(partDeviceOut, AOutput, AName); 143 | end; 144 | 145 | procedure PARegisterDeviceIn(AInput: TPAAudioDestinationClass; AName: String); 146 | begin 147 | PARegister(partDeviceIn, AInput, AName); 148 | end; 149 | 150 | procedure PARegisterFilter(AFilter: TPAAudioLinkClass; AName: String); 151 | begin 152 | PARegister(partFilter, AFilter, AName); 153 | end; 154 | 155 | function PARegisteredGetDecoderClass(AExtention: String): TPAStreamSourceClass; 156 | var 157 | i: TAudioClassEntry; 158 | begin 159 | for i in DecoderList do 160 | if CompareStr(i.Extention, AExtention) = 0 then 161 | Exit(TPAStreamSourceClass(i.AudioClass)); 162 | 163 | Result := nil; 164 | end; 165 | 166 | function PARegisteredGetDecoderClass(AMagic: TFileMagic): TPAStreamSourceClass; 167 | var 168 | i: TAudioClassEntry; 169 | begin 170 | for i in DecoderList do 171 | begin 172 | if CompareMem(@i.Magic[0], @AMagic[i.MagicOffset], i.MagicLen) then 173 | Exit(TPAStreamSourceClass(i.AudioClass)); 174 | end; 175 | 176 | Result := nil; 177 | end; 178 | 179 | function PARegisteredGetDecoderClass(AStream: TStream): TPAStreamSourceClass; 180 | var 181 | lSavedPos: Int64; 182 | lMagic: TFileMagic; 183 | begin 184 | Result := nil; 185 | lSavedPos := AStream.Position; 186 | AStream.Position:=0; 187 | SetLength(lMagic, 8); 188 | if AStream.Read(lMagic[0], SizeOf(lMagic)) = SizeOf(lMagic) then 189 | Result := PARegisteredGetDecoderClass(lMagic); 190 | 191 | AStream.Position:=lSavedPos; 192 | end; 193 | 194 | function PARegisteredGetDecoderClass(AFileName: String; AOnlyUseExtention: Boolean): TPAStreamSourceClass; 195 | var 196 | lFile: TFileStream; 197 | begin 198 | if AOnlyUseExtention then 199 | Exit(PARegisteredGetDecoderClass(ExtractFileExt(AFileName))); 200 | 201 | Result := nil; 202 | 203 | lFile := TFileStream.Create(AFileName, fmOpenRead); 204 | try 205 | Result := PARegisteredGetDecoderClass(lFile); 206 | finally 207 | lFile.Free; 208 | end; 209 | end; 210 | 211 | function PARegisteredGetEncoderClass(AExtention: String): TPAStreamDestinationClass; 212 | var 213 | i: TAudioClassEntry; 214 | begin 215 | for i in EncoderList do 216 | if i.Name = AExtention then 217 | Exit(TPAStreamDestinationClass(i.AudioClass)); 218 | 219 | Result := nil; 220 | end; 221 | 222 | function PARegisteredGetEncoderClass(AMagic: TFileMagic): TPAStreamDestinationClass; 223 | var 224 | i: TAudioClassEntry; 225 | begin 226 | for i in EncoderList do 227 | if i.Magic = AMagic then 228 | Exit(TPAStreamDestinationClass(i.AudioClass)); 229 | 230 | Result := nil; 231 | end; 232 | 233 | function PARegisteredGetDeviceOut(AName: String): TPAAudioDestinationClass; 234 | begin 235 | if AName = '' then 236 | Exit(TPAAudioDestinationClass(DeviceOutList.Items[0].AudioClass)); 237 | 238 | Result := TPAAudioDestinationClass(PARegisteredGet(partDeviceOut, AName)); 239 | end; 240 | 241 | function PARegisteredGetDeviceIn(AName: String): TPAAudioSourceClass; 242 | begin 243 | Result := TPAAudioSourceClass(PARegisteredGet(partDeviceOut, AName)); 244 | end; 245 | 246 | function PARegisteredGetFilter(AName: String): TPAAudioLinkClass; 247 | begin 248 | Result := TPAAudioLinkClass(PARegisteredGet(partFilter, AName)); 249 | end; 250 | 251 | function PARegisteredGet(AType: TPARegisterType; AName: String): TClass; 252 | var 253 | i: TAudioClassEntry; 254 | List: TAudioClassList; 255 | begin 256 | case AType of 257 | partDecoder : List := DecoderList; 258 | partEncoder : List := EncoderList; 259 | partDeviceIn : List := DeviceInList; 260 | partDeviceOut : List := DeviceOutList; 261 | partFilter : List := FilterList; 262 | end; 263 | for i in List do 264 | if i.Name = AName then 265 | Exit(i.AudioClass); 266 | 267 | Result := nil; 268 | end; 269 | 270 | function PARegisteredGetList(AType: TPARegisterType; const AExtentions: TStrings): TStrings; 271 | var 272 | i: TAudioClassEntry; 273 | List: TAudioClassList; 274 | begin 275 | case AType of 276 | partDecoder : List := DecoderList; 277 | partEncoder : List := EncoderList; 278 | partDeviceIn : List := DeviceInList; 279 | partDeviceOut : List := DeviceOutList; 280 | partFilter : List := FilterList; 281 | end; 282 | 283 | Result := TStringList.Create; 284 | 285 | for i in List do 286 | begin 287 | Result.AddObject(i.Name, TObject(i.AudioClass)); 288 | if Assigned(AExtentions) then 289 | AExtentions.Add(i.Extention); 290 | end; 291 | 292 | if Result.Count = 0 then 293 | FreeAndNil(Result); 294 | 295 | end; 296 | 297 | initialization 298 | EncoderList := TAudioClassList.Create; 299 | DecoderList := TAudioClassList.Create; 300 | DeviceOutList := TAudioClassList.Create; 301 | DeviceInList := TAudioClassList.Create; 302 | FilterList := TAudioClassList.Create; 303 | finalization 304 | EncoderList.Free; 305 | DecoderList.Free; 306 | DeviceOutList.Free; 307 | DeviceInList.Free; 308 | FilterList.Free; 309 | end. 310 | 311 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_resample.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_resample; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | {$IFDEF UNIX} 21 | 22 | uses 23 | Classes, SysUtils, unixtype, pa_base, resample; 24 | 25 | type 26 | 27 | { TPAResampleLink } 28 | 29 | TPAResampleLink = class(TPAAudioLink, IPAAudioInformation) 30 | private 31 | FResampleHelper: TResampleHelper; 32 | FOutSamplesPerSecond: Integer; 33 | FInited: Boolean; 34 | SourceSamplesPS: Integer; 35 | procedure InitData; 36 | procedure FinishConvert; 37 | protected 38 | function GetSamplesPerSecond: Integer; override; 39 | procedure SetSamplesPerSecond(AValue: Integer); override; 40 | function InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; override; 41 | procedure SignalDestinationsDone; override; 42 | public 43 | constructor Create; override; 44 | destructor Destroy; override; 45 | end; 46 | 47 | implementation 48 | 49 | uses 50 | ctypes, pa_ringbuffer; 51 | 52 | procedure TPAResampleLink.InitData; 53 | begin 54 | FInited:=True; 55 | with (DataSource.GetSourceObject as IPAAudioInformation) do 56 | begin 57 | SourceSamplesPS:=SamplesPerSecond; 58 | end; 59 | FResampleHelper := TResampleHelper.Create(Channels); 60 | //WriteLn('done Init data'); 61 | end; 62 | 63 | procedure TPAResampleLink.FinishConvert; 64 | begin 65 | FreeAndNil(FResampleHelper); 66 | end; 67 | 68 | function TPAResampleLink.GetSamplesPerSecond: Integer; 69 | begin 70 | Result:=FOutSamplesPerSecond; 71 | end; 72 | 73 | procedure TPAResampleLink.SetSamplesPerSecond(AValue: Integer); 74 | begin 75 | FOutSamplesPerSecond:=AValue; 76 | end; 77 | 78 | function TPAResampleLink.InternalProcessData(const AData; ACount: Int64; 79 | AIsLastData: Boolean): Int64; 80 | var 81 | i: Integer; 82 | InBufUsed: cint; 83 | ConvertedData: TSingleArray; 84 | ConvertedSize: Integer; 85 | Count: Integer; 86 | InPos: Integer; 87 | OutPos: Integer; 88 | begin 89 | if not FInited then 90 | InitData; 91 | 92 | // check if sample is the same and just pass the data forward if it is. No need to process it 93 | if (SourceSamplesPS = FOutSamplesPerSecond) 94 | then 95 | begin 96 | Result := WriteToBuffer(AData, ACount, AIsLastData); 97 | Exit; 98 | end; 99 | 100 | {Helper:= TResampleHelper.Create(AData, ACount, Channels, FOutSamplesPerSecond / SourceSamplesPS); 101 | 102 | for i := 0 to High(Channels) do 103 | begin 104 | InPos := 0; 105 | OutPos := 0; 106 | repeat 107 | 108 | Count := resample_process(FResample, 109 | FOutSamplesPerSecond / SourceSamplesPS, 110 | @Helper.InBuffers[i][InPos], 111 | Helper.InSamplesCount-InPos, 112 | 1,//1,//Ord(AIsLastData),//0, // IsLastData 113 | @InBufUsed, 114 | @Helper.OutBuffers[i][OutPos], 115 | Helper.OutBufferLength - OutPos); 116 | Inc(InPos, InBufUsed); 117 | //WriteLn('InnerLoop InPos = ', InPos, ' InUsed = ' , InBufUsed); 118 | if Count > 0 then 119 | Inc(OutPos, Count); 120 | 121 | 122 | until (Count < 0) or ((Count = 0) and (InPos = Helper.InSamplesCount)); 123 | Helper.OutBuffersLength[i] := OutPos; 124 | //WriteLn('Channel ', i, ' BufferOutCount = ',Helper.OutBuffersLength[i], ' Orig Length = ', Helper.InSamplesCount); 125 | end; 126 | 127 | ConvertedData := Helper.PlexOutBuffers(ConvertedSize);} 128 | 129 | ConvertedData := FResampleHelper.Write(PSingle(@AData), ACount div SizeOf(Single), FOutSamplesPerSecond / SourceSamplesPS, AIsLastData); 130 | 131 | 132 | Result := WriteToBuffer(ConvertedData[0], Length(ConvertedData)*SizeOf(Single), AIsLastData); 133 | //Result := WriteToBuffer(AData, ACount, AIsLastData); 134 | 135 | //FreeMem(ConvertedData); 136 | SetLength(ConvertedData, 0); 137 | //Helper.Free; 138 | 139 | end; 140 | 141 | procedure TPAResampleLink.SignalDestinationsDone; 142 | begin 143 | inherited SignalDestinationsDone; 144 | end; 145 | 146 | constructor TPAResampleLink.Create; 147 | begin 148 | inherited Create; 149 | FOutSamplesPerSecond:=44100; 150 | FFormat:=afFloat32; 151 | end; 152 | 153 | destructor TPAResampleLink.Destroy; 154 | begin 155 | inherited Destroy; 156 | end; 157 | 158 | { TStereoFloat } 159 | 160 | {$ELSE} 161 | implementation 162 | {$ENDIF} 163 | 164 | end. 165 | 166 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_samplerate.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | libsamplerate upon which this depends is GPL 14 | 15 | } 16 | unit pa_samplerate; 17 | 18 | {$mode objfpc}{$H+} 19 | {$packrecords c} 20 | 21 | interface 22 | 23 | uses 24 | Classes, SysUtils, pa_base, samplerate; 25 | 26 | 27 | type 28 | 29 | { TPASampleRateLink } 30 | 31 | TPASampleRateLink = class(TPAAudioLink) 32 | private 33 | FSrc: PSRC_STATE; 34 | FOutSamplesPerSecond: Integer; 35 | FOutChannels: Integer; 36 | FInited: Boolean; 37 | procedure InitData; 38 | procedure FinishConvert; 39 | protected 40 | function GetSamplesPerSecond: Integer; override; 41 | procedure SetSamplesPerSecond(AValue: Integer); override; 42 | function GetChannels: Integer; override; 43 | procedure SetChannels(AValue: Integer); override; 44 | function InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; override; 45 | procedure SignalDestinationsDone; override; 46 | public 47 | constructor Create; override; 48 | destructor Destroy; override; 49 | 50 | end; 51 | 52 | implementation 53 | uses 54 | ctypes, math; 55 | 56 | 57 | 58 | 59 | 60 | 61 | { TPASampleRateLink } 62 | 63 | procedure TPASampleRateLink.InitData; 64 | var 65 | error: cint; 66 | begin 67 | //WriteLn('Init data'); 68 | FInited:=True; 69 | FSrc:= src_new(SRC_SINC_FASTEST, FOutChannels, @error); 70 | if error <> 0 then 71 | WriteLn('ProcessResult: ',src_strerror(error)); 72 | src_set_ratio(FSrc, SamplesPerSecond / (DataSource.GetSourceObject as IPAAudioInformation).SamplesPerSecond); 73 | //WriteLn(src_is_valid_ratio(SamplesPerSecond / (DataSource.GetSourceObject as IPAAudioInformation).SamplesPerSecond)); 74 | //WriteLn('done Init data'); 75 | end; 76 | 77 | procedure TPASampleRateLink.FinishConvert; 78 | begin 79 | src_delete(FSrc); 80 | FInited:=False; 81 | end; 82 | 83 | function TPASampleRateLink.GetSamplesPerSecond: Integer; 84 | begin 85 | Result:=FOutSamplesPerSecond; 86 | end; 87 | 88 | procedure TPASampleRateLink.SetSamplesPerSecond(AValue: Integer); 89 | begin 90 | FOutSamplesPerSecond:=AValue; 91 | end; 92 | 93 | function TPASampleRateLink.GetChannels: Integer; 94 | begin 95 | Result := FOutChannels; 96 | end; 97 | 98 | procedure TPASampleRateLink.SetChannels(AValue: Integer); 99 | begin 100 | FOutChannels:=AValue; 101 | end; 102 | 103 | 104 | 105 | 106 | //function StereoShortToFloatArrays 107 | 108 | 109 | function TPASampleRateLink.InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; 110 | var 111 | sdata: SRC_DATA; 112 | Converted: Pointer; 113 | ConvertedToFloat: pcfloat absolute Converted; 114 | ConvertedToShort: pcshort absolute Converted; 115 | OutData: pointer; 116 | OutDataSize: Integer; 117 | Ratio: Single; 118 | SourceSamplesPS: Integer; 119 | SourceFormat: TPAAudioFormat; 120 | SourceChannels: Integer; 121 | res: cint; 122 | begin 123 | WriteLn('samplerate process'); 124 | if not FInited then 125 | InitData; 126 | 127 | with (DataSource.GetSourceObject as IPAAudioInformation) do 128 | begin 129 | SourceSamplesPS:=SamplesPerSecond; 130 | SourceFormat:=Format; 131 | SourceChannels:=Channels; 132 | end; 133 | 134 | sdata.src_ratio:=SamplesPerSecond / SourceSamplesPS; 135 | 136 | //Converted := Getmem(Max(ACount, Trunc(ACount * 4{sdata.src_ratio})+1)+4); 137 | 138 | // alloc enough memory for before and after frames. whichever is greater 139 | OutDataSize := Max(ACount, Trunc(ACount * sdata.src_ratio))+100; 140 | OutDataSize := ACount * 4 + 4; 141 | WriteLn('DataSize : ', OutDataSize, ' : ', ACount * 4 + 4); 142 | OutData := GetMem(OutDataSize); 143 | 144 | //src_short_to_float_array(pcshort(@AData), ConvertedToFloat, ACount div BytesPerSample(Format)); 145 | //ConvertShortIntsToFloat(PShortInt(@AData), ConvertedToFloat, ACount div BytesPerSample); 146 | 147 | // sdata.data_in:=ConvertedToFloat; 148 | sdata.data_in:=@AData; 149 | sdata.data_out:=pcfloat(OutData); 150 | sdata.input_frames:=ACount div BytesPerSample(Format) div SourceChannels; 151 | 152 | //sdata.input_frames:=(ACount div ((SourceSamplesPS div SourceBytesPerSample) div SourceChannels)); 153 | //sdata.output_frames:= OutDataSize div 2 div SourceChannels; 154 | sdata.output_frames:= OutDataSize div 2 div SourceChannels; 155 | sdata.end_of_input:=0;//ord(FDataIsEnded and FBufferManager.Empty); 156 | 157 | { WriteLn('input frames avail: ', sdata.input_frames); 158 | 159 | WriteLn('Processing Data:', ACount);} 160 | 161 | res:=src_process(FSrc, @sdata); 162 | if Res <> 0 then 163 | WriteLn('ProcessResult: ',src_strerror(res)); 164 | { 165 | WriteLn('input frames avail: ', sdata.input_frames); 166 | WriteLn('input frames used: ', sdata.input_frames_used); 167 | WriteLn('output frames generated: ', sdata.output_frames_gen); 168 | } 169 | 170 | //src_float_to_short_array(sdata.data_out, ConvertedToShort, sdata.output_frames_gen * FOutChannels); 171 | //WriteToBuffer(ConvertedToShort^, sdata.output_frames_gen * FOutChannels * FOutBytesPerSample, AIsLastData); 172 | 173 | WriteToBuffer(sdata.data_out^, sdata.output_frames_gen * FOutChannels * BytesPerSample(afFloat32), AIsLastData); 174 | 175 | //Freemem(Converted); 176 | FreeMem(OutData); 177 | 178 | end; 179 | 180 | procedure TPASampleRateLink.SignalDestinationsDone; 181 | begin 182 | inherited SignalDestinationsDone; 183 | end; 184 | 185 | constructor TPASampleRateLink.Create; 186 | begin 187 | inherited Create; 188 | FOutSamplesPerSecond:=44100; 189 | FFormat:=afFloat32; 190 | end; 191 | 192 | destructor TPASampleRateLink.Destroy; 193 | begin 194 | inherited Destroy; 195 | end; 196 | 197 | 198 | 199 | end. 200 | 201 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_sox.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_sox; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, pa_base, pa_process; 22 | 23 | type 24 | 25 | { TPASoxSource } 26 | 27 | TPASoxSource = class(TPAProcessSource) 28 | private 29 | FFileName: String; 30 | procedure SetFileName(AValue: String); 31 | protected 32 | procedure BeforeExecuteLoop; override; 33 | public 34 | constructor Create; override; 35 | property FileName: String read FFileName write SetFileName; 36 | end; 37 | 38 | implementation 39 | 40 | { TPASoxSource } 41 | 42 | procedure TPASoxSource.SetFileName(AValue: String); 43 | begin 44 | if FFileName=AValue then Exit; 45 | FFileName:=AValue; 46 | end; 47 | 48 | procedure TPASoxSource.BeforeExecuteLoop; 49 | begin 50 | FProcess.Parameters.AddStrings([FFileName, '-t', 'raw', '--bits', '32', '--encoding', 'float']); 51 | FProcess.Parameters.AddStrings(['-r', IntToStr(SamplesPerSecond), '-c', IntToStr(Channels), '-']); 52 | inherited BeforeExecuteLoop; 53 | end; 54 | 55 | constructor TPASoxSource.Create; 56 | begin 57 | inherited Create; 58 | FProcess.Executable:='sox'; 59 | Format:=afFloat32; 60 | end; 61 | 62 | end. 63 | 64 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_stream.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_stream; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, pa_base; 22 | 23 | type 24 | 25 | { TPAStreamSource } 26 | 27 | TPAStreamSourceClass = class of TPAStreamSource; 28 | TPAStreamSource = class(TPAAudioSource, IPAStream) 29 | protected 30 | FStream: TStream; 31 | FOwnsStream: Boolean; 32 | procedure SetStream(AValue: TStream); virtual; 33 | function GetStream: TStream; virtual; 34 | private 35 | function GetOwnsStream: Boolean; 36 | procedure SetOwnsStream(AValue: Boolean); 37 | 38 | protected 39 | function InternalOutputToDestination: Boolean; override; 40 | public 41 | constructor Create; override; // you must set ownsstream and stream 42 | constructor Create(AStream: TStream; AOwnsStream: Boolean = True); virtual; 43 | destructor Destroy; override; 44 | property Stream: TStream read GetStream write SetStream; 45 | property OwnsStream: Boolean read GetOwnsStream write SetOwnsStream; 46 | end; 47 | 48 | { TPAStreamDestination } 49 | 50 | TPAStreamDestinationClass = class of TPAStreamDestination; 51 | TPAStreamDestination = class(TPAAudioDestination, IPAStream) 52 | private 53 | FOwnsStream: Boolean; 54 | function GetOwnsStream: Boolean; 55 | procedure SetOwnsStream(AValue: Boolean); 56 | protected 57 | FStream: TStream; 58 | function GetStream: TStream; virtual; 59 | procedure SetStream(AValue: TStream); virtual; 60 | function InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; override; 61 | procedure EndOfData; override; 62 | public 63 | constructor Create(AStream: TStream; AOwnsStream: Boolean); virtual; 64 | destructor Destroy; override; 65 | property Stream: TStream read GetStream write SetStream; 66 | property OwnsStream: Boolean read GetOwnsStream write SetOwnsStream; 67 | end; 68 | 69 | implementation 70 | 71 | { TPAStreamSource } 72 | 73 | function TPAStreamSource.GetStream: TStream; 74 | begin 75 | Result := FStream; 76 | end; 77 | 78 | function TPAStreamSource.GetOwnsStream: Boolean; 79 | begin 80 | Result := FOwnsStream; 81 | end; 82 | 83 | procedure TPAStreamSource.SetOwnsStream(AValue: Boolean); 84 | begin 85 | FOwnsStream := AValue; 86 | end; 87 | 88 | procedure TPAStreamSource.SetStream(AValue: TStream); 89 | begin 90 | if Assigned(FStream) and (AValue <> FStream) and FOwnsStream then 91 | FreeAndNil(FStream); 92 | FStream := AValue; 93 | end; 94 | 95 | function TPAStreamSource.InternalOutputToDestination: Boolean; 96 | var 97 | Buffer: PAudioBuffer; 98 | begin 99 | 100 | Buffer := BufferPool.GetBufferFromPool(True); 101 | Buffer^.UsedData := FStream.Read(Buffer^.Data, AUDIO_BUFFER_SIZE); 102 | Buffer^.IsEndOfData:= Buffer^.UsedData < AUDIO_BUFFER_SIZE; 103 | 104 | Result := Buffer^.UsedData > 0; 105 | WriteToDestinations(Buffer); 106 | if Not Result then 107 | SignalDestinationsDone; 108 | 109 | end; 110 | 111 | constructor TPAStreamSource.Create; 112 | begin 113 | Create(nil, True); 114 | end; 115 | 116 | constructor TPAStreamSource.Create(AStream: TStream; AOwnsStream: Boolean); 117 | begin 118 | inherited Create; 119 | OwnsStream := AOwnsStream; 120 | Stream := AStream; 121 | end; 122 | 123 | destructor TPAStreamSource.Destroy; 124 | begin 125 | DestroyWaitSync; 126 | if FOwnsStream and Assigned(FStream) then 127 | FreeAndNil(FStream); 128 | inherited Destroy; 129 | end; 130 | 131 | { TPAStreamDestination } 132 | 133 | function TPAStreamDestination.GetStream: TStream; 134 | begin 135 | Result := FStream; 136 | end; 137 | 138 | function TPAStreamDestination.GetOwnsStream: Boolean; 139 | begin 140 | Result := FOwnsStream; 141 | end; 142 | 143 | procedure TPAStreamDestination.SetOwnsStream(AValue: Boolean); 144 | begin 145 | FOwnsStream := AValue; 146 | end; 147 | 148 | procedure TPAStreamDestination.SetStream(AValue: TStream); 149 | begin 150 | if Assigned(FStream) and FOwnsStream then 151 | FreeAndNil(FStream); 152 | FStream := AValue; 153 | end; 154 | 155 | function TPAStreamDestination.InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; 156 | begin 157 | if Assigned(FStream) then 158 | Result := FStream.Write(AData, ACount); 159 | 160 | if AIsLastData then 161 | EndOfData; 162 | 163 | end; 164 | 165 | procedure TPAStreamDestination.EndOfData; 166 | begin 167 | inherited EndOfData; 168 | //WriteLn('StreamDEstination EndOf Data;'); 169 | FBufferManager.Flush; 170 | end; 171 | 172 | constructor TPAStreamDestination.Create(AStream: TStream; AOwnsStream: Boolean); 173 | begin 174 | FStream := AStream; 175 | inherited Create; 176 | Format := afRaw; 177 | end; 178 | 179 | destructor TPAStreamDestination.Destroy; 180 | begin 181 | Stream := nil; // this will free the stream if we own it. 182 | inherited Destroy; 183 | end; 184 | 185 | { TPAStreamSource } 186 | 187 | 188 | 189 | end. 190 | 191 | -------------------------------------------------------------------------------- /pascalaudiosuite/pa_wav.pas: -------------------------------------------------------------------------------- 1 | { 2 | This unit is part of PascalAudioSuite package. 3 | 4 | Copyright (c) 2016 by Andrew Haines. 5 | 6 | See the files COPYING.modifiedLGPL and LICENSES.txt, included in this 7 | distribution, for details about the license. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 | 13 | } 14 | unit pa_wav; 15 | 16 | {$mode objfpc}{$H+} 17 | 18 | interface 19 | 20 | uses 21 | Classes, SysUtils, pa_base, pa_stream, pa_register, fpwavformat; 22 | 23 | type 24 | 25 | { TPAWavSource } 26 | 27 | TPAWavSource = class(TPAStreamSource, IPAStream) 28 | private 29 | FWavFormat: TWaveFormat; 30 | FValid: Boolean; 31 | FCurrentChunk: TChunkHeader; 32 | procedure ReadHeader; 33 | function LoadNextChunk: Boolean; 34 | protected 35 | procedure SetStream(AValue: TStream); override; 36 | function InternalOutputToDestination: Boolean; override; 37 | end; 38 | 39 | TPAWavDest = class(TPAStreamDestination, IPAStream) 40 | private 41 | const 42 | cFileSizeOff = 4; 43 | cDataSizeOff = 40; 44 | private 45 | FInited: Boolean; 46 | FFinished: Boolean; 47 | FDataSize: DWord; 48 | procedure InitStream; 49 | procedure FinishStream; 50 | protected 51 | function InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; override; 52 | public 53 | constructor Create(AStream: TStream; AOwnsStream: Boolean); override; 54 | property Stream; 55 | end; 56 | 57 | implementation 58 | 59 | { TPAWavSource } 60 | 61 | procedure LEtoN(var fmt: TWaveFormat); overload; 62 | begin 63 | // from fpwavreader 64 | with fmt, ChunkHeader do begin 65 | Size := LEtoN(Size); 66 | Format := LEtoN(Format); 67 | Channels := LEtoN(Channels); 68 | SampleRate := LEtoN(SampleRate); 69 | ByteRate := LEtoN(ByteRate); 70 | BlockAlign := LEtoN(BlockAlign); 71 | BitsPerSample := LEtoN(BitsPerSample); 72 | end; 73 | end; 74 | 75 | 76 | procedure NtoLE(var fmt: TWaveFormat); overload; 77 | begin 78 | // from fpwavreader 79 | with fmt, ChunkHeader do begin 80 | Size := NtoLE(Size); 81 | Format := NtoLE(Format); 82 | Channels := NtoLE(Channels); 83 | SampleRate := NtoLE(SampleRate); 84 | ByteRate := NtoLE(ByteRate); 85 | BlockAlign := NtoLE(BlockAlign); 86 | BitsPerSample := NtoLE(BitsPerSample); 87 | end; 88 | end; 89 | 90 | { TPAWavDest } 91 | 92 | procedure TPAWavDest.InitStream; 93 | var 94 | Riff: TRiffHeader; 95 | WavChunk: TWaveFormat; 96 | DataChunk: TChunkHeader; 97 | begin 98 | FInited := True; 99 | 100 | Riff.ChunkHeader.ID := AUDIO_CHUNK_ID_RIFF; 101 | Riff.Format := AUDIO_CHUNK_ID_WAVE; 102 | Riff.ChunkHeader.Size:=0; // fill in later 103 | 104 | WavChunk.ChunkHeader.ID := AUDIO_CHUNK_ID_fmt; 105 | WavChunk.ChunkHeader.Size:=16; 106 | WavChunk.Format := AUDIO_FORMAT_PCM; 107 | 108 | WavChunk.BitsPerSample := 16; 109 | WavChunk.Channels:=Channels; 110 | WavChunk.SampleRate:= SamplesPerSecond div Channels; 111 | WavChunk.ByteRate:=(WavChunk.BitsPerSample * Channels) div 8; 112 | NtoLE(WavChunk); 113 | 114 | DataChunk.ID := AUDIO_CHUNK_ID_data; 115 | DataChunk.Size:=0; // fill in later 116 | 117 | FStream.Write(Riff, SizeOf(Riff)); 118 | FStream.Write(WavChunk, SizeOf(WavChunk)); 119 | FStream.Write(DataChunk, SizeOf(DataChunk)); 120 | //That's it. 121 | end; 122 | 123 | procedure TPAWavDest.FinishStream; 124 | begin 125 | if FFinished then 126 | Exit; 127 | 128 | FFinished:= True; 129 | 130 | // write total size -1 131 | FStream.Position:=cFileSizeOff; 132 | FStream.WriteDWord(NtoLE(DWord(FStream.Size-SizeOf(TChunkHeader)))); 133 | 134 | // write raw data size 135 | FStream.Position:=cDataSizeOff; 136 | FStream.WriteDWord(NtoLE(FDataSize)); 137 | FStream.Seek(0, soEnd); 138 | end; 139 | 140 | function TPAWavDest.InternalProcessData(const AData; ACount: Int64; AIsLastData: Boolean): Int64; 141 | begin 142 | if not FInited then 143 | InitStream; 144 | 145 | Result := FStream.Write(AData, ACount); 146 | Inc(FDataSize, Result); 147 | 148 | if AIsLastData then 149 | begin 150 | FinishStream; 151 | end; 152 | end; 153 | 154 | constructor TPAWavDest.Create(AStream: TStream; AOwnsStream: Boolean); 155 | begin 156 | Inherited Create(AStream, AOwnsStream); 157 | Format:= afS16; 158 | end; 159 | 160 | procedure TPAWavSource.ReadHeader; 161 | var 162 | Riff: TRiffHeader; 163 | RCount: Integer; 164 | begin 165 | FValid := False; 166 | 167 | if not Assigned(FStream) then 168 | Exit; 169 | FStream.Seek(0, soBeginning); 170 | RCount := FStream.Read(Riff, SizeOf(Riff)); 171 | if RCount <> SizeOf(Riff) then 172 | Exit; 173 | Riff.ChunkHeader.Size:=LEtoN(Riff.ChunkHeader.Size); 174 | FValid := (Riff.ChunkHeader.ID = AUDIO_CHUNK_ID_RIFF) 175 | and (Riff.Format = AUDIO_CHUNK_ID_WAVE); 176 | if not FValid then 177 | Exit; 178 | FStream.Read(FWavFormat, SizeOf(FWavFormat)); 179 | LEtoN(FWavFormat); 180 | FValid := (FWavFormat.ChunkHeader.ID = AUDIO_CHUNK_ID_fmt) 181 | and (FWavFormat.Format = AUDIO_FORMAT_PCM); 182 | 183 | Channels:=FWavFormat.Channels; 184 | SamplesPerSecond:=FWavFormat.SampleRate; 185 | Format:=afS16; 186 | end; 187 | 188 | function TPAWavSource.LoadNextChunk: Boolean; 189 | var 190 | RCount: LongInt; 191 | begin 192 | Result := False; 193 | RCount := FStream.Read(FCurrentChunk, SizeOf(FCurrentChunk)); 194 | if RCount < SizeOf(FCurrentChunk) then 195 | begin 196 | FCurrentChunk.Size:=0; 197 | FCurrentChunk.ID := ' '; 198 | Exit; 199 | end; 200 | FCurrentChunk.Size:=LeToN(FCurrentChunk.Size); 201 | 202 | // fix datasize for illformed wav files. 203 | if (FCurrentChunk.ID = AUDIO_CHUNK_ID_data) and (FCurrentChunk.Size = 0) then 204 | FCurrentChunk.Size:=FStream.Size-FStream.Position; 205 | 206 | Result := True; 207 | end; 208 | 209 | procedure TPAWavSource.SetStream(AValue: TStream); 210 | begin 211 | inherited SetStream(AValue); 212 | if Assigned(AValue) then 213 | ReadHeader 214 | else 215 | FValid := False; 216 | end; 217 | 218 | function TPAWavSource.InternalOutputToDestination: Boolean; 219 | var 220 | Buf: array[0..AUDIO_BUFFER_SIZE-1] of byte; 221 | RCount: Integer; 222 | WSize: Integer = 0; 223 | OutOfChunks: Boolean = False; 224 | begin 225 | Result := False; 226 | if not FValid then 227 | Exit; 228 | while (WSize < SizeOf(Buf)) and not OutOfChunks do 229 | begin 230 | if FCurrentChunk.Size = 0 then 231 | repeat 232 | OutOfChunks := not LoadNextChunk; 233 | if OutOfChunks then 234 | Break; 235 | until FCurrentChunk.ID = AUDIO_CHUNK_ID_data; 236 | 237 | RCount := FStream.Read(Buf[WSize], Min(SizeOf(Buf)-WSize, FCurrentChunk.Size)); 238 | Dec(FCurrentChunk.Size, RCount); 239 | Inc(WSize, RCount); 240 | end; 241 | 242 | Result := FStream.Position < FStream.Size; 243 | 244 | if WSize > 0 then 245 | WriteToBuffer(Buf, WSize, not Result); 246 | end; 247 | 248 | initialization 249 | PARegister(partEncoder, TPAWavDest, 'Wave/PCM', '.wav' ,'RIFF', 4); 250 | PARegister(partDecoder, TPAWavSource, 'Wave/PCM', '.wav'); 251 | end. 252 | 253 | -------------------------------------------------------------------------------- /pascalaudiosuite/pascalaudiosuite.lpk: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <CONFIG> 3 | <Package Version="5"> 4 | <Name Value="PascalAudioSuite"/> 5 | <Type Value="RunAndDesignTime"/> 6 | <CompilerOptions> 7 | <Version Value="11"/> 8 | <SearchPaths> 9 | <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> 10 | </SearchPaths> 11 | <Conditionals Value="if SrcOS = 'unix' then 12 | CustomOptions+='-dUSEPULSE';"/> 13 | </CompilerOptions> 14 | <Files Count="20"> 15 | <Item1> 16 | <Filename Value="pa_base.pas"/> 17 | <UnitName Value="pa_base"/> 18 | </Item1> 19 | <Item2> 20 | <Filename Value="pa_lists.pas"/> 21 | <UnitName Value="pa_lists"/> 22 | </Item2> 23 | <Item3> 24 | <Filename Value="pa_resample.pas"/> 25 | <UnitName Value="pa_resample"/> 26 | </Item3> 27 | <Item4> 28 | <Filename Value="pa_enc_oggvorbis.pas"/> 29 | <UnitName Value="pa_enc_oggvorbis"/> 30 | </Item4> 31 | <Item5> 32 | <Filename Value="pa_stream.pas"/> 33 | <UnitName Value="pa_stream"/> 34 | </Item5> 35 | <Item6> 36 | <Filename Value="pa_ladspa.pas"/> 37 | <UnitName Value="pa_ladspa"/> 38 | </Item6> 39 | <Item7> 40 | <Filename Value="pa_dec_oggvorbis.pas"/> 41 | <UnitName Value="pa_dec_oggvorbis"/> 42 | </Item7> 43 | <Item8> 44 | <Filename Value="pa_noiseremoval.pas"/> 45 | <UnitName Value="pa_noiseremoval"/> 46 | </Item8> 47 | <Item9> 48 | <Filename Value="pa_cdaudio.pas"/> 49 | <UnitName Value="pa_cdaudio"/> 50 | </Item9> 51 | <Item10> 52 | <Filename Value="pa_process.pas"/> 53 | <UnitName Value="pa_process"/> 54 | </Item10> 55 | <Item11> 56 | <Filename Value="pa_wav.pas"/> 57 | <UnitName Value="pa_wav"/> 58 | </Item11> 59 | <Item12> 60 | <Filename Value="pa_binaural.pas"/> 61 | <UnitName Value="pa_binaural"/> 62 | </Item12> 63 | <Item13> 64 | <Filename Value="pa_samplerate.pas"/> 65 | <UnitName Value="pa_samplerate"/> 66 | </Item13> 67 | <Item14> 68 | <Filename Value="pa_pulse_simple.pas"/> 69 | <UnitName Value="pa_pulse_simple"/> 70 | </Item14> 71 | <Item15> 72 | <Filename Value="pa_sox.pas"/> 73 | <UnitName Value="pa_sox"/> 74 | </Item15> 75 | <Item16> 76 | <Filename Value="pa_flac.pas"/> 77 | <UnitName Value="pa_flac"/> 78 | </Item16> 79 | <Item17> 80 | <Filename Value="pa_register.pas"/> 81 | <UnitName Value="pa_register"/> 82 | </Item17> 83 | <Item18> 84 | <Filename Value="pa_mmdevice.pas"/> 85 | <UnitName Value="pa_mmdevice"/> 86 | </Item18> 87 | <Item19> 88 | <Filename Value="pa_m4a.pas"/> 89 | <UnitName Value="pa_m4a"/> 90 | </Item19> 91 | <Item20> 92 | <Filename Value="pa_ogg_opus.pas"/> 93 | <UnitName Value="pa_ogg_opus"/> 94 | </Item20> 95 | </Files> 96 | <RequiredPkgs Count="3"> 97 | <Item1> 98 | <PackageName Value="pulse_simple_pkg"/> 99 | </Item1> 100 | <Item2> 101 | <PackageName Value="PascalAudioIO"/> 102 | </Item2> 103 | <Item3> 104 | <PackageName Value="FCL"/> 105 | </Item3> 106 | </RequiredPkgs> 107 | <UsageOptions> 108 | <UnitPath Value="$(PkgOutDir)"/> 109 | </UsageOptions> 110 | <PublishOptions> 111 | <Version Value="2"/> 112 | </PublishOptions> 113 | </Package> 114 | </CONFIG> 115 | -------------------------------------------------------------------------------- /pascalaudiosuite/pascalaudiosuite.pas: -------------------------------------------------------------------------------- 1 | { This file was automatically created by Lazarus. Do not edit! 2 | This source is only used to compile and install the package. 3 | } 4 | 5 | unit PascalAudioSuite; 6 | 7 | {$warn 5023 off : no warning about unused units} 8 | interface 9 | 10 | uses 11 | pa_base, pa_lists, pa_resample, pa_enc_oggvorbis, pa_stream, pa_ladspa, 12 | pa_dec_oggvorbis, pa_noiseremoval, pa_cdaudio, pa_process, pa_wav, 13 | pa_binaural, pa_samplerate, pa_pulse_simple, pa_sox, pa_flac, pa_register, 14 | pa_mmdevice, pa_m4a, pa_ogg_opus, LazarusPackageIntf; 15 | 16 | implementation 17 | 18 | procedure Register; 19 | begin 20 | end; 21 | 22 | initialization 23 | RegisterPackage('PascalAudioSuite', @Register); 24 | end. 25 | --------------------------------------------------------------------------------