├── Example.bmp
├── NAudio Pitchshifter Test.csproj.user
├── bin
├── Debug
│ ├── NAudio.dll
│ ├── NAudio Pitchshifter Test.exe
│ ├── NAudio Pitchshifter Test.pdb
│ └── NAudio Pitchshifter Test.vshost.exe
└── Release
│ ├── NAudio.dll
│ ├── NAudio Pitchshifter Test.exe
│ └── NAudio Pitchshifter Test.pdb
├── NAudio Pitchshifter Test.suo
├── Properties
├── Settings.settings
├── Settings.Designer.cs
├── AssemblyInfo.cs
├── Resources.Designer.cs
└── Resources.resx
├── Program.cs
├── NAudio Pitchshifter Test.sln
├── README.md
├── FormMain.cs
├── NAudio Pitchshifter Test.csproj
├── FormMain.Designer.cs
├── SMBPitchShiftingSampleProvider.cs
├── FormMain.resx
└── SMBPitchShifter.cs
/Example.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Freefall63/NAudio-Pitchshifter/HEAD/Example.bmp
--------------------------------------------------------------------------------
/NAudio Pitchshifter Test.csproj.user:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/bin/Debug/NAudio.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Freefall63/NAudio-Pitchshifter/HEAD/bin/Debug/NAudio.dll
--------------------------------------------------------------------------------
/bin/Release/NAudio.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Freefall63/NAudio-Pitchshifter/HEAD/bin/Release/NAudio.dll
--------------------------------------------------------------------------------
/NAudio Pitchshifter Test.suo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Freefall63/NAudio-Pitchshifter/HEAD/NAudio Pitchshifter Test.suo
--------------------------------------------------------------------------------
/bin/Debug/NAudio Pitchshifter Test.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Freefall63/NAudio-Pitchshifter/HEAD/bin/Debug/NAudio Pitchshifter Test.exe
--------------------------------------------------------------------------------
/bin/Debug/NAudio Pitchshifter Test.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Freefall63/NAudio-Pitchshifter/HEAD/bin/Debug/NAudio Pitchshifter Test.pdb
--------------------------------------------------------------------------------
/bin/Release/NAudio Pitchshifter Test.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Freefall63/NAudio-Pitchshifter/HEAD/bin/Release/NAudio Pitchshifter Test.exe
--------------------------------------------------------------------------------
/bin/Release/NAudio Pitchshifter Test.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Freefall63/NAudio-Pitchshifter/HEAD/bin/Release/NAudio Pitchshifter Test.pdb
--------------------------------------------------------------------------------
/bin/Debug/NAudio Pitchshifter Test.vshost.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Freefall63/NAudio-Pitchshifter/HEAD/bin/Debug/NAudio Pitchshifter Test.vshost.exe
--------------------------------------------------------------------------------
/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Windows.Forms;
5 |
6 | namespace NAudio_Pitchshifter_Test
7 | {
8 | static class Program
9 | {
10 | ///
11 | /// Der Haupteinstiegspunkt für die Anwendung.
12 | ///
13 | [STAThread]
14 | static void Main()
15 | {
16 | Application.EnableVisualStyles();
17 | Application.SetCompatibleTextRenderingDefault(false);
18 | Application.Run(new FormMain());
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/NAudio Pitchshifter Test.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 10.00
3 | # Visual Studio 2008
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NAudio Pitchshifter Test", "NAudio Pitchshifter Test.csproj", "{EF8F9430-A62D-4C3D-95FB-CF6FF4B32F2B}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Any CPU = Debug|Any CPU
9 | Release|Any CPU = Release|Any CPU
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {EF8F9430-A62D-4C3D-95FB-CF6FF4B32F2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
13 | {EF8F9430-A62D-4C3D-95FB-CF6FF4B32F2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
14 | {EF8F9430-A62D-4C3D-95FB-CF6FF4B32F2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
15 | {EF8F9430-A62D-4C3D-95FB-CF6FF4B32F2B}.Release|Any CPU.Build.0 = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:2.0.50727.5448
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace NAudio_Pitchshifter_Test.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NAudio-Pitchshifter
2 |
3 | A realtime C# audio pitch shifter based on S. M. Bernsees phase vocoder that implements an NAudio ISampleProvider.
4 |
5 | Or in simple words: this lets you change the pitch of audio (e.g. in realtime playback using the NAudio library).
6 |
7 | UPDATE: contributed the newest version to NAudio, where it is now included from V1.8 on:
8 | https://github.com/naudio/NAudio/blob/master/NAudio/Wave/SampleProviders/SMBPitchShiftingSampleProvider.cs
9 |
10 |
11 | Test application screenshot:
12 |
13 |
14 | 
15 |
16 | ________________________________________________________________________________________
17 |
18 |
19 | Usage
20 |
21 | ________________________________________________________________________________________
22 |
23 |
24 | Imports NAudio.Wave
25 |
26 | // Choose FFTSize and Osamp. (recommended are 4096 and 4)
27 | // Define Pitch shifting factor. (0.5f pitches one octave down, 2f would pitch one octave up)
28 |
29 | Sub Test()
30 |
31 | SMBPitchShiftingSampleProvider SMB = new SMBPitchShiftingSampleProvider(new AudioFileReader(@"C:\Test.wav"), 4096, 4L, 0.5f);
32 |
33 | WaveOutEvent wo = new WaveOutEvent
34 | {
35 | DesiredLatency = 150,
36 | NumberOfBuffers = 3
37 | };
38 |
39 | wo.Init(new SampleToWaveProvider16(SMB));
40 |
41 | wo.Play();
42 |
43 | End Sub
44 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // Allgemeine Informationen über eine Assembly werden über die folgenden
6 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
7 | // die mit einer Assembly verknüpft sind.
8 | [assembly: AssemblyTitle("NAudio Pitchshifter Test")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Microsoft")]
12 | [assembly: AssemblyProduct("NAudio Pitchshifter Test")]
13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
18 | // für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
19 | // COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
20 | [assembly: ComVisible(false)]
21 |
22 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
23 | [assembly: Guid("02b8e433-9003-4485-ad1c-f611ee5a017b")]
24 |
25 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
26 | //
27 | // Hauptversion
28 | // Nebenversion
29 | // Buildnummer
30 | // Revision
31 | //
32 | // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
33 | // übernehmen, indem Sie "*" eingeben:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/FormMain.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Windows.Forms;
9 | using NAudio.Wave;
10 | using NAudio.Wave.SampleProviders;
11 |
12 | namespace NAudio_Pitchshifter_Test
13 | {
14 | public partial class FormMain : Form
15 | {
16 | public FormMain()
17 | {
18 | InitializeComponent();
19 | }
20 |
21 | SMBPitchShiftingSampleProvider SMB;
22 | float Pitch = 1f;
23 |
24 | private void trackBar1_Scroll(object sender, EventArgs e)
25 | {
26 | if (trackBar1.Value > 11)
27 | {
28 | Pitch = (float)((trackBar1.Value - 1) / 10f);
29 | }
30 | else if (trackBar1.Value < 11)
31 | {
32 | Pitch = (float)(((trackBar1.Value - 1) / 10f * 0.5f) + 0.5f);
33 | }
34 | else
35 | {
36 | Pitch = 1f;
37 | }
38 | button1.Text = Pitch.ToString();
39 | if (!Object.ReferenceEquals(null, SMB))
40 | {
41 | SMB.PitchFactor = Pitch;
42 | }
43 | }
44 |
45 | private void button2_Click(object sender, EventArgs e)
46 | {
47 | OpenFileDialog OFD = new OpenFileDialog();
48 | if (OFD.ShowDialog() == DialogResult.OK)
49 | {
50 | SMB = new SMBPitchShiftingSampleProvider(new AudioFileReader(OFD.FileName), 4096, 8L, Pitch);
51 | WaveOutEvent wo = new WaveOutEvent
52 | {
53 | DesiredLatency = 150,
54 | NumberOfBuffers = 3
55 | };
56 | wo.Init(new SampleToWaveProvider16(SMB));
57 | wo.Play();
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // Dieser Code wurde von einem Tool generiert.
4 | // Laufzeitversion:2.0.50727.5448
5 | //
6 | // Änderungen an dieser Datei können fehlerhaftes Verhalten verursachen und gehen verloren, wenn
7 | // der Code neu generiert wird.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace NAudio_Pitchshifter_Test.Properties
12 | {
13 |
14 |
15 | ///
16 | /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
17 | ///
18 | // Diese Klasse wurde von der StronglyTypedResourceBuilder-Klasse
19 | // über ein Tool wie ResGen oder Visual Studio automatisch generiert.
20 | // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
21 | // mit der Option /str erneut aus, oder erstellen Sie Ihr VS-Projekt neu.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources
26 | {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources()
34 | {
35 | }
36 |
37 | ///
38 | /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
39 | ///
40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41 | internal static global::System.Resources.ResourceManager ResourceManager
42 | {
43 | get
44 | {
45 | if ((resourceMan == null))
46 | {
47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NAudio_Pitchshifter_Test.Properties.Resources", typeof(Resources).Assembly);
48 | resourceMan = temp;
49 | }
50 | return resourceMan;
51 | }
52 | }
53 |
54 | ///
55 | /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
56 | /// Ressourcenlookups, die diese stark typisierte Ressourcenklasse verwenden.
57 | ///
58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
59 | internal static global::System.Globalization.CultureInfo Culture
60 | {
61 | get
62 | {
63 | return resourceCulture;
64 | }
65 | set
66 | {
67 | resourceCulture = value;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/NAudio Pitchshifter Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.21022
7 | 2.0
8 | {EF8F9430-A62D-4C3D-95FB-CF6FF4B32F2B}
9 | WinExe
10 | Properties
11 | NAudio_Pitchshifter_Test
12 | NAudio Pitchshifter Test
13 | v3.5
14 | 512
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 |
36 |
37 | 3.5
38 |
39 |
40 | 3.5
41 |
42 |
43 | 3.5
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | Form
54 |
55 |
56 | FormMain.cs
57 |
58 |
59 |
60 |
61 |
62 | FormMain.cs
63 | Designer
64 |
65 |
66 | ResXFileCodeGenerator
67 | Resources.Designer.cs
68 | Designer
69 |
70 |
71 | True
72 | Resources.resx
73 |
74 |
75 | SettingsSingleFileGenerator
76 | Settings.Designer.cs
77 |
78 |
79 | True
80 | Settings.settings
81 | True
82 |
83 |
84 |
85 |
86 |
93 |
--------------------------------------------------------------------------------
/FormMain.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace NAudio_Pitchshifter_Test
2 | {
3 | partial class FormMain
4 | {
5 | ///
6 | /// Erforderliche Designervariable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Verwendete Ressourcen bereinigen.
12 | ///
13 | /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Vom Windows Form-Designer generierter Code
24 |
25 | ///
26 | /// Erforderliche Methode für die Designerunterstützung.
27 | /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.trackBar1 = new System.Windows.Forms.TrackBar();
32 | this.label1 = new System.Windows.Forms.Label();
33 | this.button1 = new System.Windows.Forms.Button();
34 | this.button2 = new System.Windows.Forms.Button();
35 | ((System.ComponentModel.ISupportInitialize)(this.trackBar1)).BeginInit();
36 | this.SuspendLayout();
37 | //
38 | // trackBar1
39 | //
40 | this.trackBar1.Location = new System.Drawing.Point(17, 118);
41 | this.trackBar1.Maximum = 21;
42 | this.trackBar1.Minimum = 1;
43 | this.trackBar1.Name = "trackBar1";
44 | this.trackBar1.Size = new System.Drawing.Size(259, 45);
45 | this.trackBar1.TabIndex = 0;
46 | this.trackBar1.Value = 11;
47 | this.trackBar1.Scroll += new System.EventHandler(this.trackBar1_Scroll);
48 | //
49 | // label1
50 | //
51 | this.label1.AutoSize = true;
52 | this.label1.Location = new System.Drawing.Point(114, 86);
53 | this.label1.Name = "label1";
54 | this.label1.Size = new System.Drawing.Size(61, 13);
55 | this.label1.TabIndex = 1;
56 | this.label1.Text = "PitchFactor";
57 | //
58 | // button1
59 | //
60 | this.button1.Enabled = false;
61 | this.button1.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
62 | this.button1.Location = new System.Drawing.Point(108, 164);
63 | this.button1.Name = "button1";
64 | this.button1.Size = new System.Drawing.Size(75, 23);
65 | this.button1.TabIndex = 2;
66 | this.button1.Text = "1";
67 | this.button1.UseVisualStyleBackColor = true;
68 | //
69 | // button2
70 | //
71 | this.button2.Location = new System.Drawing.Point(89, 26);
72 | this.button2.Name = "button2";
73 | this.button2.Size = new System.Drawing.Size(117, 23);
74 | this.button2.TabIndex = 3;
75 | this.button2.Text = "Open file";
76 | this.button2.UseVisualStyleBackColor = true;
77 | this.button2.Click += new System.EventHandler(this.button2_Click);
78 | //
79 | // FormMain
80 | //
81 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
82 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
83 | this.ClientSize = new System.Drawing.Size(292, 209);
84 | this.Controls.Add(this.button2);
85 | this.Controls.Add(this.button1);
86 | this.Controls.Add(this.label1);
87 | this.Controls.Add(this.trackBar1);
88 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
89 | this.MaximizeBox = false;
90 | this.Name = "FormMain";
91 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
92 | this.Text = "NAudio Pitchshift Example";
93 | ((System.ComponentModel.ISupportInitialize)(this.trackBar1)).EndInit();
94 | this.ResumeLayout(false);
95 | this.PerformLayout();
96 |
97 | }
98 |
99 | #endregion
100 |
101 | private System.Windows.Forms.TrackBar trackBar1;
102 | private System.Windows.Forms.Label label1;
103 | private System.Windows.Forms.Button button1;
104 | private System.Windows.Forms.Button button2;
105 | }
106 | }
107 |
108 |
--------------------------------------------------------------------------------
/SMBPitchShiftingSampleProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Data;
5 | using System.Diagnostics;
6 | using NAudio.Wave;
7 |
8 | ///
9 | /// Author: Freefall
10 | /// Date: 05.08.16
11 | /// Based on: the port of Stephan M. Bernsee´s pitch shifting class
12 | /// Port site: https://sites.google.com/site/mikescoderama/pitch-shifting
13 | /// Test application and github site: https://github.com/Freefall63/NAudio-Pitchshifter
14 | ///
15 | /// NOTE: I strongly advice to add a Limiter for post-processing.
16 | /// For my needs the FastAttackCompressor1175 provides acceptable results:
17 | /// https://github.com/Jiyuu/SkypeFX/blob/master/JSNet/FastAttackCompressor1175.cs
18 | ///
19 | /// UPDATE: Added a simple Limiter based on the pydirac implementation.
20 | /// https://github.com/echonest/remix/blob/master/external/pydirac225/source/Dirac_LE.cpp
21 | ///
22 | public class SMBPitchShiftingSampleProvider : ISampleProvider
23 | {
24 | //Shifter objects
25 | private ISampleProvider SourceStream = null;
26 | private WaveFormat WFormat = null;
27 | private float Pitch = 1f;
28 | private int _FFTSize;
29 | private long _osamp;
30 | private SMBPitchShifter ShifterLeft = new SMBPitchShifter();
31 | private SMBPitchShifter ShifterRight = new SMBPitchShifter();
32 |
33 | //Limiter constants
34 | const float LIM_THRESH = 0.95f;
35 | const float LIM_RANGE = (1f - LIM_THRESH);
36 | const float M_PI_2 = (float)(Math.PI / 2);
37 |
38 | public SMBPitchShiftingSampleProvider(ISampleProvider SourceProvider)
39 | : this(SourceProvider, 4096, 4L, 1f) { }
40 |
41 | public SMBPitchShiftingSampleProvider(ISampleProvider SourceProvider, int FFTSize, long osamp, float InitialPitch)
42 | {
43 | SourceStream = SourceProvider;
44 | WFormat = SourceProvider.WaveFormat;
45 | _FFTSize = FFTSize;
46 | _osamp = osamp;
47 | PitchFactor = InitialPitch;
48 | }
49 |
50 | public int Read(float[] buffer, int offset, int count)
51 | {
52 | int SampRead = SourceStream.Read(buffer, offset, count);
53 | if (Pitch == 1f)
54 | //Nothing to do.
55 | return SampRead;
56 | if (WFormat.Channels == 1) {
57 | float[] Mono = new float[SampRead];
58 | int index = 0;
59 | for (int sample = offset; sample <= SampRead + offset - 1; sample++) {
60 | Mono[index] = buffer[sample];
61 | index += 1;
62 | }
63 | ShifterLeft.PitchShift(Pitch, SampRead, _FFTSize, _osamp, WFormat.SampleRate, Mono);
64 | index = 0;
65 | for (int sample = offset; sample <= SampRead + offset - 1; sample++) {
66 | buffer[sample] = Limiter(Mono[index]);
67 | index += 1;
68 | }
69 | return SampRead;
70 | } else if (WFormat.Channels == 2) {
71 | float[] Left = new float[(SampRead >> 1)];
72 | float[] Right = new float[(SampRead >> 1)];
73 | int index = 0;
74 | for (int sample = offset; sample <= SampRead + offset - 1; sample += 2) {
75 | Left[index] = buffer[sample];
76 | Right[index] = buffer[sample + 1];
77 | index += 1;
78 | }
79 | ShifterLeft.PitchShift(Pitch, SampRead >> 1, _FFTSize, _osamp, WFormat.SampleRate, Left);
80 | ShifterRight.PitchShift(Pitch, SampRead >> 1, _FFTSize, _osamp, WFormat.SampleRate, Right);
81 | index = 0;
82 | for (int sample = offset; sample <= SampRead + offset - 1; sample += 2) {
83 | buffer[sample] = Limiter(Left[index]);
84 | buffer[sample + 1] = Limiter(Right[index]);
85 | index += 1;
86 | }
87 | return SampRead;
88 | } else {
89 | throw new Exception("Shifting of more than 2 channels is currently not supported.");
90 | }
91 | }
92 |
93 | public NAudio.Wave.WaveFormat WaveFormat {
94 | get { return WFormat; }
95 | }
96 |
97 | public float PitchFactor {
98 | get { return Pitch; }
99 | set {
100 | Pitch = value;
101 | }
102 | }
103 |
104 | private float Limiter(float Sample)
105 | {
106 | float res = 0f;
107 | if ((LIM_THRESH < Sample)) {
108 | res = (Sample - LIM_THRESH) / LIM_RANGE;
109 | res = (float)((Math.Atan(res) / M_PI_2) * LIM_RANGE + LIM_THRESH);
110 | } else if ((Sample < -LIM_THRESH)) {
111 | res = -(Sample + LIM_THRESH) / LIM_RANGE;
112 | res = -(float)((Math.Atan(res) / M_PI_2) * LIM_RANGE + LIM_THRESH);
113 | } else {
114 | res = Sample;
115 | }
116 | return res;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Properties/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 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/FormMain.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/SMBPitchShifter.cs:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | *
3 | * NAME: PitchShift.cs
4 | * VERSION: 1.0
5 | * HOME URL: http://www.dspdimension.com
6 | * KNOWN BUGS: none
7 | *
8 | * SYNOPSIS: Routine for doing pitch shifting while maintaining
9 | * duration using the Short Time Fourier Transform.
10 | *
11 | * DESCRIPTION: The routine takes a pitchShift factor value which is between 0.5
12 | * (one octave down) and 2. (one octave up). A value of exactly 1 does not change
13 | * the pitch. numSampsToProcess tells the routine how many samples in indata[0...
14 | * numSampsToProcess-1] should be pitch shifted and moved to outdata[0 ...
15 | * numSampsToProcess-1]. The two buffers can be identical (ie. it can process the
16 | * data in-place). fftFrameSize defines the FFT frame size used for the
17 | * processing. Typical values are 1024, 2048 and 4096. It may be any value <=
18 | * MAX_FRAME_LENGTH but it MUST be a power of 2. osamp is the STFT
19 | * oversampling factor which also determines the overlap between adjacent STFT
20 | * frames. It should at least be 4 for moderate scaling ratios. A value of 32 is
21 | * recommended for best quality. sampleRate takes the sample rate for the signal
22 | * in unit Hz, ie. 44100 for 44.1 kHz audio. The data passed to the routine in
23 | * indata[] should be in the range [-1.0, 1.0), which is also the output range
24 | * for the data, make sure you scale the data accordingly (for 16bit signed integers
25 | * you would have to divide (and multiply) by 32768).
26 | *
27 | * COPYRIGHT 1999-2006 Stephan M. Bernsee
28 | *
29 | * The Wide Open License (WOL)
30 | *
31 | * Permission to use, copy, modify, distribute and sell this software and its
32 | * documentation for any purpose is hereby granted without fee, provided that
33 | * the above copyright notice and this license appear in all source copies.
34 | * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF
35 | * ANY KIND. See http://www.dspguru.com/wol.htm for more information.
36 | *
37 | *****************************************************************************/
38 |
39 | /****************************************************************************
40 | *
41 | * This code was converted to C# by Michael Knight
42 | * madmik3 at gmail dot com.
43 | * http://sites.google.com/site/mikescoderama/
44 | *
45 | *****************************************************************************/
46 |
47 | using System;
48 | using System.Collections.Generic;
49 | using System.Text;
50 |
51 | public class SMBPitchShifter
52 | {
53 |
54 | private static int MAX_FRAME_LENGTH = 16000;
55 | private float[] gInFIFO = new float[MAX_FRAME_LENGTH];
56 | private float[] gOutFIFO = new float[MAX_FRAME_LENGTH];
57 | private float[] gFFTworksp = new float[2 * MAX_FRAME_LENGTH];
58 | private float[] gLastPhase = new float[MAX_FRAME_LENGTH / 2 + 1];
59 | private float[] gSumPhase = new float[MAX_FRAME_LENGTH / 2 + 1];
60 | private float[] gOutputAccum = new float[2 * MAX_FRAME_LENGTH];
61 | private float[] gAnaFreq = new float[MAX_FRAME_LENGTH];
62 | private float[] gAnaMagn = new float[MAX_FRAME_LENGTH];
63 | private float[] gSynFreq = new float[MAX_FRAME_LENGTH];
64 | private float[] gSynMagn = new float[MAX_FRAME_LENGTH];
65 | private long gRover, gInit;
66 |
67 | public void PitchShift(float pitchShift, long numSampsToProcess,
68 | float sampleRate, float[] indata)
69 | {
70 | PitchShift(pitchShift, numSampsToProcess, (long)2048, (long)10, sampleRate, indata);
71 | }
72 | public void PitchShift(float pitchShift, long numSampsToProcess, long fftFrameSize,
73 | long osamp, float sampleRate, float[] indata)
74 | {
75 | double magn, phase, tmp, window, real, imag;
76 | double freqPerBin, expct;
77 | long i, k, qpd, index, inFifoLatency, stepSize, fftFrameSize2;
78 |
79 |
80 | float[] outdata = indata;
81 | /* set up some handy variables */
82 | fftFrameSize2 = fftFrameSize / 2;
83 | stepSize = fftFrameSize / osamp;
84 | freqPerBin = sampleRate / (double)fftFrameSize;
85 | expct = 2.0 * Math.PI * (double)stepSize / (double)fftFrameSize;
86 | inFifoLatency = fftFrameSize - stepSize;
87 | if (gRover == 0) gRover = inFifoLatency;
88 |
89 |
90 | /* main processing loop */
91 | for (i = 0; i < numSampsToProcess; i++)
92 | {
93 |
94 | /* As long as we have not yet collected enough data just read in */
95 | gInFIFO[gRover] = indata[i];
96 | outdata[i] = gOutFIFO[gRover - inFifoLatency];
97 | gRover++;
98 |
99 | /* now we have enough data for processing */
100 | if (gRover >= fftFrameSize)
101 | {
102 | gRover = inFifoLatency;
103 |
104 | /* do windowing and re,im interleave */
105 | for (k = 0; k < fftFrameSize; k++)
106 | {
107 | window = -.5 * Math.Cos(2.0 * Math.PI * (double)k / (double)fftFrameSize) + .5;
108 | gFFTworksp[2 * k] = (float)(gInFIFO[k] * window);
109 | gFFTworksp[2 * k + 1] = 0.0F;
110 | }
111 |
112 |
113 | /* ***************** ANALYSIS ******************* */
114 | /* do transform */
115 | ShortTimeFourierTransform(gFFTworksp, fftFrameSize, -1);
116 |
117 | /* this is the analysis step */
118 | for (k = 0; k <= fftFrameSize2; k++)
119 | {
120 |
121 | /* de-interlace FFT buffer */
122 | real = gFFTworksp[2 * k];
123 | imag = gFFTworksp[2 * k + 1];
124 |
125 | /* compute magnitude and phase */
126 | magn = 2.0 * Math.Sqrt(real * real + imag * imag);
127 | phase = Math.Atan2(imag, real);
128 |
129 | /* compute phase difference */
130 | tmp = phase - gLastPhase[k];
131 | gLastPhase[k] = (float)phase;
132 |
133 | /* subtract expected phase difference */
134 | tmp -= (double)k * expct;
135 |
136 | /* map delta phase into +/- Pi interval */
137 | qpd = (long)(tmp / Math.PI);
138 | if (qpd >= 0) qpd += qpd & 1;
139 | else qpd -= qpd & 1;
140 | tmp -= Math.PI * (double)qpd;
141 |
142 | /* get deviation from bin frequency from the +/- Pi interval */
143 | tmp = osamp * tmp / (2.0 * Math.PI);
144 |
145 | /* compute the k-th partials' true frequency */
146 | tmp = (double)k * freqPerBin + tmp * freqPerBin;
147 |
148 | /* store magnitude and true frequency in analysis arrays */
149 | gAnaMagn[k] = (float)magn;
150 | gAnaFreq[k] = (float)tmp;
151 |
152 | }
153 |
154 | /* ***************** PROCESSING ******************* */
155 | /* this does the actual pitch shifting */
156 | for (int zero = 0; zero < fftFrameSize; zero++)
157 | {
158 | gSynMagn[zero] = 0;
159 | gSynFreq[zero] = 0;
160 | }
161 |
162 | for (k = 0; k <= fftFrameSize2; k++)
163 | {
164 | index = (long)(k * pitchShift);
165 | if (index <= fftFrameSize2)
166 | {
167 | gSynMagn[index] += gAnaMagn[k];
168 | gSynFreq[index] = gAnaFreq[k] * pitchShift;
169 | }
170 | }
171 |
172 | /* ***************** SYNTHESIS ******************* */
173 | /* this is the synthesis step */
174 | for (k = 0; k <= fftFrameSize2; k++)
175 | {
176 |
177 | /* get magnitude and true frequency from synthesis arrays */
178 | magn = gSynMagn[k];
179 | tmp = gSynFreq[k];
180 |
181 | /* subtract bin mid frequency */
182 | tmp -= (double)k * freqPerBin;
183 |
184 | /* get bin deviation from freq deviation */
185 | tmp /= freqPerBin;
186 |
187 | /* take osamp into account */
188 | tmp = 2.0 * Math.PI * tmp / osamp;
189 |
190 | /* add the overlap phase advance back in */
191 | tmp += (double)k * expct;
192 |
193 | /* accumulate delta phase to get bin phase */
194 | gSumPhase[k] += (float)tmp;
195 | phase = gSumPhase[k];
196 |
197 | /* get real and imag part and re-interleave */
198 | gFFTworksp[2 * k] = (float)(magn * Math.Cos(phase));
199 | gFFTworksp[2 * k + 1] = (float)(magn * Math.Sin(phase));
200 | }
201 |
202 | /* zero negative frequencies */
203 | for (k = fftFrameSize + 2; k < 2 * fftFrameSize; k++) gFFTworksp[k] = 0.0F;
204 |
205 | /* do inverse transform */
206 | ShortTimeFourierTransform(gFFTworksp, fftFrameSize, 1);
207 |
208 | /* do windowing and add to output accumulator */
209 | for (k = 0; k < fftFrameSize; k++)
210 | {
211 | window = -.5 * Math.Cos(2.0 * Math.PI * (double)k / (double)fftFrameSize) + .5;
212 | gOutputAccum[k] += (float)(2.0 * window * gFFTworksp[2 * k] / (fftFrameSize2 * osamp));
213 | }
214 | for (k = 0; k < stepSize; k++) gOutFIFO[k] = gOutputAccum[k];
215 |
216 | /* shift accumulator */
217 | //memmove(gOutputAccum, gOutputAccum + stepSize, fftFrameSize * sizeof(float));
218 | for (k = 0; k < fftFrameSize; k++)
219 | {
220 | gOutputAccum[k] = gOutputAccum[k + stepSize];
221 | }
222 |
223 | /* move input FIFO */
224 | for (k = 0; k < inFifoLatency; k++) gInFIFO[k] = gInFIFO[k + stepSize];
225 | }
226 | }
227 | }
228 |
229 | public void ShortTimeFourierTransform(float[] fftBuffer, long fftFrameSize, long sign)
230 | {
231 | float wr, wi, arg, temp;
232 | float tr, ti, ur, ui;
233 | long i, bitm, j, le, le2, k;
234 |
235 | for (i = 2; i < 2 * fftFrameSize - 2; i += 2)
236 | {
237 | for (bitm = 2, j = 0; bitm < 2 * fftFrameSize; bitm <<= 1)
238 | {
239 | if ((i & bitm) != 0) j++;
240 | j <<= 1;
241 | }
242 | if (i < j)
243 | {
244 | temp = fftBuffer[i];
245 | fftBuffer[i] = fftBuffer[j];
246 | fftBuffer[j] = temp;
247 | temp = fftBuffer[i + 1];
248 | fftBuffer[i + 1] = fftBuffer[j + 1];
249 | fftBuffer[j + 1] = temp;
250 | }
251 | }
252 | long max = (long)(Math.Log(fftFrameSize) / Math.Log(2.0) + .5);
253 | for (k = 0, le = 2; k < max; k++)
254 | {
255 | le <<= 1;
256 | le2 = le >> 1;
257 | ur = 1.0F;
258 | ui = 0.0F;
259 | arg = (float)Math.PI / (le2 >> 1);
260 | wr = (float)Math.Cos(arg);
261 | wi = (float)(sign * Math.Sin(arg));
262 | for (j = 0; j < le2; j += 2)
263 | {
264 |
265 | for (i = j; i < 2 * fftFrameSize; i += le)
266 | {
267 | tr = fftBuffer[i + le2] * ur - fftBuffer[i + le2 + 1] * ui;
268 | ti = fftBuffer[i + le2] * ui + fftBuffer[i + le2 + 1] * ur;
269 | fftBuffer[i + le2] = fftBuffer[i] - tr;
270 | fftBuffer[i + le2 + 1] = fftBuffer[i + 1] - ti;
271 | fftBuffer[i] += tr;
272 | fftBuffer[i + 1] += ti;
273 |
274 | }
275 | tr = ur * wr - ui * wi;
276 | ui = ur * wi + ui * wr;
277 | ur = tr;
278 | }
279 | }
280 | }
281 | }
282 |
--------------------------------------------------------------------------------