├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── Directory.Build.props ├── LICENSE ├── README.md └── src ├── AudioInfo.cs ├── Effects ├── AllPassFilterEffect.cs ├── BandPassFilterEffect.cs ├── BandRejectFilterEffect.cs ├── BaseEffect.cs ├── BassEffect.cs ├── ContrastEffect.cs ├── HighPassFilterEffect.cs ├── IBaseEffect.cs ├── LoudnessEffect.cs ├── LowPassFilterEffect.cs ├── NoiseProfileEffect.cs ├── NoiseReductionEffect.cs ├── ReverseEffect.cs ├── SpeedEffect.cs ├── TempoEffect.cs ├── TrebleEffect.cs ├── TremoloEffect.cs ├── TrimEffect.cs ├── Types │ ├── FilterType.cs │ ├── Frequency.cs │ ├── FrequencyUnits.cs │ ├── GainType.cs │ ├── OptimizationType.cs │ ├── Position.cs │ ├── PositionFrom.cs │ ├── Width.cs │ └── WidthUnits.cs └── VolumeEffect.cs ├── Events.cs ├── Exceptions ├── SoxEffectException.cs ├── SoxException.cs └── SoxUnexpectedOutputException.cs ├── FormatOptions.cs ├── FormattedSize.cs ├── InputFile.cs ├── OutputFormatOptions.cs ├── Resources.Designer.cs ├── Resources.resx ├── Sox.cs ├── SoxProcess.cs ├── SoxSharp.csproj ├── SoxSharp.sln ├── Types.cs └── Utils.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: windows-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: 5.0.x 20 | - name: Restore dependencies 21 | run: dotnet restore src\SoxSharp.sln 22 | - name: Build 23 | run: dotnet build --no-restore src\SoxSharp.sln 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | 254 | # ========================= 255 | # Operating System Files 256 | # ========================= 257 | 258 | # OSX 259 | # ========================= 260 | 261 | .DS_Store 262 | .AppleDouble 263 | .LSOverride 264 | 265 | # Thumbnails 266 | ._* 267 | 268 | # Files that might appear in the root of a volume 269 | .DocumentRevisions-V100 270 | .fseventsd 271 | .Spotlight-V100 272 | .TemporaryItems 273 | .Trashes 274 | .VolumeIcon.icns 275 | 276 | # Directories potentially created on remote AFP share 277 | .AppleDB 278 | .AppleDesktop 279 | Network Trash Folder 280 | Temporary Items 281 | .apdisk 282 | 283 | # Windows 284 | # ========================= 285 | 286 | # Windows image file caches 287 | Thumbs.db 288 | ehthumbs.db 289 | 290 | # Folder config file 291 | Desktop.ini 292 | 293 | # Recycle Bin used on file shares 294 | $RECYCLE.BIN/ 295 | 296 | # Windows Installer files 297 | *.cab 298 | *.msi 299 | *.msm 300 | *.msp 301 | 302 | # Windows shortcuts 303 | *.lnk 304 | /src/Test 305 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ..\obj 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![nuget](https://img.shields.io/nuget/v/SoxSharp.svg)](https://www.nuget.org/packages/SoxSharp) 2 | [![nuget](https://img.shields.io/nuget/dt/SoxSharp.svg)](https://www.nuget.org/packages/SoxSharp) 3 | [![Build](https://github.com/igece/SoxSharp/actions/workflows/build.yml/badge.svg)](https://github.com/igece/SoxSharp/actions/workflows/build.yml) 4 | 5 | ## SoxSharp 6 | 7 | SoxSharp is a C# library that serves as a wrapper to [SoX - the Sound eXchange tool](http://sox.sourceforge.net/). 8 | SoX is a cross-platform (Windows, Linux, MacOS X, etc.) command line utility that can convert various formats of computer audio files in to other formats. It can also apply various effects to these sound files and play and record audio files on most platforms. 9 | 10 | 11 | ### How It Works 12 | 13 | #### Initial Setup 14 | 15 | When instantiating the **Sox** class, we pass to the constructor the location of the SoX executable to be used. Please note that, when SoX is executed, the directory of the application using the library will be used as the working directory, so any relative path included in both input and output files shall be relative to this directory. 16 | 17 | 18 | #### Get File Information 19 | 20 | Usage is pretty straightforward: 21 | 22 | ```cs 23 | using (var sox = new Sox("sox.exe")) 24 | { 25 | AudioInfo wavInfo = sox.GetInfo("test.wav"); 26 | Console.WriteLine(wavInfo); 27 | } 28 | ``` 29 | 30 | This is the same as executing `sox --info test.wav`. SoxSharp parses the SoX output and fills an **AudioInfo** instance with the retrieved information. 31 | 32 | 33 | #### File Conversion 34 | 35 | The simplest way to perform an audio conversion is to just call the **Sox.Process** method with both input and output files: 36 | 37 | ```cs 38 | using (var sox = new Sox("sox.exe")) 39 | { 40 | sox.Process("test.wav", "test.mp3"); 41 | } 42 | ``` 43 | 44 | The previous code will launch SoX with 'test.wav' as input file and 'test.mp3' as output file (the equivalent of executing SoX with the following parameters: `sox test.wav test.mp3`). 45 | 46 | To force the output to be encoded with MP3 format (instead of letting SoX to guess it from the output file extension) and use a sample rate of 32 KHz: 47 | 48 | ```cs 49 | using (var sox = new Sox("sox.exe")) 50 | { 51 | sox.Output.Type = FileType.MP3; 52 | sox.Output.SampleRate = 32000; 53 | 54 | sox.Process("test.wav", "test.mp3"); 55 | } 56 | ``` 57 | 58 | This is equivalent to call SoX with the following parameters: `sox test.wav --type mp3 --rate 32000 test.mp3`. 59 | 60 | SoX global options can be set through their respective properties in the **Sox** class. Format options to be applied to the output can be stablished through its respective properties in **Sox.Output**. 61 | 62 | Format options to be applied to the input are specified passing an **InputFile** instance to the **Sox.Process** method (instead of an input file string): 63 | 64 | ```cs 65 | using (var sox = new Sox("sox.exe")) 66 | { 67 | sox.Output.Type = FileType.MP3; 68 | sox.Output.SampleRate = 32000; 69 | 70 | InputFile testInput = new InputFile("test.wav"); 71 | testInput.Volume = 0.8; 72 | 73 | sox.Process(testInput, "test.mp3"); 74 | } 75 | ``` 76 | 77 | This is equivalent to call SoX with the following parameters: `sox --volume 0.8 test.wav --type mp3 --rate 32000 test.mp3`. 78 | 79 | 80 | #### Applying Effects 81 | 82 | One or multiple effects can be added so they will applied to the output file: 83 | 84 | ```cs 85 | using (var sox = new Sox("sox.exe")) 86 | { 87 | sox.Output.Type = FileType.MP3; 88 | sox.Effects.Add(new VolumeEffect(10, GainType.Db)); 89 | sox.Effects.Add(new HighPassFilterEffect(500)); 90 | 91 | sox.Process("test.wav", "test.mp3"); 92 | } 93 | ``` 94 | 95 | Currently not all SoX effects have been implemented into SoxSharp. To see which effects are supported, please read [this issue](https://github.com/igece/SoxSharp/issues/1). 96 | 97 | 98 | #### Playing and Recording 99 | 100 | To record audio from the default audio device, use the **Sox.Record** method specifying the output file name: 101 | 102 | ```cs 103 | sox.Record("test_record.mp3"); 104 | ``` 105 | 106 | Analogously, **Sox.Play** allows to send an input file to the default audio device. In this case, the input file can be expressed as an file name string or a **InputFile** instance. 107 | 108 | The same results can be obtained if calling **Sox.Process** using `--default-device` as output (for playing) or input (for recording) file. 109 | 110 | 111 | #### Error Handling 112 | 113 | If any requested SoX operation is unable to be processed, either because SoX process can't be launched or beacuse it generates an error, a **SoxException** with details about the error will be thrown. 114 | 115 | 116 | #### Events 117 | 118 | You can subscribe to receive any log message generated by SoX through the **OnLogMessage** event. Please note that log messages of type FAIL will not be reported through this event but as exceptions, as stated in [Error Handling](#error-handling). 119 | 120 | ```cs 121 | // Subscribe to OnLogMessage. 122 | sox.OnLogMessage += sox_OnLogMessage; 123 | 124 | void sox_OnLogMessage(object sender, LogMessageEventArgs e) 125 | { 126 | Console.WriteLine(e.LogLevel + ": " + e.Message); 127 | } 128 | ``` 129 | 130 | Also, while executing a **Process** call you can obtain updated progress information about the operation subscribing to the **OnProgress** event: 131 | 132 | ```cs 133 | // Subscribe to OnProgress event before calling Process method. 134 | sox.OnProgress += sox_OnProgress; 135 | 136 | void sox_OnProgress(object sender, ProgressEventArgs e) 137 | { 138 | Console.WriteLine("{0} ({1}% completed)", e.Processed.ToString(@"hh\:mm\:ss\.ff"), e.Progress); 139 | } 140 | ``` 141 | 142 | 143 | ### Concurrency 144 | 145 | All the work is done in the calling thread. Each **Sox** class instance can handle only one process at the same time, and will block the calling thread until finished. You are responsible for creating any worker thread if needed. 146 | 147 | The library provides two different methods to cancel any work that is in progress: 148 | 149 | * Calling the **Abort()** method of the **Sox** class. 150 | 151 | * Inside the **OnProgress** event handler. **ProgressEventArgs** provides a boolean **Abort** member that can be set to **true** to end the current work. 152 | 153 | 154 | ### Library Reference 155 | 156 | A detailed description of all components of the library is available at the [repository wiki](https://github.com/igece/SoxSharp/wiki/Reference-Guide). 157 | 158 | 159 | -------------------------------------------------------------------------------- /src/AudioInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | 5 | namespace SoxSharp 6 | { 7 | /// 8 | /// Provides information about an audio file. 9 | /// 10 | public struct AudioInfo 11 | { 12 | /// 13 | /// Number of audio channels. 14 | /// 15 | public readonly UInt16 Channels; 16 | 17 | /// 18 | /// Audio sample rate. 19 | /// 20 | public readonly UInt32 SampleRate; 21 | 22 | /// 23 | /// Audio sample size (bits). 24 | /// 25 | public readonly UInt16 SampleSize; 26 | 27 | /// 28 | /// Audio time length. 29 | /// 30 | public readonly TimeSpan Duration; 31 | 32 | /// 33 | /// Audio file size. 34 | /// 35 | public readonly UInt64 Size; 36 | 37 | /// 38 | /// Audio bitrate. 39 | /// 40 | public readonly UInt32 BitRate; 41 | 42 | /// 43 | /// Audio format. 44 | /// 45 | public readonly string Format; 46 | 47 | 48 | /// 49 | /// Initializes a new instance of the class. 50 | /// 51 | /// Number of audio channels. 52 | /// Audio sample rate. 53 | /// Audio sample size (bits). 54 | /// Audio time length. 55 | /// Audio file size 56 | /// 57 | /// Audio format. 58 | public AudioInfo(UInt16 channels, UInt32 sampleRate, UInt16 sampleSize, TimeSpan duration, UInt64 size, UInt32 bitRate, string format) 59 | { 60 | Channels = channels; 61 | SampleRate = sampleRate; 62 | SampleSize = sampleSize; 63 | Duration = duration; 64 | Size = size; 65 | BitRate = bitRate; 66 | Format = format; 67 | } 68 | 69 | 70 | /// 71 | /// Returns information about the instance (invalidates ). 72 | /// 73 | /// String containing all instance properties values. 74 | public override string ToString() 75 | { 76 | StringBuilder result = new StringBuilder(); 77 | 78 | result.AppendLine("Channels: " + Channels); 79 | result.AppendLine("Sample Rate: " + SampleRate); 80 | result.AppendLine("Sample Size: " + SampleSize); 81 | result.AppendLine("Duration: " + Duration.ToString(@"hh\:mm\:ss\.ff")); 82 | result.AppendLine("Size: " + Size); 83 | result.AppendLine("BitRate: " + BitRate); 84 | result.AppendLine("Format: " + Format); 85 | 86 | return result.ToString(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Effects/AllPassFilterEffect.cs: -------------------------------------------------------------------------------- 1 | using SoxSharp.Effects.Types; 2 | using System.Collections.Generic; 3 | 4 | 5 | namespace SoxSharp.Effects 6 | { 7 | /// 8 | /// Apply a two-pole all-pass filter with a central frequency and width. An all-pass filter changes the audio’s 9 | /// frequency to phase relationship without changing its frequency to amplitude relationship. 10 | /// 11 | public class AllPassFilterEffect : BaseEffect 12 | { 13 | /// 14 | /// SoX effect name. 15 | /// 16 | public override string Name { get { return "allpass"; } } 17 | 18 | /// 19 | /// Central frequency. 20 | /// 21 | public Frequency Frequency { get; set; } 22 | 23 | /// 24 | /// Filter width. 25 | /// 26 | public Width Width { get; set; } 27 | 28 | 29 | /// 30 | /// Initializes a new instance of the class. 31 | /// 32 | /// Central frequency. 33 | /// Filter width. 34 | public AllPassFilterEffect(double frequency, double width) 35 | { 36 | Frequency = frequency; 37 | Width = width; 38 | } 39 | 40 | 41 | /// 42 | /// Initializes a new instance of the class. 43 | /// 44 | /// Central frequency. 45 | /// Filter width. 46 | public AllPassFilterEffect(Frequency frequency, Width width) 47 | { 48 | Frequency = frequency; 49 | Width = width; 50 | } 51 | 52 | 53 | /// 54 | /// Translate an instance to a set of command arguments to be passed to SoX. 55 | /// (invalidates ). 56 | /// 57 | /// A containing SoX command arguments to apply an All-Pass Filter 58 | /// effect. 59 | public override string ToString() 60 | { 61 | List effectArgs = new List(3) { Name }; 62 | 63 | effectArgs.Add(Frequency.ToString()); 64 | effectArgs.Add(Width.ToString()); 65 | 66 | return string.Join(" ", effectArgs); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Effects/BandPassFilterEffect.cs: -------------------------------------------------------------------------------- 1 | using SoxSharp.Effects.Types; 2 | using System.Collections.Generic; 3 | 4 | 5 | namespace SoxSharp.Effects 6 | { 7 | /// 8 | /// Apply a two-pole Butterworth band-pass filter with a central frequency and a (3dB-point) bandwidth. 9 | /// The filter roll off at 6dB per octave (20dB per decade). 10 | /// 11 | public class BandPassFilterEffect : BaseEffect 12 | { 13 | /// 14 | /// SoX effect name. 15 | /// 16 | public override string Name { get { return "bandpass"; } } 17 | 18 | /// 19 | /// Central frequency. 20 | /// 21 | public Frequency Frequency { get; set; } 22 | 23 | /// 24 | /// Filter width. 25 | /// 26 | public Width Width { get; set; } 27 | 28 | /// 29 | /// Selects a constant skirt gain (peak gain = Q) instead of the default constant 0dB peak gain. 30 | /// 31 | public bool? SkirtGain { get; set; } 32 | 33 | 34 | /// 35 | /// 36 | /// 37 | /// Central frequency. 38 | /// Filter width. 39 | public BandPassFilterEffect(double frequency, double width) 40 | { 41 | Frequency = frequency; 42 | Width = width; 43 | } 44 | 45 | 46 | /// 47 | /// 48 | /// 49 | /// Central frequency. 50 | /// Filter width. 51 | public BandPassFilterEffect(Frequency frequency, Width width) 52 | { 53 | Frequency = frequency; 54 | Width = width; 55 | } 56 | 57 | 58 | /// 59 | /// Translate an instance to a set of command arguments to be passed to SoX. 60 | /// (invalidates ). 61 | /// 62 | /// A containing SoX command arguments to apply an Band-Pass Filter effect. 63 | public override string ToString() 64 | { 65 | List effectArgs = new List(4) { Name }; 66 | 67 | if (SkirtGain.HasValue && (SkirtGain.Value == true)) 68 | effectArgs.Add("-c"); 69 | 70 | effectArgs.Add(Frequency.ToString()); 71 | effectArgs.Add(Width.ToString()); 72 | 73 | return string.Join(" ", effectArgs); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Effects/BandRejectFilterEffect.cs: -------------------------------------------------------------------------------- 1 | using SoxSharp.Effects.Types; 2 | using System.Collections.Generic; 3 | 4 | 5 | namespace SoxSharp.Effects 6 | { 7 | /// 8 | /// Apply a two-pole Butterworth band-reject filter with a central frequency and a (3dB-point) bandwidth. 9 | /// The filter roll off at 6dB per octave (20dB per decade). 10 | /// 11 | public class BandRejectFilterEffect : BaseEffect 12 | { 13 | /// 14 | /// SoX effect name. 15 | /// 16 | public override string Name { get { return "bandreject"; } } 17 | 18 | /// 19 | /// Central frequency. 20 | /// 21 | public Frequency Frequency { get; set; } 22 | 23 | /// 24 | /// Filter width. 25 | /// 26 | public Width Width { get; set; } 27 | 28 | 29 | /// 30 | /// Initializes a new instance of the class. 31 | /// 32 | /// Central frequency. 33 | /// Filter width. 34 | public BandRejectFilterEffect(double frequency, double width) 35 | { 36 | Frequency = frequency; 37 | Width = width; 38 | } 39 | 40 | 41 | /// 42 | /// Initializes a new instance of the class. 43 | /// 44 | /// Central frequency. 45 | /// Filter width. 46 | public BandRejectFilterEffect(Frequency frequency, Width width) 47 | { 48 | Frequency = frequency; 49 | Width = width; 50 | } 51 | 52 | 53 | /// 54 | /// Translate a instance to a set of command arguments to be passed to SoX. 55 | /// (invalidates ). 56 | /// 57 | /// A containing SoX command arguments to apply a Band-Reject Filter effect. 58 | public override string ToString() 59 | { 60 | List effectArgs = new List(3) { Name }; 61 | 62 | effectArgs.Add(Frequency.ToString()); 63 | effectArgs.Add(Width.ToString()); 64 | 65 | return string.Join(" ", effectArgs); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /src/Effects/BaseEffect.cs: -------------------------------------------------------------------------------- 1 | namespace SoxSharp.Effects 2 | { 3 | public abstract class BaseEffect : IBaseEffect 4 | { 5 | /// 6 | /// SoX effect name. 7 | /// 8 | public abstract string Name { get; } 9 | 10 | /// 11 | /// Translate an effect class instance to a set of command arguments to be passed to SoX. 12 | /// (invalidates ). 13 | /// 14 | /// A containing SoX command arguments to apply the effect. 15 | public abstract override string ToString(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Effects/BassEffect.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SoxSharp.Effects.Types; 3 | 4 | 5 | namespace SoxSharp.Effects 6 | { 7 | /// 8 | /// Boost or cut the bass (lower) frequencies of the audio using a two-pole shelving filter with a response 9 | /// similar to that of a standard hi-fi’s tone-controls. This is also known as shelving equalisation (EQ). 10 | /// 11 | public class BassEffect : BaseEffect 12 | { 13 | /// 14 | /// SoX effect name. 15 | /// 16 | public override string Name { get { return "bass"; } } 17 | 18 | /// 19 | /// Gives the gain at 0 Hz. Its useful range is about −20 (for a large cut) to +20 (for a large boost). 20 | /// Beware of Clipping when using a positive gain. 21 | /// 22 | public double Gain { get; set; } 23 | 24 | /// 25 | /// Sets the filter’s central frequency and so can be used to extend or reduce the frequency range to 26 | /// be boosted or cut. The default value is 100 Hz. 27 | /// 28 | public Frequency? Frequency { get; set; } 29 | 30 | /// 31 | /// Determines how steep is the filter’s shelf transition. In addition to the common width specification methods, 32 | /// ‘slope’ (the default) may be used. The useful range of ‘slope’ is about 0.3, for a gentle slope, to 1 33 | /// (the maximum), for a steep slope; the default value is 0.5. 34 | /// 35 | public Width? Width { get; set; } 36 | 37 | 38 | public BassEffect(double gain) 39 | { 40 | Gain = gain; 41 | } 42 | 43 | 44 | public BassEffect(double gain, double frequency) 45 | : this(gain) 46 | { 47 | Frequency = frequency; 48 | } 49 | 50 | 51 | public BassEffect(double gain, double frequency, double width) 52 | : this(gain, frequency) 53 | { 54 | Width = width; 55 | } 56 | 57 | 58 | public BassEffect(double gain, Frequency frequency) 59 | : this(gain) 60 | { 61 | Frequency = frequency; 62 | } 63 | 64 | 65 | public BassEffect(double gain, Frequency frequency, Width width) 66 | : this(gain, frequency) 67 | { 68 | Width = width; 69 | } 70 | 71 | /// 72 | /// Translate a instance to a set of command arguments to be passed to SoX. 73 | /// (invalidates ). 74 | /// 75 | /// A containing SoX command arguments to apply a Bass effect. 76 | public override string ToString() 77 | { 78 | List effectArgs = new List(4) { Name }; 79 | 80 | effectArgs.Add(Gain.ToString()); 81 | 82 | if (Frequency.HasValue) 83 | effectArgs.Add(Frequency.Value.ToString()); 84 | 85 | if (Width.HasValue) 86 | effectArgs.Add(Width.Value.ToString()); 87 | 88 | return string.Join(" ", effectArgs); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Effects/ContrastEffect.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | 4 | namespace SoxSharp.Effects 5 | { 6 | /// 7 | /// Comparable with compression, this effect modifies an audio signal to make it sound louder. 8 | /// 9 | public class ContrastEffect : BaseEffect 10 | { 11 | /// 12 | /// SoX effect name. 13 | /// 14 | public override string Name { get { return "contrast"; } } 15 | 16 | /// 17 | /// Controls the amount of the enhancement and is a number in the range 0−100 (default 75). Note that a value of 0 18 | /// still gives a significant contrast enhancement. 19 | /// 20 | public double Enhancement { get; set; } 21 | 22 | 23 | public ContrastEffect(double enhancement) 24 | { 25 | Enhancement = enhancement; 26 | } 27 | 28 | 29 | /// 30 | /// Translate a instance to a set of command arguments to be passed to SoX. 31 | /// (invalidates ). 32 | /// 33 | /// A containing SoX command arguments to apply a Contrast effect. 34 | public override string ToString() 35 | { 36 | List effectArgs = new List(2) { Name }; 37 | effectArgs.Add(Enhancement.ToString(CultureInfo.InvariantCulture)); 38 | 39 | return string.Join(" ", effectArgs); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Effects/HighPassFilterEffect.cs: -------------------------------------------------------------------------------- 1 | using SoxSharp.Effects.Types; 2 | using System.Collections.Generic; 3 | 4 | 5 | namespace SoxSharp.Effects 6 | { 7 | /// 8 | /// Apply a high-pass filter with 3dB point frequency. The filter can be either single-pole or double-pole 9 | /// (the default). The filter roll off at 6dB per pole per octave (20dB per pole per decade). 10 | /// 11 | public class HighPassFilterEffect : BaseEffect 12 | { 13 | /// 14 | /// SoX effect name. 15 | /// 16 | public override string Name { get { return "highpass"; } } 17 | 18 | /// 19 | /// Filter type (single-pole or double-pole). 20 | /// 21 | public FilterType? Type { get; set; } 22 | 23 | /// 24 | /// Central frequency. 25 | /// 26 | public Frequency Frequency { get; set; } 27 | 28 | /// 29 | /// Filter width. Applies only to double-pole filter; the default is Q = 0.707 and gives a Butterworth response. 30 | /// 31 | public Width? Width { get; set; } 32 | 33 | 34 | /// 35 | /// Initializes a new instance of the class. 36 | /// 37 | /// Central frequency. 38 | public HighPassFilterEffect(double frequency) 39 | { 40 | Frequency = frequency; 41 | } 42 | 43 | 44 | /// 45 | /// Initializes a new instance of the class. 46 | /// 47 | /// Central frequency. 48 | /// Filter width. 49 | public HighPassFilterEffect(double frequency, double width) 50 | : this(frequency) 51 | { 52 | Width = width; 53 | } 54 | 55 | 56 | /// 57 | /// Initializes a new instance of the class. 58 | /// 59 | /// Central frequency. 60 | public HighPassFilterEffect(Frequency frequency) 61 | { 62 | Frequency = frequency; 63 | } 64 | 65 | 66 | /// 67 | /// Initializes a new instance of the class. 68 | /// 69 | /// Central frequency. 70 | /// Filter width. 71 | public HighPassFilterEffect(Frequency frequency, Width width) 72 | : this(frequency) 73 | { 74 | Width = width; 75 | } 76 | 77 | 78 | /// 79 | /// Translate a instance to a set of command arguments to be passed to SoX. 80 | /// (invalidates ). 81 | /// 82 | /// A containing SoX command arguments to apply a High-Pass Filter effect. 83 | public override string ToString() 84 | { 85 | List effectArgs = new List(4) { Name }; 86 | 87 | if (Type.HasValue) 88 | { 89 | switch (Type.Value) 90 | { 91 | case FilterType.SinglePole: 92 | 93 | effectArgs.Add("-1"); 94 | break; 95 | 96 | case FilterType.DoublePole: 97 | 98 | effectArgs.Add("-2"); 99 | break; 100 | 101 | default: 102 | 103 | // Do nothing; 104 | break; 105 | } 106 | } 107 | 108 | effectArgs.Add(Frequency.ToString()); 109 | 110 | if (Width.HasValue) 111 | effectArgs.Add(Width.Value.ToString()); 112 | 113 | return string.Join(" ", effectArgs); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Effects/IBaseEffect.cs: -------------------------------------------------------------------------------- 1 | namespace SoxSharp.Effects 2 | { 3 | public interface IBaseEffect 4 | { 5 | /// 6 | /// SoX effect name. 7 | /// 8 | string Name { get; } 9 | 10 | /// 11 | /// Translate an effect class instance to a set of command arguments to be passed to SoX. 12 | /// (invalidates ). 13 | /// 14 | /// A containing SoX command arguments to apply the effect. 15 | string ToString(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Effects/LoudnessEffect.cs: -------------------------------------------------------------------------------- 1 | using SoxSharp.Exceptions; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | 5 | namespace SoxSharp.Effects 6 | { 7 | /// 8 | /// Loudness control. Similar to the gain effect, but provides equalisation for the human auditory system. See 9 | /// http://en.wikipedia.org/wiki/Loudness for a detailed description of loudness. The gain adjustment is usually 10 | /// negative and the signal is equalised according to ISO 226 w.r.t. a reference level of 65dB. An alternative 11 | /// reference level may be given if the original audio has been equalised for some other optimal level. 12 | /// 13 | public class LoudnessEffect : BaseEffect 14 | { 15 | /// 16 | /// SoX effect name. 17 | /// 18 | public override string Name { get { return "loudness"; } } 19 | 20 | /// 21 | /// Gain adjustment. A default gain of −10dB is used if no value is given. 22 | /// 23 | public double? Gain { get; set; } 24 | 25 | /// 26 | /// Alternative reference level to use. 27 | /// 28 | public double? Reference { get; set; } 29 | 30 | 31 | /// 32 | /// Initializes a new instance of the class. 33 | /// 34 | public LoudnessEffect() 35 | { 36 | } 37 | 38 | /// 39 | /// Initializes a new instance of the class. 40 | /// 41 | /// Volume gain. 42 | public LoudnessEffect(double gain) 43 | { 44 | Gain = gain; 45 | } 46 | 47 | 48 | /// 49 | /// Initializes a new instance of the class. 50 | /// 51 | /// Volume gain. 52 | /// How to interpret the gain value. 53 | public LoudnessEffect(double gain, double reference) 54 | : this(gain) 55 | { 56 | Reference = reference; 57 | } 58 | 59 | 60 | /// 61 | /// Translate a instance to a set of command arguments to be passed to SoX to be applied 62 | /// to the input file (invalidates ). 63 | /// 64 | /// A containing SoX command arguments to apply a Loudness effect. 65 | public override string ToString() 66 | { 67 | if (Reference.HasValue && !Gain.HasValue) 68 | throw new SoxEffectException(Name, "Reference value set without specifying a Gain value."); 69 | 70 | List effectArgs = new List(3) { Name }; 71 | 72 | if (Gain.HasValue) 73 | effectArgs.Add(Gain.Value.ToString(CultureInfo.InvariantCulture)); 74 | 75 | if (Reference.HasValue) 76 | effectArgs.Add(Reference.Value.ToString(CultureInfo.InvariantCulture)); 77 | 78 | return string.Join(" ", effectArgs); 79 | } 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /src/Effects/LowPassFilterEffect.cs: -------------------------------------------------------------------------------- 1 | using SoxSharp.Effects.Types; 2 | using System.Collections.Generic; 3 | 4 | 5 | namespace SoxSharp.Effects 6 | { 7 | /// 8 | /// Apply a low-pass filter with 3dB point frequency. The filter can be either single-pole or double-pole 9 | /// (the default). The filter roll off at 6dB per pole per octave (20dB per pole per decade). 10 | /// 11 | public class LowPassFilterEffect : BaseEffect 12 | { 13 | /// 14 | /// SoX effect name. 15 | /// 16 | public override string Name { get { return "lowpass"; } } 17 | 18 | public FilterType? Type { get; set; } 19 | 20 | public Frequency Frequency { get; set; } 21 | 22 | /// 23 | /// Applies only to double-pole filter; the default is Q = 0.707 and gives a Butterworth response. 24 | /// 25 | public Width? Width { get; set; } 26 | 27 | 28 | /// 29 | /// Initializes a new instance of the class. 30 | /// 31 | public LowPassFilterEffect(double frequency) 32 | { 33 | Frequency = frequency; 34 | } 35 | 36 | 37 | /// 38 | /// Initializes a new instance of the class. 39 | /// 40 | public LowPassFilterEffect(double frequency, double width) 41 | : this(frequency) 42 | { 43 | Width = width; 44 | } 45 | 46 | 47 | public LowPassFilterEffect(Frequency frequency) 48 | { 49 | Frequency = frequency; 50 | } 51 | 52 | 53 | public LowPassFilterEffect(Frequency frequency, Width width) 54 | : this(frequency) 55 | { 56 | Width = width; 57 | } 58 | 59 | 60 | /// 61 | /// Translate a instance to a set of command arguments to be passed to SoX. 62 | /// (invalidates ). 63 | /// 64 | /// A containing SoX command arguments to apply a Low-Pass Filter effect. 65 | public override string ToString() 66 | { 67 | List effectArgs = new List(3) { Name }; 68 | 69 | if (Type.HasValue) 70 | { 71 | switch (Type.Value) 72 | { 73 | case FilterType.SinglePole: 74 | 75 | effectArgs.Add("-1"); 76 | break; 77 | 78 | case FilterType.DoublePole: 79 | 80 | effectArgs.Add("-2"); 81 | break; 82 | 83 | default: 84 | 85 | // Do nothing. 86 | break; 87 | } 88 | } 89 | 90 | effectArgs.Add(Frequency.ToString()); 91 | 92 | if (Width.HasValue) 93 | effectArgs.Add(Width.Value.ToString()); 94 | 95 | return string.Join(" ", effectArgs); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /src/Effects/NoiseProfileEffect.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | 4 | namespace SoxSharp.Effects 5 | { 6 | /// 7 | /// Calculate a profile of the audio for use in noise reduction and save it to file. This effect is usually run on a 8 | /// section of audio (obtained adding a effect to the process chain) that 9 | /// ideally would contain silence but in fact contains noise. Such sections are typically found at the beginning or 10 | /// the end of a recording. 11 | /// 12 | public class NoiseProfileEffect : BaseEffect 13 | { 14 | /// 15 | /// SoX effect name. 16 | /// 17 | public override string Name { get { return "noiseprof"; } } 18 | 19 | public string File { get; set; } 20 | 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// File. 26 | public NoiseProfileEffect(string file) 27 | { 28 | File = file; 29 | } 30 | 31 | 32 | /// 33 | /// Translate a instance to a set of command arguments to be passed to SoX to be applied to the input file (invalidates ). 34 | /// 35 | /// A containing SoX command arguments to apply a Noise Profile effect. 36 | public override string ToString() 37 | { 38 | List effectArgs = new List(2) { Name }; 39 | effectArgs.Add(File); 40 | 41 | return string.Join(" ", effectArgs); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Effects/NoiseReductionEffect.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | 4 | namespace SoxSharp.Effects 5 | { 6 | /// 7 | /// Reduce noise in the audio signal by profiling and filtering. This effect is moderately effective at removing 8 | /// consistent background noise such as hiss or hum. Previous to use this effect, a noise profile of the file should 9 | /// be obatined using . 10 | /// 11 | public class NoiseReductionEffect : BaseEffect 12 | { 13 | /// 14 | /// SoX effect name. 15 | /// 16 | public override string Name { get { return "noisered"; } } 17 | 18 | public string File { get; set; } 19 | 20 | /// 21 | /// How much noise should be removed. Valid values are between 0 and 1, with a default of 0.5. Higher numbers will 22 | /// remove more noise but present a greater likelihood of removing wanted components of the audio signal. 23 | /// 24 | public double? Amount { get; set; } 25 | 26 | 27 | public NoiseReductionEffect(string profile) 28 | { 29 | File = profile; 30 | } 31 | 32 | 33 | public NoiseReductionEffect(string profile, double amount) 34 | : this(profile) 35 | { 36 | Amount = amount; 37 | } 38 | 39 | 40 | /// 41 | /// Translate a instance to a set of command arguments to be passed to SoX. 42 | /// (invalidates ). 43 | /// 44 | /// A containing SoX command arguments to apply a Noise Reduction effect. 45 | public override string ToString() 46 | { 47 | List effectArgs = new List(3) { Name }; 48 | effectArgs.Add(File); 49 | 50 | if (Amount.HasValue) 51 | effectArgs.Add(Amount.Value.ToString(CultureInfo.InvariantCulture)); 52 | 53 | return string.Join(" ", effectArgs); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Effects/ReverseEffect.cs: -------------------------------------------------------------------------------- 1 | namespace SoxSharp.Effects 2 | { 3 | /// 4 | /// Reverse the audio completely. Requires temporary file space to store the audio to be reversed. 5 | /// 6 | public class ReverseEffect : BaseEffect 7 | { 8 | /// 9 | /// SoX effect name. 10 | /// 11 | public override string Name { get { return "reverse"; } } 12 | 13 | 14 | /// 15 | /// Translate a instance to a set of command arguments to be passed to SoX. 16 | /// (invalidates ). 17 | /// 18 | /// A containing SoX command arguments to apply a Reverse effect. 19 | public override string ToString() 20 | { 21 | return Name; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Effects/SpeedEffect.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Text; 3 | 4 | 5 | namespace SoxSharp.Effects 6 | { 7 | /// 8 | /// Adjust the audio speed (pitch and tempo together). Technically, the speed effect only changes the sample rate 9 | /// information, leaving the samples themselves untouched. The rate effect is invoked automatically to resample to 10 | /// the output sample rate, using its default quality/speed. For higher quality or higher speed resampling, in 11 | /// addition to the speed effect, specify the rate effect with the desired quality option. 12 | /// 13 | public class SpeedEffect : BaseEffect 14 | { 15 | /// 16 | /// SoX effect name. 17 | /// 18 | public override string Name { get { return "speed"; } } 19 | 20 | /// 21 | /// The ratio of the new speed to the old speed: greater than 1 speeds up, less than 1 slows down. 22 | /// 23 | public double Factor { get; set; } 24 | 25 | /// 26 | /// Interpret Factor as number of cents (i.e. 100ths of a semitone) by which the pitch (and tempo) should be 27 | /// adjusted: greater than 0 increases, less than 0 decreases. 28 | /// 29 | public bool? AsCents { get; set; } 30 | 31 | 32 | public SpeedEffect(double factor) 33 | { 34 | Factor = factor; 35 | } 36 | 37 | 38 | public SpeedEffect(double factor, bool asCents) 39 | : this(factor) 40 | { 41 | AsCents = asCents; 42 | } 43 | 44 | 45 | /// 46 | /// Translate a instance to a set of command arguments to be passed to SoX. 47 | /// (invalidates ). 48 | /// 49 | /// A containing SoX command arguments to apply a Speed effect. 50 | public override string ToString() 51 | { 52 | StringBuilder effectArgs = new StringBuilder(Name); 53 | effectArgs.Append(" " + Factor.ToString(CultureInfo.InvariantCulture)); 54 | 55 | if (AsCents.HasValue && (AsCents.Value == true)) 56 | effectArgs.Append("c"); 57 | 58 | return effectArgs.ToString(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Effects/TempoEffect.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | using SoxSharp.Effects.Types; 4 | using SoxSharp.Exceptions; 5 | 6 | 7 | namespace SoxSharp.Effects 8 | { 9 | /// 10 | /// Change the audio playback speed but not its pitch. 11 | /// This effect uses the WSOLA algorithm. The audio is chopped up into segments which are then shifted in 12 | /// the time domain and overlapped (cross-faded) at points where their waveforms are most similar as 13 | /// determined by measurement of ‘least squares’. 14 | /// 15 | public class TempoEffect: BaseEffect 16 | { 17 | /// 18 | /// SoX effect name. 19 | /// 20 | public override string Name { get { return "tempo"; } } 21 | 22 | /// 23 | /// Ratio of new tempo to the old tempo. 24 | /// A value of 1.1 speeds up the tempo by 10%, 0.9 slows it down by 10%. 25 | /// 26 | public double Factor { get; set; } 27 | 28 | /// 29 | /// Use tree searches instead of linear searches to find the best overlapping points. 30 | /// This makes the effect work more quickly, but the result may not sound as good. 31 | /// However, if you must improve the processing speed, this generally reduces the sound 32 | /// quality less than reducing the search or overlap values. 33 | /// 34 | public bool? UseTreeSearches; 35 | 36 | /// 37 | /// Calculates default value of Segment based on factor, and default Search and Overlap values based on Segment. 38 | /// 39 | public OptimizationType? Optimization; 40 | 41 | /// 42 | /// Algorithm’s segment size in milliseconds. 43 | /// If no other flags are specified, the default value is 82 and is typically suited to making small changes to the tempo of music. 44 | /// For larger changes (e.g. a factor of 2), 41 ms may give a better result. 45 | /// 46 | public double? Segment { get; set; } 47 | 48 | /// 49 | /// Audio length in milliseconds over which the algorithm will search for overlapping points. 50 | /// If no other flags are specified, the default value is 14.68. Larger values use more processing time and may or may not produce better results. 51 | /// A practical maximum is half the value of segment. Search can be reduced to cut processing time at the risk of degrading output quality. 52 | /// The −m, −s, and −l flags will cause the search default to be automatically adjusted based on segment. 53 | /// 54 | public double? Search { get; set; } 55 | 56 | /// 57 | /// Segment overlap length in milliseconds. 58 | /// Default value is 12 but −m, −s, or −l flags automatically adjust overlap based on segment size. 59 | /// Increasing overlap increases processing time and may increase quality. A practical maximum for overlap 60 | /// is the value of search, with overlap typically being (at least) a little smaller than search. 61 | /// 62 | public double? Overlap { get; set; } 63 | 64 | 65 | /// 66 | /// Initializes a new instance of the class. 67 | /// 68 | /// Ratio of new tempo to the old tempo. 69 | public TempoEffect(double factor) 70 | { 71 | Factor = factor; 72 | } 73 | 74 | 75 | /// 76 | /// Initializes a new instance of the class. 77 | /// 78 | /// Ratio of new tempo to the old tempo. 79 | /// Algorithm’s segment size in milliseconds. 80 | public TempoEffect(double factor, double segment) 81 | : this (factor) 82 | { 83 | Segment = segment; 84 | } 85 | 86 | 87 | /// 88 | /// Initializes a new instance of the class. 89 | /// 90 | /// Ratio of new tempo to the old tempo. 91 | /// Algorithm’s segment size in milliseconds. 92 | /// Audio length in milliseconds over which the algorithm will search for overlapping points. 93 | public TempoEffect(double factor, double segment, double search) 94 | : this(factor, segment) 95 | { 96 | Search = search; 97 | } 98 | 99 | 100 | /// 101 | /// Initializes a new instance of the class. 102 | /// 103 | /// Ratio of new tempo to the old tempo. 104 | /// Algorithm’s segment size in milliseconds. 105 | /// Audio length in milliseconds over which the algorithm will search for overlapping points. 106 | /// Segment overlap length in milliseconds. 107 | public TempoEffect(double factor, double segment, double search, double overlap) 108 | : this(factor, segment, search) 109 | { 110 | Overlap = overlap; 111 | } 112 | 113 | 114 | /// 115 | /// Translate a instance to a set of command arguments to be passed to SoX. 116 | /// (invalidates ). 117 | /// 118 | /// A containing SoX command arguments to apply a Tempo effect. 119 | public override string ToString() 120 | { 121 | if (Search.HasValue && !Segment.HasValue) 122 | throw new SoxEffectException(Name, "Search value set without specifying a Segment value."); 123 | 124 | if (Overlap.HasValue && (!Search.HasValue || !Segment.HasValue)) 125 | throw new SoxEffectException(Name, "Overlap value set without specifying Segment and Search values."); 126 | 127 | List effectArgs = new List(6) { Name }; 128 | 129 | if (UseTreeSearches.HasValue) 130 | effectArgs.Add("-q"); 131 | 132 | if (Optimization.HasValue) 133 | { 134 | switch (Optimization.Value) 135 | { 136 | case OptimizationType.Music: 137 | 138 | effectArgs.Add("-m"); 139 | break; 140 | 141 | case OptimizationType.Speech: 142 | 143 | effectArgs.Add("-s"); 144 | break; 145 | 146 | case OptimizationType.LinearProcessing: 147 | 148 | effectArgs.Add("-l"); 149 | break; 150 | 151 | default: 152 | 153 | // Do nothing. 154 | break; 155 | } 156 | } 157 | 158 | effectArgs.Add(Factor.ToString(CultureInfo.InvariantCulture)); 159 | 160 | if (Segment.HasValue) 161 | effectArgs.Add(Segment.Value.ToString(CultureInfo.InvariantCulture)); 162 | 163 | if (Search.HasValue) 164 | effectArgs.Add(Search.Value.ToString(CultureInfo.InvariantCulture)); 165 | 166 | if (Overlap.HasValue) 167 | effectArgs.Add(Overlap.Value.ToString(CultureInfo.InvariantCulture)); 168 | 169 | return string.Join(" ", effectArgs); 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/Effects/TrebleEffect.cs: -------------------------------------------------------------------------------- 1 | using SoxSharp.Effects.Types; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | 5 | namespace SoxSharp.Effects 6 | { 7 | /// 8 | /// Boost or cut the treble (upper) frequencies of the audio using a two-pole shelving filter with a response similar 9 | /// to that of a standard hi-fi’s tone-controls. This is also known as shelving equalisation (EQ). 10 | /// 11 | public class TrebleEffect : BaseEffect 12 | { 13 | /// 14 | /// SoX effect name. 15 | /// 16 | public override string Name { get { return "treble"; } } 17 | 18 | /// 19 | /// Gives the gain at whichever is the lower of ∼22 kHz and the Nyquist frequency. Its useful range is about −20 20 | /// (for a large cut) to +20 (for a large boost). Beware of Clipping when using a positive gain. 21 | /// 22 | public double Gain { get; set; } 23 | 24 | /// 25 | /// Sets the filter’s central frequency and so can be used to extend or reduce the frequency range to 26 | /// be boosted or cut. The default value is 3 kHz. 27 | /// 28 | public Frequency? Frequency { get; set; } 29 | 30 | /// 31 | /// Determines how steep is the filter’s shelf transition. In addition to the common width specification methods, 32 | /// ‘slope’ (the default) may be used. The useful range of ‘slope’ is about 0.3, for a gentle slope, to 1 33 | /// (the maximum), for a steep slope; the default value is 0.5. 34 | /// 35 | public Width? Width { get; set; } 36 | 37 | 38 | /// 39 | /// Initializes a new instance of the class. 40 | /// 41 | /// Gain value. 42 | public TrebleEffect(double gain) 43 | { 44 | Gain = gain; 45 | } 46 | 47 | 48 | /// 49 | /// Initializes a new instance of the class. 50 | /// 51 | /// Gain value. 52 | /// Central frequency. 53 | public TrebleEffect(double gain, double frequency) 54 | : this(gain) 55 | { 56 | Frequency = frequency; 57 | } 58 | 59 | 60 | /// 61 | /// Initializes a new instance of the class. 62 | /// 63 | /// Gain value. 64 | /// Central frequency. 65 | /// Shelf transition steep. 66 | public TrebleEffect(double gain, double frequency, double width) 67 | : this(gain, frequency) 68 | { 69 | Width = width; 70 | } 71 | 72 | 73 | /// 74 | /// Initializes a new instance of the class. 75 | /// 76 | /// Gain value. 77 | /// Central frequency. 78 | public TrebleEffect(double gain, Frequency frequency) 79 | : this(gain) 80 | { 81 | Frequency = frequency; 82 | } 83 | 84 | 85 | /// 86 | /// Initializes a new instance of the class. 87 | /// 88 | /// Gain value. 89 | /// Central frequency. 90 | /// Shelf transition steep. 91 | public TrebleEffect(double gain, Frequency frequency, Width width) 92 | : this(gain, frequency) 93 | { 94 | Width = width; 95 | } 96 | 97 | 98 | /// 99 | /// Translate a instance to a set of command arguments to be passed to SoX. 100 | /// (invalidates ). 101 | /// 102 | /// A containing SoX command arguments to apply a Treble effect. 103 | public override string ToString() 104 | { 105 | List effectArgs = new List(4) { Name }; 106 | 107 | effectArgs.Add(Gain.ToString(CultureInfo.InvariantCulture)); 108 | 109 | if (Frequency.HasValue) 110 | effectArgs.Add(Frequency.Value.ToString()); 111 | 112 | if (Width.HasValue) 113 | effectArgs.Add(Width.Value.ToString()); 114 | 115 | return string.Join(" ", effectArgs); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Effects/TremoloEffect.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | 4 | namespace SoxSharp.Effects 5 | { 6 | /// 7 | /// Apply a tremolo (low frequency amplitude modulation) effect to the audio. 8 | /// 9 | public class TremoloEffect : BaseEffect 10 | { 11 | /// 12 | /// SoX effect name. 13 | /// 14 | public override string Name { get { return "tremolo"; } } 15 | 16 | /// 17 | /// Tremolo frequency in Hz. 18 | /// 19 | public double Speed { get; set; } 20 | 21 | /// 22 | /// Tremolo depth as a percentage (default is 40). 23 | /// 24 | public ushort? Depth { get; set; } 25 | 26 | 27 | public TremoloEffect(double speed) 28 | { 29 | Speed = speed; 30 | } 31 | 32 | 33 | public TremoloEffect(double speed, ushort depth) 34 | : this(speed) 35 | { 36 | Depth = depth; 37 | } 38 | 39 | 40 | /// 41 | /// Translate a instance to a set of command arguments to be passed to SoX. 42 | /// (invalidates ). 43 | /// 44 | /// A containing SoX command arguments to apply a Tremolo effect. 45 | public override string ToString() 46 | { 47 | List effectArgs = new List(3) { Name }; 48 | 49 | effectArgs.Add(Speed.ToString(CultureInfo.InvariantCulture)); 50 | 51 | if (Depth.HasValue) 52 | effectArgs.Add(Depth.Value.ToString()); 53 | 54 | return string.Join(" ", effectArgs); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Effects/TrimEffect.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SoxSharp.Effects.Types; 3 | 4 | 5 | namespace SoxSharp.Effects 6 | { 7 | /// 8 | /// Cuts portions out of the audio. Any number of positions may be given; audio is not sent to the output until the 9 | /// first position is reached. The effect then alternates between copying and discarding audio at each position. 10 | /// Using a value of 0 for the first position parameter allows copying from the beginning of the audio. 11 | /// 12 | public class TrimEffect : BaseEffect 13 | { 14 | /// 15 | /// SoX effect name. 16 | /// 17 | public override string Name { get { return "trim"; } } 18 | 19 | /// 20 | /// The positions. 21 | /// 22 | public readonly List Positions = new List(); 23 | 24 | 25 | /// 26 | /// Initializes a new instance of the class. 27 | /// 28 | /// Position to use. 29 | public TrimEffect(Position position) 30 | { 31 | Positions.Add(position); 32 | } 33 | 34 | 35 | /// 36 | /// Initializes a new instance of the class. 37 | /// 38 | /// First position to use. 39 | /// Second position to use. 40 | public TrimEffect(Position position1, Position position2) 41 | { 42 | Positions.Add(position1); 43 | Positions.Add(position2); 44 | } 45 | 46 | 47 | /// 48 | /// Initializes a new instance of the class. 49 | /// 50 | /// Positions to use. 51 | public TrimEffect(Position[] positions) 52 | { 53 | Positions.AddRange(positions); 54 | } 55 | 56 | 57 | /// 58 | /// Translate a instance to a set of command arguments to be passed to SoX. 59 | /// (invalidates ). 60 | /// 61 | /// A containing SoX command arguments to apply a Trim effect. 62 | public override string ToString() 63 | { 64 | return Name + " " + string.Join(" ", Positions); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Effects/Types/FilterType.cs: -------------------------------------------------------------------------------- 1 | namespace SoxSharp.Effects.Types 2 | { 3 | public enum FilterType 4 | { 5 | SinglePole, 6 | DoublePole 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Effects/Types/Frequency.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace SoxSharp.Effects.Types 4 | { 5 | public struct Frequency 6 | { 7 | private readonly double value_; 8 | 9 | private readonly FrequencyUnits? units_; 10 | 11 | 12 | public Frequency(double value) 13 | { 14 | value_ = value; 15 | units_ = null; 16 | } 17 | 18 | 19 | public Frequency(double value, FrequencyUnits units) 20 | { 21 | value_ = value; 22 | units_ = units; 23 | } 24 | 25 | 26 | public static implicit operator Frequency(double value) 27 | { 28 | return new Frequency(value); 29 | } 30 | 31 | 32 | public override string ToString() 33 | { 34 | if (units_.HasValue) 35 | { 36 | switch (units_.Value) 37 | { 38 | case FrequencyUnits.Hz: 39 | return value_.ToString(CultureInfo.InvariantCulture) + "h"; 40 | 41 | case FrequencyUnits.KHz: 42 | return value_.ToString(CultureInfo.InvariantCulture) + "k"; 43 | 44 | default: 45 | break; // Do nothing. 46 | } 47 | } 48 | 49 | return value_.ToString(CultureInfo.InvariantCulture); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Effects/Types/FrequencyUnits.cs: -------------------------------------------------------------------------------- 1 | namespace SoxSharp.Effects.Types 2 | { 3 | public enum FrequencyUnits 4 | { 5 | Hz, 6 | KHz 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Effects/Types/GainType.cs: -------------------------------------------------------------------------------- 1 | namespace SoxSharp.Effects.Types 2 | { 3 | /// 4 | /// When type is amplitude or power, a gain of 1 leaves the volume unchanged, less than 1 decreases it, and greater than 1 increases it; a negative gain inverts the audio signal in addition to adjusting its volume. 5 | /// 6 | public enum GainType 7 | { 8 | /// 9 | /// Gain is an amplitude (i.e. voltage or linear) ratio. A gain of 1 leaves the volume unchanged, less than 1 10 | /// decreases it, and greater than 1 increases it; a negative gain inverts the audio signal in addition to 11 | /// adjusting its volume. 12 | /// 13 | Amplitude, 14 | 15 | /// 16 | /// Gain is a power (i.e. wattage or voltage-squared) ratio. A gain of 1 leaves the volume unchanged, less than 1 17 | /// decreases it, and greater than 1 increases it; a negative gain inverts the audio signal in addition to 18 | /// adjusting its volume. 19 | /// 20 | Power, 21 | 22 | /// 23 | /// Gain is a power change in dB. A gain of 0 leaves the volume unchanged, less than 0 decreases it, 24 | /// and greater than 0 increases it. 25 | /// 26 | Db 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Effects/Types/OptimizationType.cs: -------------------------------------------------------------------------------- 1 | namespace SoxSharp.Effects.Types 2 | { 3 | public enum OptimizationType 4 | { 5 | /// 6 | /// Optimize default values of segment, search and overlap for music processing. 7 | /// 8 | Music, 9 | 10 | /// 11 | /// Optimize default values of segment, search and overlap for speech processing. 12 | /// 13 | Speech, 14 | 15 | /// 16 | /// Optimize default values of segment, search and overlap for ‘linear’ processing that tends to cause more noticeable distortion but may be useful when factor is close to 1. 17 | /// 18 | LinearProcessing 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Effects/Types/Position.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | 5 | namespace SoxSharp.Effects.Types 6 | { 7 | /// 8 | /// A position within the audio stream. 9 | /// 10 | public struct Position 11 | { 12 | /// 13 | /// Whether the position is to be interpreted relative to the start, end or the previous position if the effect accepts multiple position arguments. The audio length must be known for end-relative locations to work. 14 | /// 15 | private readonly PositionFrom? from_; 16 | 17 | /// 18 | /// Position expressed as a time value. 19 | /// 20 | private readonly TimeSpan? time_; 21 | 22 | /// 23 | /// Position expressed as number of samples. 24 | /// 25 | private readonly uint? samples_; 26 | 27 | 28 | /// 29 | /// Initializes a new instance of the struct. 30 | /// 31 | /// Position expressed as a time value. 32 | public Position(TimeSpan time) 33 | { 34 | time_ = time; 35 | samples_ = null; 36 | from_ = null; 37 | } 38 | 39 | 40 | /// 41 | /// Initializes a new instance of the struct. 42 | /// 43 | /// Position expressed as a time value. 44 | /// How 45 | public Position(TimeSpan time, PositionFrom from) 46 | { 47 | time_ = time; 48 | samples_ = null; 49 | from_ = from; 50 | } 51 | 52 | 53 | /// 54 | /// Initializes a new instance of the struct. 55 | /// 56 | /// Position expressed as number of samples. 57 | public Position(uint samples) 58 | { 59 | time_ = null; 60 | samples_ = samples; 61 | from_ = null; 62 | } 63 | 64 | /// 65 | /// Initializes a new instance of the struct. 66 | /// 67 | /// Position expressed as number of samples. 68 | /// Whether the position is to be interpreted relative to the start, end or the previous position. 69 | public Position(uint samples, PositionFrom from) 70 | { 71 | time_ = null; 72 | samples_ = samples; 73 | from_ = from; 74 | } 75 | 76 | 77 | public static implicit operator Position(uint samples) 78 | { 79 | return new Position(samples); 80 | } 81 | 82 | 83 | public static implicit operator Position(TimeSpan time) 84 | { 85 | return new Position(time); 86 | } 87 | 88 | 89 | public override string ToString() 90 | { 91 | List position = new List(3); 92 | 93 | if (from_.HasValue) 94 | { 95 | switch (from_.Value) 96 | { 97 | case PositionFrom.Start: 98 | position.Add("="); 99 | break; 100 | 101 | case PositionFrom.End: 102 | position.Add("-"); 103 | break; 104 | 105 | case PositionFrom.Last: 106 | position.Add("+"); 107 | break; 108 | 109 | default: 110 | // Do nothing; 111 | break; 112 | } 113 | } 114 | 115 | if (time_.HasValue) 116 | position.Add(time_.Value.ToString(@"hh\:mm\:ss\.ff")); 117 | else if (samples_.HasValue) 118 | position.Add(samples_.Value.ToString() + "s"); 119 | 120 | return string.Join("", position); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/Effects/Types/PositionFrom.cs: -------------------------------------------------------------------------------- 1 | namespace SoxSharp.Effects.Types 2 | { 3 | public enum PositionFrom 4 | { 5 | Start, 6 | End, 7 | Last 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Effects/Types/Width.cs: -------------------------------------------------------------------------------- 1 |  2 | 3 | using System.Globalization; 4 | 5 | namespace SoxSharp.Effects.Types 6 | { 7 | public struct Width 8 | { 9 | private readonly double value_; 10 | 11 | private readonly WidthUnits? units_; 12 | 13 | 14 | public Width(double value) 15 | { 16 | value_ = value; 17 | units_ = null; 18 | } 19 | 20 | 21 | public Width(double value, WidthUnits units) 22 | { 23 | value_ = value; 24 | units_ = units; 25 | } 26 | 27 | 28 | public static implicit operator Width(double value) 29 | { 30 | return new Width(value); 31 | } 32 | 33 | 34 | public override string ToString() 35 | { 36 | if (units_.HasValue) 37 | { 38 | switch (units_.Value) 39 | { 40 | case WidthUnits.Hz: 41 | return value_.ToString(CultureInfo.InvariantCulture) + "h"; 42 | 43 | case WidthUnits.KHz: 44 | return value_.ToString(CultureInfo.InvariantCulture) + "k"; 45 | 46 | case WidthUnits.Octaves: 47 | return value_.ToString(CultureInfo.InvariantCulture) + "o"; 48 | 49 | case WidthUnits.Qfactor: 50 | return value_.ToString(CultureInfo.InvariantCulture) + "q"; 51 | 52 | default: 53 | break; // Do nothing. 54 | } 55 | } 56 | 57 | return value_.ToString(CultureInfo.InvariantCulture); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Effects/Types/WidthUnits.cs: -------------------------------------------------------------------------------- 1 | namespace SoxSharp.Effects.Types 2 | { 3 | public enum WidthUnits 4 | { 5 | Hz, 6 | KHz, 7 | Octaves, 8 | Qfactor 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Effects/VolumeEffect.cs: -------------------------------------------------------------------------------- 1 | using SoxSharp.Effects.Types; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | 5 | namespace SoxSharp.Effects 6 | { 7 | /// 8 | /// Apply an amplification or an attenuation to the audio signal. 9 | /// 10 | public class VolumeEffect : BaseEffect 11 | { 12 | /// 13 | /// SoX effect name. 14 | /// 15 | public override string Name { get { return "vol"; } } 16 | 17 | /// 18 | /// The amount to change the volume, interpreted according to the given type. 19 | /// 20 | public double Gain { get; set; } 21 | 22 | /// 23 | /// How to interpret the gain value. 24 | /// 25 | public GainType? Type { get; set; } 26 | 27 | /// 28 | /// Used only on peaks to prevent clipping. Should be a value much less than 1 (e.g. 0.05 or 0.02). 29 | /// 30 | public double? Limiter { get; set; } 31 | 32 | 33 | /// 34 | /// Initializes a new instance of the class. 35 | /// 36 | /// Volume gain. 37 | public VolumeEffect(double gain) 38 | { 39 | Gain = gain; 40 | } 41 | 42 | 43 | /// 44 | /// Initializes a new instance of the class. 45 | /// 46 | /// Volume gain. 47 | /// How to interpret the gain value. 48 | public VolumeEffect(double gain, GainType type) 49 | : this(gain) 50 | { 51 | Type = type; 52 | } 53 | 54 | 55 | /// 56 | /// Initializes a new instance of the class. 57 | /// 58 | /// Volume gain. 59 | /// How to interpret the gain value. 60 | /// Gain limiter. 61 | public VolumeEffect(double gain, GainType type, double limiter) 62 | : this(gain, type) 63 | { 64 | Limiter = limiter; 65 | } 66 | 67 | 68 | /// 69 | /// Translate a instance to a set of command arguments to be passed to SoX to be applied 70 | /// to the input file (invalidates ). 71 | /// 72 | /// A containing SoX command arguments to apply a Volume effect. 73 | public override string ToString() 74 | { 75 | List effectArgs = new List(4) { Name }; 76 | effectArgs.Add(Gain.ToString(CultureInfo.InvariantCulture)); 77 | 78 | if (Type.HasValue) 79 | { 80 | switch (Type.Value) 81 | { 82 | case GainType.Amplitude: 83 | 84 | effectArgs.Add("amplitude"); 85 | break; 86 | 87 | case GainType.Power: 88 | 89 | effectArgs.Add("power"); 90 | break; 91 | 92 | case GainType.Db: 93 | 94 | effectArgs.Add("dB"); 95 | break; 96 | 97 | default: 98 | // Do nothing. 99 | break; 100 | } 101 | } 102 | 103 | if (Limiter.HasValue) 104 | effectArgs.Add(Limiter.Value.ToString(CultureInfo.InvariantCulture)); 105 | 106 | return string.Join(" ", effectArgs); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Events.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | namespace SoxSharp 5 | { 6 | /// 7 | /// Provides data for the event. 8 | /// 9 | public class LogMessageEventArgs : EventArgs 10 | { 11 | /// 12 | /// Initializes a instance with the provided values. 13 | /// 14 | /// Message severity. 15 | /// Message source. 16 | /// Message text. 17 | public LogMessageEventArgs(LogLevelType logLevel, string source, string message) 18 | { 19 | LogLevel = logLevel; 20 | Source = source; 21 | Message = message; 22 | } 23 | 24 | /// 25 | /// Message severity. 26 | /// 27 | public LogLevelType LogLevel { get; private set; } 28 | 29 | /// 30 | /// SoX logger module. 31 | /// 32 | public string Source { get; private set; } 33 | 34 | /// 35 | /// Message text. 36 | /// 37 | public string Message { get; private set; } 38 | } 39 | 40 | 41 | /// 42 | /// Provides data for the event. 43 | /// 44 | public class ProgressEventArgs : EventArgs 45 | { 46 | /// 47 | /// Initializes a instance with the provided values. 48 | /// 49 | /// The actual progress value, from 0 to 100. 50 | /// File time that has been processed, based on file total duration. 51 | /// File time pending to be processed, based on file total duration. 52 | /// Actual size of the generated output file. 53 | public ProgressEventArgs(UInt16 progress, TimeSpan processed, TimeSpan remaining, UInt64 outputSize) 54 | { 55 | if (progress > 100) 56 | Progress = 100; 57 | else 58 | Progress = progress; 59 | 60 | Processed = processed; 61 | Remaining = remaining; 62 | OutputSize = outputSize; 63 | Abort = false; 64 | } 65 | 66 | /// 67 | /// The actual progress value, from 0 to 100. 68 | /// 69 | public UInt16 Progress { get; private set; } 70 | 71 | /// 72 | /// File time that has been processed, based on file total duration. 73 | /// 74 | public TimeSpan Processed { get; private set; } 75 | 76 | /// 77 | /// File time pending to be processed, based on file total duration. 78 | /// 79 | public TimeSpan Remaining { get; private set; } 80 | 81 | /// 82 | /// Actual size of the generated output file. 83 | /// 84 | public UInt64 OutputSize { get; private set; } 85 | 86 | /// 87 | /// Allows to cancel the current operation. 88 | /// 89 | /// true to cancel; otherwise, leave as false. 90 | public bool Abort { get; set; } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Exceptions/SoxEffectException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | namespace SoxSharp.Exceptions 5 | { 6 | /// 7 | /// Exception that is thrown when trying to apply an Effect which caontains invalid data. 8 | /// 9 | public class SoxEffectException : SoxException 10 | { 11 | /// 12 | /// Name of the effect that caused the exception. 13 | /// 14 | public string EffectName { get; private set; } 15 | 16 | /// 17 | /// Initializes a new instance of the class with its message string set 18 | /// to a default message. 19 | /// 20 | /// The effect name. 21 | public SoxEffectException(string effectName) 22 | { 23 | EffectName = effectName; 24 | } 25 | 26 | /// 27 | /// Initializes a new instance of the class with a specified message. 28 | /// 29 | /// The effect name. 30 | /// The exception's message. 31 | public SoxEffectException(string effectName, string message) 32 | : base(message) 33 | { 34 | EffectName = effectName; 35 | } 36 | 37 | /// 38 | /// Initializes a new instance of the class with a specified message and a 39 | /// reference to the inner exception that is the cause of this exception. 40 | /// 41 | /// The effect name. 42 | /// The exception's message. 43 | /// Exception that caused it. 44 | public SoxEffectException(string effectName, string message, Exception inner) 45 | : base(message, inner) 46 | { 47 | EffectName = effectName; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Exceptions/SoxException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | namespace SoxSharp.Exceptions 5 | { 6 | /// 7 | /// Generic SoX exception. 8 | /// 9 | public class SoxException : Exception 10 | { 11 | /// 12 | /// Initializes a new instance of the class with its message string set 13 | /// to a default message. 14 | /// 15 | public SoxException() 16 | { 17 | } 18 | 19 | /// 20 | /// Initializes a new instance of the class with a specified message. 21 | /// 22 | /// The exception's message. 23 | public SoxException(string message) 24 | : base(message) 25 | { 26 | } 27 | 28 | /// 29 | /// Initializes a new instance of the class with a specified message and a 30 | /// reference to the inner exception that is the cause of this exception. 31 | /// 32 | /// The exception's message. 33 | /// Exception that caused it. 34 | public SoxException(string message, Exception inner) 35 | : base(message, inner) 36 | { 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Exceptions/SoxUnexpectedOutputException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | 4 | namespace SoxSharp.Exceptions 5 | { 6 | /// 7 | /// Exception that is thrown when SoX output doesn't match the expected regular expression. 8 | /// 9 | public class SoxUnexpectedOutputException: SoxException 10 | { 11 | /// 12 | /// Initializes a new instance of the class with an output string. 13 | /// 14 | /// SoX unexpected output string. 15 | public SoxUnexpectedOutputException(string output) 16 | : base(output) 17 | { 18 | } 19 | 20 | 21 | /// 22 | /// Initializes a new instance of the class with an output string 23 | /// and a reference to the inner exception that is the cause of this exception. 24 | /// 25 | /// SoX unexpected output string. 26 | /// Exception that caused it. 27 | public SoxUnexpectedOutputException(string output, Exception inner) 28 | : base(output, inner) 29 | { 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/FormatOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | 5 | namespace SoxSharp 6 | { 7 | /// 8 | /// Format options that are applicable to both input and output files. 9 | /// 10 | public abstract class FormatOptions 11 | { 12 | /// 13 | /// Audio file type. 14 | /// 15 | public FileType? Type { get; set; } 16 | 17 | /// 18 | /// Audio encoding. 19 | /// 20 | public EncodingType? Encoding { get; set; } 21 | 22 | /// 23 | /// Sample size (bits). 24 | /// 25 | public ushort? SampleSize { get; set; } 26 | 27 | /// 28 | /// Encoded nibble order. 29 | /// 30 | public bool? ReverseNibbles { get; set; } 31 | 32 | /// 33 | /// Encoded bit order. 34 | /// 35 | public bool? ReverseBits { get; set; } 36 | 37 | /// 38 | /// Encoded byte order. 39 | /// 40 | public ByteOrderType? ByteOrder { get; set; } 41 | 42 | /// 43 | /// Number of audio channels. 44 | /// 45 | public ushort? Channels { get; set; } 46 | 47 | /// 48 | /// Audio sample rate. 49 | /// 50 | public uint? SampleRate { get; set; } 51 | 52 | /// 53 | /// Allow glob wildcard match filename. 54 | /// 55 | public bool? Glob { get; set; } 56 | 57 | /// 58 | /// Custom format arguments. 59 | /// 60 | public string CustomArgs { get; set; } 61 | 62 | /// 63 | /// Default constructor. 64 | /// 65 | protected FormatOptions() 66 | { 67 | } 68 | 69 | 70 | /// 71 | /// Translate a instance to a set of command arguments to be passed to SoX (invalidates ). 72 | /// 73 | /// String containing SoX command arguments. 74 | public override string ToString() 75 | { 76 | var formatOptions = new List(); 77 | 78 | if (Type.HasValue) 79 | formatOptions.Add("--type " + Type.Value.ToString().ToLower()); 80 | 81 | if (Encoding.HasValue) 82 | { 83 | switch (Encoding.Value) 84 | { 85 | case EncodingType.ALaw: formatOptions.Add("--encoding a-law"); break; 86 | case EncodingType.FloatingPoint: formatOptions.Add("--encoding floating-point"); break; 87 | case EncodingType.GsmFullRate: formatOptions.Add("--encoding gsm-full-rate"); break; 88 | case EncodingType.ImaAdpcm: formatOptions.Add("--encoding ima-adpcm"); break; 89 | case EncodingType.MsAdpcm: formatOptions.Add("--encoding ms-pcm"); break; 90 | case EncodingType.MuLaw: formatOptions.Add("--encoding mu-law"); break; 91 | case EncodingType.SignedInteger: formatOptions.Add("--encoding signed-integer"); break; 92 | case EncodingType.UnsignedInteger: formatOptions.Add("--encoding unsigned-integer"); break; 93 | default: break; // Do nothing. 94 | } 95 | } 96 | 97 | if (SampleSize.HasValue) 98 | formatOptions.Add("--bits " + SampleSize.Value); 99 | 100 | if (ReverseNibbles.HasValue && (ReverseNibbles.Value == true)) 101 | formatOptions.Add("--reverse-nibbles"); 102 | 103 | if (ReverseBits.HasValue && (ReverseBits.Value == true)) 104 | formatOptions.Add("--reverse-bits"); 105 | 106 | if (ByteOrder.HasValue) 107 | { 108 | switch (ByteOrder.Value) 109 | { 110 | case ByteOrderType.BigEndian: formatOptions.Add("--endian big"); break; 111 | case ByteOrderType.LittleEndian: formatOptions.Add("--endian little"); break; 112 | case ByteOrderType.Swap: formatOptions.Add("--endian swap"); break; 113 | default: break; // Do nothing. 114 | } 115 | } 116 | 117 | if (Channels.HasValue) 118 | formatOptions.Add("--channels " + Channels.Value); 119 | 120 | if (SampleRate.HasValue) 121 | formatOptions.Add("--rate " + SampleRate.Value); 122 | 123 | if (Glob.HasValue && (Glob.Value == false)) 124 | formatOptions.Add("--no-glob"); 125 | 126 | if (!string.IsNullOrEmpty(CustomArgs)) 127 | formatOptions.Add(CustomArgs); 128 | 129 | return string.Join(" ", formatOptions); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/FormattedSize.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | 5 | namespace SoxSharp 6 | { 7 | /// 8 | /// Utility class that converts a a SoX file size string (an integer or double value, optionally followed by a k, M or G character) to a numeric value. 9 | /// 10 | public static class FormattedSize 11 | { 12 | /// 13 | /// Converts a SoX file size string to a value. 14 | /// 15 | /// Size string. 16 | /// Numeric value. 17 | public static UInt64 ToUInt64(string formattedSize) 18 | { 19 | UInt64 multiplier = 1; 20 | 21 | if (formattedSize.EndsWith("k", StringComparison.InvariantCulture)) 22 | { 23 | multiplier = 1024; 24 | formattedSize = formattedSize.Substring(0, formattedSize.Length - 1); 25 | } 26 | 27 | else if (formattedSize.EndsWith("M", StringComparison.InvariantCulture)) 28 | { 29 | multiplier = 1024 * 1024; 30 | formattedSize = formattedSize.Substring(0, formattedSize.Length - 1); 31 | } 32 | 33 | else if (formattedSize.EndsWith("G", StringComparison.InvariantCulture)) 34 | { 35 | multiplier = 1024 * 1024 * 1024; 36 | formattedSize = formattedSize.Substring(0, formattedSize.Length - 1); 37 | } 38 | 39 | return Convert.ToUInt64(double.Parse(formattedSize, CultureInfo.InvariantCulture)) * multiplier; 40 | } 41 | 42 | 43 | /// 44 | /// Converts a SoX file size string to a value. 45 | /// 46 | /// Size string. 47 | /// Numeric value. 48 | public static UInt32 ToUInt32(string formattedSize) 49 | { 50 | UInt32 multiplier = 1; 51 | 52 | if (formattedSize.EndsWith("k", StringComparison.InvariantCulture)) 53 | { 54 | multiplier = 1024; 55 | formattedSize = formattedSize.Substring(0, formattedSize.Length - 1); 56 | } 57 | 58 | else if (formattedSize.EndsWith("M", StringComparison.InvariantCulture)) 59 | { 60 | multiplier = 1024 * 1024; 61 | formattedSize = formattedSize.Substring(0, formattedSize.Length - 1); 62 | } 63 | 64 | else if (formattedSize.EndsWith("G", StringComparison.InvariantCulture)) 65 | { 66 | multiplier = 1024 * 1024 * 1024; 67 | formattedSize = formattedSize.Substring(0, formattedSize.Length - 1); 68 | } 69 | 70 | return Convert.ToUInt32(double.Parse(formattedSize, CultureInfo.InvariantCulture)) * multiplier; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/InputFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | 5 | namespace SoxSharp 6 | { 7 | /// 8 | /// Input format options. 9 | /// 10 | public class InputFile : FormatOptions 11 | { 12 | /// 13 | /// Input file name. 14 | /// 15 | public string FileName { get; set; } 16 | 17 | /// 18 | /// Input file volume adjustment factor. 19 | /// 20 | public double? Volume { get; set; } 21 | 22 | /// 23 | /// Ignore input file length given in header and read to EOF. 24 | /// 25 | public bool? IgnoreLength { get; set; } 26 | 27 | 28 | /// 29 | /// Initializes a new instance of the class. 30 | /// 31 | public InputFile() 32 | : base() 33 | { 34 | } 35 | 36 | 37 | /// 38 | /// Initializes a new instance of the class. 39 | /// 40 | public InputFile(string fileName) 41 | : base() 42 | { 43 | FileName = fileName; 44 | } 45 | 46 | 47 | /// 48 | /// Translate a instance to a set of command arguments to be passed to SoX to be applied to the input file (invalidates ). 49 | /// 50 | /// String containing SoX command arguments. 51 | public override string ToString() 52 | { 53 | List inputOptions = new List(4); 54 | 55 | string baseStr = base.ToString(); 56 | 57 | if (!string.IsNullOrEmpty(baseStr)) 58 | inputOptions.Add(baseStr); 59 | 60 | if (Volume.HasValue) 61 | inputOptions.Add("--volume " + Volume.Value); 62 | 63 | if (IgnoreLength.HasValue && (IgnoreLength.Value == true)) 64 | inputOptions.Add("--ignore-length"); 65 | 66 | if (!string.IsNullOrEmpty(FileName)) 67 | { 68 | if (FileName.Contains(" ")) 69 | { 70 | if ((Environment.OSVersion.Platform == PlatformID.Win32NT) || 71 | (Environment.OSVersion.Platform == PlatformID.Win32Windows) || 72 | (Environment.OSVersion.Platform == PlatformID.Win32S) || 73 | (Environment.OSVersion.Platform == PlatformID.WinCE)) 74 | inputOptions.Add("\"" + FileName + "\""); 75 | else 76 | inputOptions.Add("'" + FileName + "'"); 77 | } 78 | else 79 | inputOptions.Add(FileName); 80 | } 81 | else 82 | inputOptions.Add("--null"); 83 | 84 | return string.Join(" ", inputOptions); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/OutputFormatOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | 4 | namespace SoxSharp 5 | { 6 | /// 7 | /// Format options to be applied to the output file. For any property not set here, SoX will infer the value from the input file. 8 | /// 9 | public class OutputFormatOptions : FormatOptions 10 | { 11 | /// 12 | /// Compression factor for output format. 13 | /// 14 | public double? Compression { get; set; } 15 | 16 | /// 17 | /// Set comment for output file. 18 | /// 19 | public string Comment { get; set; } 20 | 21 | /// 22 | /// Add comment to output file. 23 | /// 24 | public string AddComment { get; set; } 25 | 26 | 27 | /// 28 | /// Initializes a new instance of the class. 29 | /// 30 | public OutputFormatOptions() 31 | : base() 32 | { 33 | } 34 | 35 | 36 | /// 37 | /// Translate a instance to a set of command arguments to be passed to SoX (adds additional command arguments to ). 38 | /// 39 | /// String containing SoX command arguments. 40 | public override string ToString() 41 | { 42 | List outputOptions = new List(4); 43 | 44 | string baseStr = base.ToString(); 45 | 46 | if (!string.IsNullOrEmpty(baseStr)) 47 | outputOptions.Add(baseStr); 48 | 49 | if (Compression.HasValue) 50 | outputOptions.Add("--compression " + Compression.Value); 51 | 52 | if (AddComment != null) 53 | outputOptions.Add("--add-comment \"" + AddComment + "\""); 54 | 55 | if (Comment != null) 56 | outputOptions.Add("--comment \"" + Comment + "\""); 57 | 58 | return string.Join(" ", outputOptions); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Este código fue generado por una herramienta. 4 | // Versión de runtime:4.0.30319.42000 5 | // 6 | // Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si 7 | // se vuelve a generar el código. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SoxSharp { 12 | using System; 13 | 14 | 15 | /// 16 | /// Clase de recurso fuertemente tipado, para buscar cadenas traducidas, etc. 17 | /// 18 | // StronglyTypedResourceBuilder generó automáticamente esta clase 19 | // a través de una herramienta como ResGen o Visual Studio. 20 | // Para agregar o quitar un miembro, edite el archivo .ResX y, a continuación, vuelva a ejecutar ResGen 21 | // con la opción /str o recompile su proyecto de VS. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Devuelve la instancia de ResourceManager almacenada en caché utilizada por esta clase. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SoxSharp.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Reemplaza la propiedad CurrentUICulture del subproceso actual para todas las 51 | /// búsquedas de recursos mediante esta clase de recurso fuertemente tipado. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/Sox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Globalization; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text.RegularExpressions; 8 | using SoxSharp.Effects; 9 | using SoxSharp.Exceptions; 10 | 11 | 12 | namespace SoxSharp 13 | { 14 | /// 15 | /// Encapsulates all the information needed to launch SoX and handle its output. 16 | /// 17 | public class Sox : IDisposable 18 | { 19 | /// 20 | /// Provides updated progress status while SoX is being executed. 21 | /// 22 | public event EventHandler OnProgress = null; 23 | 24 | /// 25 | /// Occurs when SoX generates any non-FAIL log message. 26 | /// 27 | public event EventHandler OnLogMessage = null; 28 | 29 | /// 30 | /// Location of the SoX executable to be used by the library. 31 | /// 32 | /// The executable path. 33 | public string Path { get; set; } 34 | 35 | /// 36 | /// Size of all processing buffers (default is 8192). 37 | /// 38 | public uint? Buffer { get; set; } 39 | 40 | /// 41 | /// Enable parallel effects channels processing (where available). 42 | /// 43 | public bool? Multithreaded { get; set; } 44 | 45 | /// 46 | /// Output format options. 47 | /// 48 | public OutputFormatOptions Output { get; private set; } 49 | 50 | /// 51 | /// Effects to be applied. 52 | /// 53 | public List Effects { get; private set; } 54 | 55 | /// 56 | /// Custom global arguments. 57 | /// 58 | public string CustomArgs { get; set; } 59 | 60 | /// 61 | /// Custom effects. Add here the command line arguments for any effect not currently implemented in SoXSharp. 62 | /// 63 | public string CustomEffects { get; set; } 64 | 65 | /// 66 | /// Gets the full command line of the last call to SoX. 67 | /// 68 | public string LastCommand { get; private set; } 69 | 70 | 71 | private SoxProcess soxProcess_ = null; 72 | private string lastError_ = null; 73 | private string lastErrorSource_ = null; 74 | private bool disposed_ = false; 75 | 76 | 77 | /// 78 | /// Initializes a new instance of the class. 79 | /// 80 | /// Location of the SoX executable to be used by the library. 81 | public Sox(string path) 82 | { 83 | Output = new OutputFormatOptions(); 84 | Effects = new List(); 85 | Path = path; 86 | } 87 | 88 | 89 | ~Sox() 90 | { 91 | Dispose(false); 92 | } 93 | 94 | 95 | /// 96 | /// Releases all resource used by the object. 97 | /// 98 | /// Call when you are finished using the instance. This 99 | /// method leaves the instance in an unusable state. After calling 100 | /// it, you must release all references to the instance so the garbage 101 | /// collector can reclaim the memory that it was occupying. 102 | public void Dispose() 103 | { 104 | Dispose(true); 105 | GC.SuppressFinalize(this); 106 | } 107 | 108 | 109 | /// 110 | /// Gets information about the given file. 111 | /// 112 | /// File information as a instance. 113 | /// Input file. 114 | public AudioInfo GetInfo(string inputFile) 115 | { 116 | if (!File.Exists(inputFile)) 117 | throw new FileNotFoundException("File not found: " + inputFile); 118 | 119 | soxProcess_ = SoxProcess.Create(Path); 120 | 121 | lastError_ = null; 122 | lastErrorSource_ = null; 123 | 124 | try 125 | { 126 | soxProcess_.StartInfo.RedirectStandardOutput = true; 127 | 128 | if (inputFile.Contains(" ")) 129 | { 130 | if ((Environment.OSVersion.Platform == PlatformID.Win32NT) || 131 | (Environment.OSVersion.Platform == PlatformID.Win32Windows) || 132 | (Environment.OSVersion.Platform == PlatformID.Win32S) || 133 | (Environment.OSVersion.Platform == PlatformID.WinCE)) 134 | soxProcess_.StartInfo.Arguments = "--info \"" + inputFile + "\""; 135 | else 136 | soxProcess_.StartInfo.Arguments = "--info '" + inputFile + "'"; 137 | } 138 | else 139 | soxProcess_.StartInfo.Arguments = "--info " + inputFile; 140 | 141 | soxProcess_.Start(); 142 | 143 | LastCommand = Path + " " + soxProcess_.StartInfo.Arguments; 144 | 145 | string output = soxProcess_.StandardOutput.ReadToEnd(); 146 | 147 | if (String.IsNullOrEmpty(output)) 148 | output = soxProcess_.StandardError.ReadToEnd(); 149 | 150 | if (soxProcess_.WaitForExit(10000) == false) 151 | throw new TimeoutException("SoX response timeout"); 152 | 153 | CheckForLogMessage(output); 154 | 155 | if (output != null) 156 | { 157 | Match matchInfo = SoxProcess.InfoRegex.Match(output); 158 | 159 | if (matchInfo.Success) 160 | { 161 | try 162 | { 163 | UInt16 channels = Convert.ToUInt16(double.Parse(matchInfo.Groups[1].Value, CultureInfo.InvariantCulture)); 164 | UInt32 sampleRate = Convert.ToUInt32(double.Parse(matchInfo.Groups[2].Value, CultureInfo.InvariantCulture)); 165 | UInt16 sampleSize = Convert.ToUInt16(double.Parse(new string(matchInfo.Groups[3].Value.Where(Char.IsDigit).ToArray()), CultureInfo.InvariantCulture)); 166 | TimeSpan duration = Utils.TimeSpanFromString(matchInfo.Groups[4].Value); 167 | UInt64 size = FormattedSize.ToUInt64(matchInfo.Groups[5].Value); 168 | UInt32 bitRate = FormattedSize.ToUInt32(matchInfo.Groups[6].Value); 169 | string encoding = matchInfo.Groups[7].Value; 170 | 171 | return new AudioInfo(channels, sampleRate, sampleSize, duration, size, bitRate, encoding); 172 | } 173 | 174 | catch (Exception ex) 175 | { 176 | throw new SoxUnexpectedOutputException(output, ex); 177 | } 178 | } 179 | } 180 | 181 | throw new SoxUnexpectedOutputException(output != null ? output : "No output received"); 182 | } 183 | 184 | finally 185 | { 186 | if (soxProcess_ != null) 187 | { 188 | soxProcess_.Dispose(); 189 | soxProcess_ = null; 190 | } 191 | } 192 | } 193 | 194 | 195 | /// 196 | /// Spawns a new SoX process using the specified options in this instance and record audio the specified file. 197 | /// 198 | /// Audio file to be recorded. 199 | public void Record(string outputFile) 200 | { 201 | Process("--default-device", outputFile); 202 | } 203 | 204 | 205 | /// 206 | /// Spawns a new SoX process using the specified options in this instance and plays the specified file. 207 | /// 208 | /// Audio file to be played. 209 | public void Play(string inputFile) 210 | { 211 | Process(new InputFile[] { new InputFile(inputFile) }, "--default-device"); 212 | } 213 | 214 | 215 | /// 216 | /// Spawns a new SoX process using the specified options in this instance and plays the specified file. 217 | /// 218 | /// Audio file to be played. 219 | public void Play(InputFile inputFile) 220 | { 221 | Process(new InputFile[] { inputFile }, "--default-device"); 222 | } 223 | 224 | 225 | /// 226 | /// Spawns a new SoX process using the specified options in this instance and plays the specified files. 227 | /// 228 | /// Audio files to be played. 229 | public void Play(string[] inputFiles) 230 | { 231 | var inputs = new List(inputFiles.Length); 232 | 233 | foreach (var inputFile in inputFiles) 234 | inputs.Add(new InputFile(inputFile)); 235 | 236 | Process(inputs.ToArray(), "--default-device"); 237 | } 238 | 239 | 240 | /// 241 | /// Spawns a new SoX process using the specified options in this instance and plays the specified files. 242 | /// 243 | /// Audio files to be played. 244 | /// How to combine the input files. 245 | public void Play(string[] inputFiles, CombinationType combination) 246 | { 247 | var inputs = new List(inputFiles.Length); 248 | 249 | foreach (var inputFile in inputFiles) 250 | inputs.Add(new InputFile(inputFile)); 251 | 252 | Process(inputs.ToArray(), "--default-device", combination); 253 | } 254 | 255 | 256 | /// 257 | /// Spawns a new SoX process using the specified options in this instance and plays the specified files. 258 | /// 259 | /// Audio files to be played. 260 | public void Play(InputFile[] inputFiles) 261 | { 262 | Process(inputFiles, "--default-device"); 263 | } 264 | 265 | 266 | /// 267 | /// Spawns a new SoX process using the specified options in this instance and plays the specified files. 268 | /// 269 | /// Audio files to be played. 270 | /// How to combine the input files. 271 | public void Play(InputFile[] inputFiles, CombinationType combination) 272 | { 273 | Process(inputFiles, "--default-device", combination); 274 | } 275 | 276 | 277 | /// 278 | /// Spawns a new SoX process using the specified options in this instance. 279 | /// 280 | /// Audio file to be processed. 281 | public void Process(string inputFile) 282 | { 283 | Process(new InputFile[] { new InputFile(inputFile) }, null); 284 | } 285 | 286 | 287 | /// 288 | /// Spawns a new SoX process using the specified options in this instance. 289 | /// 290 | /// Audio file to be processed. 291 | public void Process(InputFile inputFile) 292 | { 293 | Process(new InputFile[] { inputFile }, null); 294 | } 295 | 296 | 297 | /// 298 | /// Spawns a new SoX process using the specified options in this instance. 299 | /// 300 | /// Audio file to be processed. 301 | /// Output file. 302 | public void Process(string inputFile, string outputFile) 303 | { 304 | Process(new InputFile[] { new InputFile(inputFile) }, outputFile); 305 | } 306 | 307 | 308 | /// 309 | /// Spawns a new SoX process using the specified options in this instance. 310 | /// 311 | /// Audio file to be processed. 312 | /// Output file. 313 | public void Process(InputFile inputFile, string outputFile) 314 | { 315 | Process(new InputFile[] { inputFile }, outputFile); 316 | } 317 | 318 | 319 | /// 320 | /// Spawns a new SoX process using the specified options in this instance. 321 | /// 322 | /// First audio file to be processed. 323 | /// Second audio file to be processed. 324 | /// Output file. 325 | public void Process(string inputFile1, string inputFile2, string outputFile) 326 | { 327 | Process(new InputFile[] { new InputFile(inputFile1), new InputFile(inputFile2) }, outputFile); 328 | } 329 | 330 | 331 | /// 332 | /// Spawns a new SoX process using the specified options in this instance. 333 | /// 334 | /// First audio file to be processed. 335 | /// Second audio file to be processed. 336 | /// Output file. 337 | /// How to combine the input files. 338 | public void Process(string inputFile1, string inputFile2, string outputFile, CombinationType combination) 339 | { 340 | Process(new InputFile[] { new InputFile(inputFile1), new InputFile(inputFile2) }, outputFile, combination); 341 | } 342 | 343 | 344 | /// 345 | /// Spawns a new SoX process using the specified options in this instance. 346 | /// 347 | /// First audio file to be processed. 348 | /// Second audio file to be processed. 349 | /// Output file. 350 | public void Process(InputFile inputFile1, InputFile inputFile2, string outputFile) 351 | { 352 | Process(new InputFile[] { inputFile1, inputFile2 }, outputFile); 353 | } 354 | 355 | 356 | /// 357 | /// Spawns a new SoX process using the specified options in this instance. 358 | /// 359 | /// First audio file to be processed. 360 | /// Second audio file to be processed. 361 | /// Output file. 362 | /// How to combine the input files. 363 | public void Process(InputFile inputFile1, InputFile inputFile2, string outputFile, CombinationType combination) 364 | { 365 | Process(new InputFile[] { inputFile1, inputFile2 }, outputFile, combination); 366 | } 367 | 368 | 369 | /// 370 | /// Spawns a new SoX process using the specified options in this instance. 371 | /// 372 | /// Audio files to be processed. 373 | /// Output file. 374 | public void Process(string[] inputFiles, string outputFile) 375 | { 376 | var inputs = new List(inputFiles.Length); 377 | 378 | foreach (var inputFile in inputFiles) 379 | inputs.Add(new InputFile(inputFile)); 380 | 381 | Process(inputs.ToArray(), outputFile); 382 | } 383 | 384 | 385 | /// 386 | /// Spawns a new SoX process using the specified options in this instance. 387 | /// 388 | /// Audio files to be processed. 389 | /// Output file. 390 | /// How to combine the input files. 391 | public void Process(string[] inputFiles, string outputFile, CombinationType combination) 392 | { 393 | var inputs = new List(inputFiles.Length); 394 | 395 | foreach (var inputFile in inputFiles) 396 | inputs.Add(new InputFile(inputFile)); 397 | 398 | Process(inputs.ToArray(), outputFile, combination); 399 | } 400 | 401 | 402 | /// 403 | /// Spawns a new SoX process using the specified options in this instance. 404 | /// 405 | /// Audio files to be processed. 406 | /// Output file. 407 | public void Process(InputFile[] inputFiles, string outputFile) 408 | { 409 | Process(inputFiles, outputFile, CombinationType.Default); 410 | } 411 | 412 | 413 | /// 414 | /// Spawns a new SoX process using the specified options in this instance. 415 | /// 416 | /// Audio files to be processed. 417 | /// Output file. 418 | /// How to combine the input files. 419 | public void Process(InputFile[] inputFiles, string outputFile, CombinationType combination) 420 | { 421 | soxProcess_ = SoxProcess.Create(Path); 422 | 423 | lastError_ = null; 424 | lastErrorSource_ = null; 425 | 426 | try 427 | { 428 | soxProcess_.ErrorDataReceived += OnSoxProcessOutputReceived; 429 | soxProcess_.OutputDataReceived += OnSoxProcessOutputReceived; 430 | 431 | List args = new List(); 432 | 433 | // Global options. 434 | 435 | if (Buffer.HasValue) 436 | args.Add("--buffer " + Buffer.Value); 437 | 438 | if (Multithreaded.HasValue) 439 | args.Add(Multithreaded.Value ? "--multi-threaded" : "--single-threaded"); 440 | 441 | if (!String.IsNullOrEmpty(CustomArgs)) 442 | args.Add(CustomArgs); 443 | 444 | switch (combination) 445 | { 446 | case CombinationType.Concatenate: 447 | args.Add("--combine concatenate"); 448 | break; 449 | 450 | case CombinationType.Merge: 451 | args.Add("--combine merge"); 452 | break; 453 | 454 | case CombinationType.Mix: 455 | args.Add("--combine mix"); 456 | break; 457 | 458 | case CombinationType.MixPower: 459 | args.Add("--combine mix-power"); 460 | break; 461 | 462 | case CombinationType.Multiply: 463 | args.Add("--combine multiply"); 464 | break; 465 | 466 | case CombinationType.Sequence: 467 | args.Add("--combine sequence"); 468 | break; 469 | 470 | default: 471 | // Do nothing. 472 | break; 473 | } 474 | 475 | args.Add("--show-progress"); 476 | 477 | // Input options and files. 478 | 479 | if ((inputFiles != null) && (inputFiles.Length > 0)) 480 | { 481 | foreach (InputFile inputFile in inputFiles) 482 | args.Add(inputFile.ToString()); 483 | } 484 | else 485 | args.Add("--null"); 486 | 487 | // Output options and file. 488 | 489 | args.Add(Output.ToString()); 490 | 491 | if (!string.IsNullOrEmpty(outputFile)) 492 | { 493 | if (outputFile.Contains(" ")) 494 | { 495 | if ((Environment.OSVersion.Platform == PlatformID.Win32NT) || 496 | (Environment.OSVersion.Platform == PlatformID.Win32Windows) || 497 | (Environment.OSVersion.Platform == PlatformID.Win32S) || 498 | (Environment.OSVersion.Platform == PlatformID.WinCE)) 499 | args.Add("\"" + outputFile + "\""); 500 | else 501 | args.Add("'" + outputFile + "'"); 502 | } 503 | else 504 | args.Add(outputFile); 505 | } 506 | else 507 | args.Add("--null"); 508 | 509 | // Effects. 510 | foreach (IBaseEffect effect in Effects) 511 | args.Add(effect.ToString()); 512 | 513 | // Custom effects. 514 | args.Add(CustomEffects); 515 | 516 | soxProcess_.StartInfo.Arguments = String.Join(" ", args); 517 | LastCommand = Path + " " + soxProcess_.StartInfo.Arguments; 518 | 519 | try 520 | { 521 | soxProcess_.Start(); 522 | soxProcess_.BeginOutputReadLine(); 523 | soxProcess_.BeginErrorReadLine(); 524 | soxProcess_.WaitForExit(); 525 | } 526 | 527 | catch (Exception ex) 528 | { 529 | throw new SoxException("Cannot spawn SoX process", ex); 530 | } 531 | 532 | if (!String.IsNullOrEmpty(lastError_)) 533 | { 534 | if (String.IsNullOrEmpty(lastErrorSource_)) 535 | throw new SoxException(lastError_); 536 | 537 | switch (lastErrorSource_) 538 | { 539 | case "getopt": 540 | throw new SoxException("Invalid parameter: " + lastError_); 541 | 542 | default: 543 | throw new SoxException("Processing error: " + lastError_); 544 | } 545 | } 546 | } 547 | 548 | finally 549 | { 550 | if (soxProcess_ != null) 551 | { 552 | soxProcess_.Dispose(); 553 | soxProcess_ = null; 554 | } 555 | } 556 | } 557 | 558 | 559 | private void OnSoxProcessOutputReceived(object sender, DataReceivedEventArgs received) 560 | { 561 | if (received.Data != null) 562 | { 563 | if (OnProgress != null) 564 | { 565 | Match matchProgress = SoxProcess.ProgressRegex.Match(received.Data); 566 | 567 | if (matchProgress.Success) 568 | { 569 | try 570 | { 571 | UInt16 progress = Convert.ToUInt16(double.Parse(matchProgress.Groups[1].Value, CultureInfo.InvariantCulture)); 572 | TimeSpan processed = Utils.TimeSpanFromString(matchProgress.Groups[2].Value); 573 | TimeSpan remaining = Utils.TimeSpanFromString(matchProgress.Groups[3].Value); 574 | UInt64 outputSize = FormattedSize.ToUInt64(matchProgress.Groups[4].Value); 575 | 576 | ProgressEventArgs eventArgs = new ProgressEventArgs(progress, processed, remaining, outputSize); 577 | OnProgress(sender, eventArgs); 578 | 579 | if (eventArgs.Abort) 580 | Abort(); 581 | 582 | return; 583 | } 584 | 585 | catch (Exception ex) 586 | { 587 | throw new SoxUnexpectedOutputException(received.Data, ex); 588 | } 589 | } 590 | } 591 | 592 | CheckForLogMessage(received.Data); 593 | } 594 | } 595 | 596 | 597 | /// 598 | /// Kills the SoX process. 599 | /// 600 | public void Abort() 601 | { 602 | if ((soxProcess_ != null) && !soxProcess_.HasExited) 603 | { 604 | try 605 | { 606 | soxProcess_.Kill(); 607 | } 608 | 609 | finally 610 | { 611 | soxProcess_.Dispose(); 612 | soxProcess_ = null; 613 | } 614 | } 615 | } 616 | 617 | 618 | private void CheckForLogMessage(string data) 619 | { 620 | if (string.IsNullOrEmpty(data)) 621 | return; 622 | 623 | Match logMatch = SoxProcess.LogRegex.Match(data); 624 | 625 | if (logMatch.Success) 626 | { 627 | string logLevel = logMatch.Groups[1].Value; 628 | string source = logMatch.Groups[2].Value; 629 | string message = logMatch.Groups[3].Value; 630 | 631 | if ("DBUG".Equals(logLevel) && (OnLogMessage != null)) 632 | OnLogMessage(this, new LogMessageEventArgs(LogLevelType.Debug, source, message)); 633 | 634 | if ("INFO".Equals(logLevel) && (OnLogMessage != null)) 635 | OnLogMessage(this, new LogMessageEventArgs(LogLevelType.Info, source, message)); 636 | 637 | if ("WARN".Equals(logLevel) && (OnLogMessage != null)) 638 | OnLogMessage(this, new LogMessageEventArgs(LogLevelType.Warning, source, message)); 639 | 640 | else if ("FAIL".Equals(logLevel)) 641 | { 642 | if (String.IsNullOrEmpty(lastError_)) 643 | lastError_ = message; 644 | 645 | if (String.IsNullOrEmpty(lastErrorSource_)) 646 | lastErrorSource_ = source; 647 | } 648 | } 649 | } 650 | 651 | 652 | protected virtual void Dispose(bool disposing) 653 | { 654 | if (disposed_) 655 | return; 656 | 657 | if (disposing) 658 | Abort(); 659 | 660 | disposed_ = true; 661 | } 662 | } 663 | } 664 | -------------------------------------------------------------------------------- /src/SoxProcess.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Text.RegularExpressions; 5 | using SoxSharp.Exceptions; 6 | 7 | 8 | namespace SoxSharp 9 | { 10 | internal sealed class SoxProcess : Process 11 | { 12 | public static readonly Regex InfoRegex = new Regex(@"Input File\s*: .+\r?\nChannels\s*: (\d+)\r?\nSample Rate\s*: (\d+)\r?\nPrecision\s*: ([\s\S]+?)\r?\nDuration\s*: (\d{2}:\d{2}:\d{2}\.?\d{2}?)[\s\S]+?\r?\nFile Size\s*: (\d+\.?\d{0,2}?[k|M|G]?)\r?\nBit Rate\s*: (\d+\.?\d{0,2}?[k|M|G]?)\r?\nSample Encoding\s*: (.+)"); 13 | public static readonly Regex ProgressRegex = new Regex(@"In:(\d{1,3}\.?\d{0,2})%\s+(\d{2}:\d{2}:\d{2}\.?\d{0,2})\s+\[(\d{2}:\d{2}:\d{2}\.?\d{0,2})\]\s+Out:(\d+\.?\d{0,2}[k|M|G]?)"); 14 | public static readonly Regex LogRegex = new Regex(@"(FAIL|WARN|DBUG|INFO)\s(\w+):\s(.+)"); 15 | 16 | 17 | private SoxProcess() 18 | : base() 19 | { 20 | StartInfo.ErrorDialog = false; 21 | StartInfo.CreateNoWindow = true; 22 | StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 23 | StartInfo.UseShellExecute = false; 24 | StartInfo.RedirectStandardOutput = true; 25 | StartInfo.RedirectStandardError = true; 26 | EnableRaisingEvents = true; 27 | } 28 | 29 | 30 | /// 31 | /// Create a new instance prepared to run SoX. 32 | /// 33 | /// The SoX process instance. 34 | public static SoxProcess Create(string path) 35 | { 36 | string soxExecutable; 37 | 38 | if (String.IsNullOrEmpty(path)) 39 | throw new SoxException("SoX path not specified"); 40 | 41 | if (File.Exists(path)) 42 | soxExecutable = path; 43 | else 44 | throw new FileNotFoundException("SoX executable not found"); 45 | 46 | using (SoxProcess versionCheck = new SoxProcess()) 47 | { 48 | versionCheck.StartInfo.RedirectStandardOutput = true; 49 | versionCheck.StartInfo.FileName = soxExecutable; 50 | versionCheck.StartInfo.Arguments = "--version"; 51 | versionCheck.Start(); 52 | 53 | string output = versionCheck.StandardOutput.ReadLine(); 54 | 55 | if (versionCheck.WaitForExit(1000) == false) 56 | throw new TimeoutException("Cannot obtain SoX version: response timeout"); 57 | 58 | Match versionMatch = new Regex(@"\sSoX v(\d{1,2})\.(\d{1,2})\.(\d{1,2})").Match(output); 59 | 60 | if (!versionMatch.Success) 61 | throw new SoxException("Cannot obtain SoX version: unable to fetch info from Sox"); 62 | 63 | try 64 | { 65 | int majorVersion = Int32.Parse(versionMatch.Groups[1].Value); 66 | int minorVersion = Int32.Parse(versionMatch.Groups[2].Value); 67 | int fixVersion = Int32.Parse(versionMatch.Groups[3].Value); 68 | 69 | if ((majorVersion < 14) || 70 | ((majorVersion == 14) && (minorVersion < 3)) || 71 | ((majorVersion == 14) && (minorVersion == 3) && (fixVersion < 1))) 72 | throw new SoxException(versionMatch.Groups[0] + " not currently supported"); 73 | } 74 | 75 | catch (Exception ex) 76 | { 77 | throw new SoxException("Cannot obtain SoX version", ex); 78 | } 79 | } 80 | 81 | SoxProcess soxProc = new SoxProcess(); 82 | soxProc.StartInfo.FileName = soxExecutable; 83 | soxProc.StartInfo.WorkingDirectory = Environment.CurrentDirectory; 84 | 85 | string soxPath = Path.GetDirectoryName(soxExecutable); 86 | 87 | if (!String.IsNullOrEmpty(soxPath)) 88 | { 89 | string pathEnv = Environment.GetEnvironmentVariable("PATH"); 90 | pathEnv += Path.PathSeparator + soxPath; 91 | soxProc.StartInfo.EnvironmentVariables["PATH"] = pathEnv; 92 | } 93 | 94 | return soxProc; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/SoxSharp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net45 5 | latest 6 | true 7 | 1.3.5 8 | igece.labs 9 | 10 | https://github.com/igece/SoxSharp 11 | https://github.com/igece/SoxSharp 12 | Library that serves as a wrapper to SoX - the Sound eXchange tool. 13 | sox wrapper audio conversion filters 14 | Added compatibility with .NET Standard 15 | LICENSE 16 | 17 | 18 | 19 | true 20 | ..\bin\$(Configuration) 21 | 22 | 23 | 24 | ..\bin\$(Configuration) 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | True 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/SoxSharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2015 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoxSharp", "SoxSharp.csproj", "{900A3274-0A8A-4AF3-A34F-CCB27114E58F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {900A3274-0A8A-4AF3-A34F-CCB27114E58F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {900A3274-0A8A-4AF3-A34F-CCB27114E58F}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {900A3274-0A8A-4AF3-A34F-CCB27114E58F}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {900A3274-0A8A-4AF3-A34F-CCB27114E58F}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {6B877572-083E-4CE5-9EAE-69D95ACCFDEB} 24 | EndGlobalSection 25 | GlobalSection(MonoDevelopProperties) = preSolution 26 | Policies = $0 27 | $0.DotNetNamingPolicy = $1 28 | $1.DirectoryNamespaceAssociation = PrefixedFlat 29 | $0.StandardHeader = $2 30 | version = 1.2.* 31 | EndGlobalSection 32 | EndGlobal 33 | -------------------------------------------------------------------------------- /src/Types.cs: -------------------------------------------------------------------------------- 1 | namespace SoxSharp 2 | { 3 | /// 4 | /// Audio file formats. 5 | /// 6 | public enum FileType 7 | { 8 | /// 9 | /// Amiga 8SVX musical instrument description format. 10 | /// 11 | SVX, 12 | 13 | /// 14 | /// Audio Interchage File Format. Used on old Apple Macs, Apple IIc/IIgs and SGI. SoX’s AIFF support does not 15 | /// include multiple audio chunks, or the 8SVX musical instrument description format. AIFF files are multimedia 16 | /// archives and can have multiple audio and picture chunks — you may need a separate archiver to work with them. 17 | /// With Mac OS X, AIFF has been superseded by CAF. 18 | /// 19 | AIF, 20 | 21 | /// 22 | /// AIFF-C (not compressed), defined in DAVIC 1.4 Part 9 Annex B. Format based on AIFF that was created to allow 23 | /// handling compressed audio. It can also handle little endian uncompressed linear data that is often referred to 24 | /// as sowt encoding. This encoding has also become the defacto format produced by modern Macs as well as iTunes on 25 | /// any platform. AIFF-C files produced by other applications typically have the file extension .aif and require 26 | /// looking at its header to detect the true format. The sowt encoding is the only encoding that SoX can handle 27 | /// with this format. Any private chunks are not supported. 28 | /// 29 | AIFC, 30 | 31 | /// 32 | /// Audio Interchage File Format. Used on old Apple Macs, Apple IIc/IIgs and SGI. SoX’s AIFF support does not 33 | /// include multiple audio chunks, or the 8SVX musical instrument description format. AIFF files are multimedia 34 | /// archives and can have multiple audio and picture chunks — you may need a separate archiver to work with them. 35 | /// With Mac OS X, AIFF has been superseded by CAF. 36 | /// 37 | AIFF, 38 | 39 | /// 40 | /// AIFF-C (not compressed), defined in DAVIC 1.4 Part 9 Annex B. Format based on AIFF that was created to allow 41 | /// handling compressed audio. It can also handle little endian uncompressed linear data that is often referred to 42 | /// as sowt encoding. This encoding has also become the defacto format produced by modern Macs as well as iTunes on 43 | /// any platform. AIFF-C files produced by other applications typically have the file extension .aif and require 44 | /// looking at its header to detect the true format. The sowt encoding is the only encoding that SoX can handle 45 | /// with this format. Any private chunks are not supported. 46 | /// 47 | AIFFC, 48 | 49 | /// 50 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 8-bit A-law. 51 | /// 52 | AL, 53 | 54 | /// 55 | /// Ambisonic B‐Format. A specialisation of .wav with between 3 and 16 channels of audio for use with an Ambisonic 56 | /// decoder. It is up to the user to get the channels together in the right order and at the correct amplitude. 57 | /// 58 | AMB, 59 | 60 | /// 61 | /// Sun Microsystems AU file. 62 | /// 63 | AU, 64 | 65 | /// 66 | /// Audio Visual Research format; used by a number of commercial packages on the Mac. 67 | /// 68 | AVR, 69 | 70 | /// 71 | /// Apple’s Core Audio File format. 72 | /// 73 | CAF, 74 | 75 | /// 76 | /// ‘Red Book’ Compact Disc Digital Audio (raw audio). CDDA has two audio channels formatted as 16-bit signed 77 | /// integers (big endian)at a sample rate of 44.1 kHz. The number of (stereo) samples in each CDDA track is always 78 | /// a multiple of 588. 79 | /// 80 | CDDA, 81 | 82 | /// 83 | /// ‘Red Book’ Compact Disc Digital Audio (raw audio). CDDA has two audio channels formatted as 16-bit signed 84 | /// integers (big endian)at a sample rate of 44.1 kHz. The number of (stereo) samples in each CDDA track is always 85 | /// a multiple of 588. 86 | /// 87 | CDR, 88 | 89 | /// 90 | /// Headerless MIL Std 188 113 Continuously Variable Slope Delta modulation. A headerless format used to compress 91 | /// speech audio for applications such as voice mail. This format is sometimes used with bit-reversed samples. 92 | /// 93 | CVS, 94 | 95 | /// 96 | /// Headerless MIL Std 188 113 Continuously Variable Slope Delta modulation. A headerless format used to compress 97 | /// speech audio for applications such as voice mail. This format is sometimes used with bit-reversed samples. 98 | /// 99 | CVSD, 100 | 101 | /// 102 | /// Headerless Continuously Variable Slope Delta modulation (unfiltered). This is an alternative handler for CVSD 103 | /// that is unfiltered but can be used with any bit-rate. 104 | /// 105 | CVU, 106 | 107 | /// 108 | /// Text Data files. These files contain a textual representation of the sample data. There is one line at the 109 | /// beginning that contains the sample rate, and one line that contains the number of channels. Subsequent lines 110 | /// contain two or more numeric data intems: the time since the beginning of the first sample and the sample value 111 | /// for each channel. 112 | /// 113 | DAT, 114 | 115 | /// 116 | /// Self-describing variant of CVSD. 117 | /// 118 | DVMS, 119 | 120 | /// 121 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 32-bit floating point PCM. 122 | /// 123 | F32, 124 | 125 | /// 126 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 4 bytes floating point PCM. 127 | /// Superseded by F32 type. 128 | /// 129 | F4, 130 | 131 | /// 132 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 64-bit floating point PCM. 133 | /// 134 | F64, 135 | 136 | /// 137 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 8 bytes floating point PCM. 138 | /// Superseded by F64 type. 139 | /// 140 | F8, 141 | 142 | /// 143 | /// Ensoniq PARIS file format (little-endian). 144 | /// 145 | FAP, 146 | 147 | /// 148 | /// Xiph.org’s Free Lossless Audio Codec Compressed Audio. Open, patent-free codec designed for compressing music. 149 | /// SoX can read native FLAC files (.flac) but not Ogg FLAC files (.ogg). 150 | /// SoX can write native FLAC files according to a given or default compression level 151 | /// (see ). 8 is the default compression level and gives the best 152 | /// (but slowest) compression; 0 gives the least (but fastest) compression. 153 | /// 154 | FLAC, 155 | 156 | /// 157 | /// An alias for the .u8 format. 158 | /// 159 | FSSD, 160 | 161 | /// 162 | /// GSM 06.10 Lossy Speech Compression. A lossy format for compressing speech which is used in the Global Standard 163 | /// for Mobile telecommunications (GSM). It’s good for its purpose, shrinking audio data size, but it will introduce 164 | /// lots of noise when a given audio signal is encoded and decoded multiple times. This format is used by some voice 165 | /// mail applications. It is rather CPU intensive. 166 | /// 167 | GSM, 168 | 169 | /// 170 | /// Grandstream ringtone files. Whilst this file format can contain A-Law, μ-law, GSM, G.722, G.723, G.726, G.728, or iLBC 171 | /// encoded audio, SoX supports reading and writing only A-Law and μ-law. 172 | /// 173 | GSRT, 174 | 175 | /// 176 | /// Macintosh HCOM files. These are Mac FSSD files with Huffman compression. 177 | /// 178 | HCOM, 179 | 180 | /// 181 | /// Single channel 16-bit PCM format used by HTK, a toolkit for building Hidden Markov Model speech processing tools. 182 | /// 183 | HTK, 184 | 185 | /// 186 | /// A headerless file of IMA ADPCM audio data. IMA ADPCM claims 16-bit precision packed into only 4 bits, but in fact 187 | /// sounds no better than .vox. 188 | /// 189 | IMA, 190 | 191 | /// 192 | /// Another name for SF. 193 | /// 194 | IRCAM, 195 | 196 | /// 197 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as inverse bit order 8-bit A-law. 198 | /// 199 | LA, 200 | 201 | /// 202 | /// LPC-10 is a compression scheme for speech developed in the United States. See http://www.arl.wustl.edu/˜jaf/lpc/ 203 | /// for details. There is no associated file format, so SoX’s implementation is headerless. 204 | /// 205 | LPC, 206 | 207 | /// 208 | /// LPC-10 is a compression scheme for speech developed in the United States. See http://www.arl.wustl.edu/˜jaf/lpc/ 209 | /// for details. There is no associated file format, so SoX’s implementation is headerless. 210 | /// 211 | LPC10, 212 | 213 | /// 214 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as inverse bit order 8-bit μ-law. 215 | /// 216 | LU, 217 | 218 | /// 219 | /// An IFF-conforming audio file type, registered by MS MacroSystem Computer GmbH, published along with the ‘Toccata’ 220 | /// soundcard on the Amiga. Allows 8-bit linear, 16-bit linear, A-Law, μ-law in mono and stereo. 221 | /// 222 | MAUD, 223 | 224 | MP2, 225 | 226 | /// 227 | /// MP3 compressed audio; MP3 (MPEG Layer 3) is a part of the MPEG standards for audio and video compression. It is a 228 | /// lossy compression format that achieves good compression rates with little quality loss. 229 | /// 230 | MP3, 231 | 232 | /// 233 | /// SPHERE (SPeech HEader Resources). Format defined by NIST (National Institute of Standards and Technology) and 234 | /// used with speech audio. SoX can read these files when they contain μ-law and PCM data. It will ignore any header 235 | /// information that says the data is compressed using shorten compression and will treat the data as either μ-law 236 | /// or PCM. 237 | /// 238 | NIST, 239 | 240 | PRC, 241 | 242 | /// 243 | /// Raw (headerless) audio file. 244 | /// 245 | RAW, 246 | 247 | /// 248 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 1 byte signed integer PCM. 249 | /// Superseded by S8 type. 250 | /// /// 251 | S1, 252 | 253 | /// 254 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 16-bit signed integer PCM. 255 | /// 256 | S16, 257 | 258 | /// 259 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 2 bytes signed integer PCM. 260 | /// Superseded by S16 type. 261 | /// 262 | S2, 263 | 264 | /// 265 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 24-bit signed integer PCM. 266 | /// 267 | S24, 268 | 269 | /// 270 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 3 bytes signed integer PCM. 271 | /// Superseded by S24 type. 272 | /// 273 | S3, 274 | 275 | /// 276 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 32-bit signed integer PCM. 277 | /// 278 | S32, 279 | 280 | /// 281 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 4 bytes signed integer PCM. 282 | /// Superseded by S32 type. 283 | /// 284 | S4, 285 | 286 | /// 287 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 8-bit signed integer PCM. 288 | /// 289 | S8, 290 | 291 | /// 292 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 1 byte signed integer PCM. 293 | /// Superseded by S8 type. 294 | /// 295 | SB, 296 | 297 | SF, 298 | 299 | /// 300 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 4 bytes signed integer PCM. 301 | /// Superseded by S32 type. 302 | /// 303 | SL, 304 | 305 | SMP, 306 | SND, 307 | SNDR, 308 | SNDT, 309 | SOU, 310 | SOX, 311 | 312 | /// 313 | /// SPHERE (SPeech HEader Resources). Format defined by NIST (National Institute of Standards and Technology) and 314 | /// used with speech audio. SoX can read these files when they contain μ-law and PCM data. It will ignore any header 315 | /// information that says the data is compressed using shorten compression and will treat the data as either μ-law 316 | /// or PCM. 317 | /// 318 | SPH, 319 | 320 | /// 321 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 2 bytes signed integer PCM. 322 | /// Superseded by S16 type. 323 | /// 324 | SW, 325 | 326 | TXW, 327 | 328 | /// 329 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 1 byte unsigned integer PCM. 330 | /// Superseded by U8 type. 331 | /// 332 | U1, 333 | 334 | /// 335 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 16-bit unsigned integer PCM. 336 | /// 337 | U16, 338 | 339 | /// 340 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 2 bytes unsigned integer PCM. 341 | /// Superseded by U16 type. 342 | /// 343 | U2, 344 | 345 | /// 346 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 24-bit unsigned integer PCM. 347 | /// 348 | U24, 349 | 350 | /// 351 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 3 bytes unsigned integer PCM. 352 | /// Superseded by U24 type. 353 | /// 354 | U3, 355 | 356 | /// 357 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 32-bit unsigned integer PCM. 358 | /// 359 | U32, 360 | 361 | /// 362 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 4 bytes unsigned integer PCM. 363 | /// Superseded by U32 type. 364 | /// 365 | U4, 366 | 367 | /// 368 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 8-bit unsigned integer PCM. 369 | /// 370 | U8, 371 | 372 | /// 373 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 1 byte unsigned integer PCM. 374 | /// Superseded by U8 type. 375 | /// 376 | UB, 377 | 378 | /// 379 | /// Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 8-bit μ-law. 380 | /// 381 | UL, 382 | 383 | /// 384 | /// [DEPRECATED] Raw (headerless) audio file with a default sample rate of 8kHz and encoded as 2 bytes unsigned integer PCM. 385 | /// Superseded by S16 type. 386 | /// 387 | UW, 388 | 389 | /// 390 | /// Self-describing variant of CVSD. 391 | /// 392 | VMS, 393 | 394 | /// 395 | /// Sound Blaster VOC files. VOC files are multi-part and contain silence parts, looping, and different sample rates 396 | /// for different chunks. On input, the silence parts are filled out, loops are rejected, and sample data with a new 397 | /// sample rate is rejected. Silence with a different sample rate is generated appropriately. On output, silence is 398 | /// not detected, nor are impossible sample rates. SoX supports reading (but not writing) VOC files with multiple 399 | /// blocks, and files containing μ-law, A-law, and 2/3/4-bit ADPCM samples. 400 | /// 401 | VOC, 402 | 403 | /// 404 | /// A headerless file of Dialogic/OKI ADPCM audio data. This ADPCM data has 12-bit precision packed into only 4-bits. 405 | /// Some early Dialogic hardware does not always reset the ADPCM encoder at the start of each vox file. This can 406 | /// result in clipping and/or DC offset problems when it comes to decoding the audio. Whilst little can be done 407 | /// about the clipping, a DC offset can be removed by passing the decoded audio through a high-pass filter. 408 | /// 409 | VOX, 410 | 411 | /// 412 | /// Microsoft .WAV RIFF files. This is the native audio file format of Windows, and widely used for uncompressed 413 | /// audio. Normally .wav files have all formatting information in their headers, and so do not need any format 414 | /// options specified for an input file. If any are, they will override the file header, and you will be warned to 415 | /// this effect. Output format options will cause a format conversion, and the .wav will written appropriately. 416 | /// SoX can read and write linear PCM, floating point, μ-law, A-law, MS ADPCM, and IMA (or DVI) ADPCM encoded 417 | /// samples. WAV files can also contain audio encoded in many other ways (not currently supported with SoX) 418 | /// e.g. MP3; in some cases such a file can still be read by SoX by overriding the file type. Big endian versions 419 | /// of RIFF files, called RIFX, are also supported. 420 | /// 421 | WAV, 422 | 423 | /// 424 | /// A non-standard, but widely used, variant of .wav. Some applications cannot read a standard WAV file header for 425 | /// PCM-encoded data with sample-size greater than 16-bits or with more than two channels, but can read a non- 426 | /// standard WAV header. It is likely that such applications will eventually be updated to support the standard 427 | /// header, but in the mean time, this SoX format can be used to create files with the non-standard header that 428 | /// should work with these applications. Note that SoX will automatically detect and read WAV files with the 429 | /// non-standard header. 430 | /// 431 | WAVPCM, 432 | 433 | /// 434 | /// Psion 8-bit A-law. Used on Psion SIBO PDAs (Series 3 and similar). This format is deprecated in SoX, but will 435 | /// continue to be used in libsndfile. 436 | /// 437 | WVE, 438 | 439 | /// 440 | /// Maxis XA files. These are 16-bit ADPCM audio files used by Maxis games. Writing .xa files is currently not 441 | /// supported. 442 | /// 443 | XA 444 | } 445 | 446 | /// 447 | /// Encoding type. 448 | /// 449 | public enum EncodingType 450 | { 451 | SignedInteger, 452 | UnsignedInteger, 453 | FloatingPoint, 454 | MuLaw, 455 | ALaw, 456 | ImaAdpcm, 457 | MsAdpcm, 458 | GsmFullRate 459 | } 460 | 461 | 462 | /// 463 | /// Byte order type. 464 | /// 465 | public enum ByteOrderType 466 | { 467 | /// 468 | /// Big endian byte order. 469 | /// 470 | BigEndian, 471 | 472 | /// 473 | /// Little endian byte order. 474 | /// 475 | LittleEndian, 476 | 477 | /// 478 | /// Swap the current byte order. 479 | /// 480 | Swap 481 | } 482 | 483 | 484 | /// 485 | /// SoX log message level. 486 | /// 487 | public enum LogLevelType 488 | { 489 | /// 490 | /// Warning message. 491 | /// 492 | Warning, 493 | 494 | /// 495 | /// Information message. 496 | /// 497 | Info, 498 | 499 | /// 500 | /// Debug message. 501 | /// 502 | Debug 503 | } 504 | 505 | 506 | /// 507 | /// How SoX’s input combiner shall combine multiple input files. 508 | /// 509 | public enum CombinationType 510 | { 511 | /// 512 | /// Use the default combination type ( for Process and Record 513 | /// and for Play). 514 | /// 515 | Default, 516 | 517 | /// 518 | /// Concatenate all input files (the default for process and record). 519 | /// Input files must have the same number of channels. The audio from each input will be concatenated 520 | /// in the order given to form the output file. 521 | /// 522 | Concatenate, 523 | 524 | /// 525 | /// Merge multiple input files. 526 | /// The number of channels in each input file need not be the same. A merged audio file comprises all 527 | /// of the channels from all of the input files. For example, two mono files could be merged to form 528 | /// one stereo file. The first and second mono files would become the left and right channels of the 529 | /// stereo file. Un-merging is possible using multiple invocations of SoX with the remix effect. 530 | /// 531 | Merge, 532 | 533 | /// 534 | /// Mix multiple input files. 535 | /// The number of channels in each input file need not be the same, but SoX will issue a warning if they 536 | /// are not and some channels in the output file will not contain audio from every input file. A mixed 537 | /// audio file cannot be un-mixed without reference to the original input files. 538 | /// 539 | Mix, 540 | 541 | /// 542 | /// Mix to equal power multiple input files. 543 | /// 544 | MixPower, 545 | 546 | /// 547 | /// Multiply samples of corresponding channels from all input files. 548 | /// Sample values of corresponding channels are treated as numbers in the interval −1 to +1. If the 549 | /// number of channels in the input files is not the same, the missing channels are considered 550 | /// to contain all zero. 551 | /// 552 | Multiply, 553 | 554 | /// 555 | /// Sequence all input files (the default for play). 556 | /// It is similar to in that the audio from each input file 557 | /// is sent serially to the output file. However, here the output file may be closed and reopened at 558 | /// the corresponding transition between input files. 559 | /// 560 | Sequence 561 | } 562 | } 563 | -------------------------------------------------------------------------------- /src/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Linq; 4 | 5 | 6 | namespace SoxSharp 7 | { 8 | static class Utils 9 | { 10 | private static int[] weights2 = { 60, 1 }; 11 | private static int[] weights3 = { 60 * 60, 60, 1 }; 12 | 13 | 14 | public static TimeSpan TimeSpanFromString(string time) 15 | { 16 | string[] parts = time.Split(':'); 17 | 18 | switch (parts.Length) 19 | { 20 | case 1: 21 | return TimeSpan.FromSeconds(Convert.ToDouble(parts[0], CultureInfo.InvariantCulture)); 22 | 23 | case 2: 24 | return TimeSpan.FromSeconds(parts.Zip(weights2, (d, w) => String.IsNullOrEmpty(d) ? 0 : Convert.ToDouble(d, CultureInfo.InvariantCulture) * w).Sum()); 25 | 26 | case 3: 27 | return TimeSpan.FromSeconds(parts.Zip(weights3, (d, w) => String.IsNullOrEmpty(d) ? 0 : Convert.ToDouble(d, CultureInfo.InvariantCulture) * w).Sum()); 28 | 29 | default: 30 | 31 | throw new ArgumentException("Invalid time string"); 32 | } 33 | } 34 | } 35 | } 36 | --------------------------------------------------------------------------------