상속: SoundObj
예제 #1
0
        static void Main(string[] args)
        {
            // Find where this executable is launched from
            string[] cargs = Environment.GetCommandLineArgs();
            _thisFolder = Path.GetDirectoryName(cargs[0]);
            if (String.IsNullOrEmpty(_thisFolder))
            {
                _thisFolder = Environment.CurrentDirectory;
            }

            string appData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
            _impulsesFolder = Path.GetFullPath(Path.Combine(appData, "InguzEQ" + slash + "Impulses" + slash));

            string[] inFiles = new string[4];
            string inL = "";
            string inR = "";
            if (!DisplayInfo())
            {
                return;
            }

            bool ok = (args.Length > 0);
            bool longUsage = false;

            for (int j = 0; ok && j < args.Length; j++)
            {
                string arg = args[j];
                switch (args[j].ToUpperInvariant())
                {
                    case "/?":
                    case "-?":
                    case "/H":
                    case "/HELP":
                        ok = false;
                        longUsage = true;
                        break;

                    case "/L":
                    case "/0":
                        inFiles[0] = args[++j];
                        _nInFiles = Math.Max(_nInFiles, 1);
                        break;

                    case "/R":
                    case "/1":
                        inFiles[1] = args[++j];
                        _nInFiles = Math.Max(_nInFiles, 2);
                        break;

                    case "/2":
                        inFiles[2] = args[++j];
                        _nInFiles = Math.Max(_nInFiles, 3);
                        break;
                    case "/3":
                        inFiles[3] = args[++j];
                        _nInFiles = Math.Max(_nInFiles, 4);
                        break;

                    case "/LENGTH":
                        _filterLen = int.Parse(args[++j], CultureInfo.InvariantCulture);
                        if (_filterLen < 16)
                        {
                            throw new Exception("Length is too small.");
                        }
                        break;

                    case "/DBL":
                        _dbl = true;
                        break;

                    case "/PCM":
                        _pcm = true;
                        break;

                    case "/NODRC":
                        _noDRC = true;
                        break;

                    case "/NOSKEW":
                        _noSkew = true;
                        break;

                    case "/NONORM":
                        // No normalization of the impulse response (undocumented)
                        _noNorm = true;
                        break;

                    case "/SPLIT":
                        _split = true;
                        break;

                    case "/COPY":
                        _copy = true;
                        break;

                    case "/GAIN":
                        _gain = double.Parse(args[++j], CultureInfo.InvariantCulture);
                        break;

                    case "/ALL":
                        // Returns negative-time components as part of the impulse response
                        // (experimental, to be used for THD measurement)
                        _returnAll = true;
                        break;

                    case "/POWER":
                        // Raises sweep to power n
                        // (experimental, to be used for THD measurement)
                        _power = int.Parse(args[++j], CultureInfo.InvariantCulture);
                        break;

                    case "/FMIN":
                        // (experimental, i.e. broken)
                        _fmin = int.Parse(args[++j], CultureInfo.InvariantCulture);
                        _fminSpecified = true;
                        break;

                    case "/FMAX":
                        // (experimental, i.e. broken)
                        _fmax = int.Parse(args[++j], CultureInfo.InvariantCulture);
                        _fmaxSpecified = true;
                        break;

                    case "/DIRECT":
                        // Create filtered (direct-sound) filters
                        _doDirectFilters = true;
                        break;

                    case "/NOSUB":
                        // Don't apply subsonic filter to the impulse response
                        _noSubsonicFilter = true;
                        break;

                    case "/NOOVER":
                        // Don't override DRC's settings for filter type and length
                        _noOverrideDRC = true;
                        break;

                    case "/KEEPTEMP":
                        // Undocumented
                        _keepTempFiles = true;
                        break;

                    case "/REFCH":
                        // Override the reference-channel detection
                        _refchannel = int.Parse(args[++j], CultureInfo.InvariantCulture);
                        if (_refchannel<0 || _refchannel > _nInFiles - 1)
                        {
                            throw new Exception(String.Format("RefCh can only be from 0 to {0}.", _nInFiles-1));
                        }
                        break;

                    case "/ENV":
                        // Undocumented.  Save the Hilbert envelope
                        _env = true;
                        break;

                    case "-":
                        // ignore
                        break;

                    default:
                        ok = false;
                        break;
                }
            }
            if (!ok)
            {
                DisplayUsage(longUsage);
            }
            else
            {
                try
                {
                    if (!_noDRC)
                    {
                        if (!File.Exists(GetDRCExe()))
                        {
                            stderr.WriteLine("Denis Sbragion's DRC (http://drc-fir.sourceforge.net/) was not found.");
                            stderr.WriteLine("Only the impulse response will be calculated, not correction filters.");
                            stderr.WriteLine("");
                            _noDRC = true;
                        }
                    }
                    if (!_noDRC)
                    {
                        FileInfo[] drcfiles = new DirectoryInfo(_thisFolder).GetFiles("*.drc");
                        if (drcfiles.Length == 0)
                        {
                            stderr.WriteLine("No .drc files were found in the current folder.");
                            stderr.WriteLine("Only the impulse response will be calculated, not correction filters.");
                            stderr.WriteLine("");
                            _noDRC = true;
                        }
                    }

                    for(int i=0; i<_nInFiles; i++)
                    {
                        string inFile = inFiles[i];
                        if (String.IsNullOrEmpty(inFile))
                        {
                            stderr.WriteLine("Error: The {0} input file was not specified.", FileDescription(i));
                            return;
                        }
                        if (!File.Exists(inFile))
                        {
                            stderr.WriteLine("Error: The {0} input file {1} was not found.", FileDescription(i), inFile);
                            return;
                        }

                        for (int j = 0; j < i; j++)
                        {
                            if (inFile.Equals(inFiles[j]))
                            {
                                stderr.WriteLine("Warning: The same input file ({0}) was specified for both {1} and {2}!", inFile, FileDescription(j), FileDescription(i));
                                //stderr.WriteLine();
                            }
                        }
                    }

                    // Temporary
                    if (_nInFiles != 2)
                    {
                        stderr.WriteLine("Error: Two input files must be specified.");
                        return;
                    }
                    inL = inFiles[0];
                    inR = inFiles[1];
                    // end temporary

                    uint sampleRate;
                    List<SoundObj> impulses;
                    List<ISoundObj> filteredImpulses;
                    List<string> impDirects;
                    List<Complex[]> impulseFFTs;
                    List<double> maxs;

                    SoundObj impulseL;
                    SoundObj impulseR;
                    ISoundObj filteredImpulseL = null;
                    ISoundObj filteredImpulseR = null;

                    string impDirectL = null;
                    string impDirectR = null;

                    Complex[] impulseLFFT;
                    Complex[] impulseRFFT;
                    WaveWriter writer;

                    ISoundObj buff;
                    double g;

                    if (!_keepTempFiles)
                    {
                        _tempFiles.Add("rps.pcm");
                        _tempFiles.Add("rtc.pcm");
                    }

                    // Find the left impulse
                    stderr.WriteLine("Processing left measurement ({0})...", inL);
                    impulseL = Deconvolve(inL, out impulseLFFT, out _peakPosL);
                    sampleRate = impulseL.SampleRate;
                    _sampleRate = sampleRate;
                    double peakM = Math.Round(MathUtil.Metres(_peakPosL, sampleRate), 2);
                    double peakFt = Math.Round(MathUtil.Feet(_peakPosL, sampleRate), 2);
                    stderr.WriteLine("  Impulse peak at sample {0} ({1}m, {2}ft)", _peakPosL, peakM, peakFt);

                    // Write to PCM
                    string impFileL = Path.GetFileNameWithoutExtension(inL) + "_imp" + ".pcm";
                    if (!_keepTempFiles)
                    {
                        _tempFiles.Add(impFileL);
                    }
                    writer = new WaveWriter(impFileL);
                    writer.Input = impulseL;
                    writer.Format = WaveFormat.IEEE_FLOAT;
                    writer.BitsPerSample = 32;
                    writer.SampleRate = _sampleRate;
                    writer.Raw = true;
                    writer.Run();
                    writer.Close();

                    // Write the impulseFFT to disk
                    int L = impulseLFFT.Length;
                    string impTempL = Path.GetFileNameWithoutExtension(inL) + "_imp" + ".dat";
                    _tempFiles.Add(impTempL);
                    writer = new WaveWriter(impTempL);
                    writer.Input = new CallbackSource(2, sampleRate, delegate(long j)
                    {
                        if (j >= L / 2)
                        {
                            return null;
                        }
                        Complex si = impulseLFFT[j]; // +impulseLFFT[L - j - 1];
                        ISample s = new Sample2();
                        s[0] = si.Magnitude;
                        s[1] = si.Phase / Math.PI;
                        return s;
                    });
                    writer.Format = WaveFormat.IEEE_FLOAT;
                    writer.BitsPerSample = 32;
                    writer.SampleRate = _sampleRate;
                    writer.Raw = false;
                    writer.Run();
                    writer.Close();
                    writer = null;

                    impulseLFFT = null;
                    GC.Collect();

                    if (_doDirectFilters)
                    {
                        // Sliding low-pass filter over the impulse
                        stderr.WriteLine("  Filtering...");
                        filteredImpulseL = SlidingLowPass(impulseL, _peakPosL);

                        // Write PCM for the filtered impulse
                        impDirectL = Path.GetFileNameWithoutExtension(inL) + "_impfilt" + ".pcm";
                        if (!_keepTempFiles)
                        {
                            _tempFiles.Add(impDirectL);
                        }
                        writer = new WaveWriter(impDirectL);
                        writer.Input = filteredImpulseL;
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.SampleRate = _sampleRate;
                        writer.BitsPerSample = 32;
                        writer.Raw = false;
                        writer.Run();
                        writer.Close();
                        writer = null;
                        filteredImpulseL.Reset();
                    }

                    GC.Collect();
                    stderr.WriteLine("  Deconvolution: left impulse done.");
                    stderr.WriteLine();

                    // Find the right impulse
                    stderr.WriteLine("Processing right measurement ({0})...", inR);
                    impulseR = Deconvolve(inR, out impulseRFFT, out _peakPosR);
                    peakM = Math.Round(MathUtil.Metres(_peakPosR, sampleRate), 2);
                    peakFt = Math.Round(MathUtil.Feet(_peakPosR, sampleRate), 2);
                    stderr.WriteLine("  Impulse peak at sample {0} ({1}m, {2}ft)", _peakPosR, peakM, peakFt);

                    // Write to PCM
                    string impFileR = Path.GetFileNameWithoutExtension(inR) + "_imp" + ".pcm";
                    if (!_keepTempFiles)
                    {
                        _tempFiles.Add(impFileR);
                    }
                    writer = new WaveWriter(impFileR);
                    writer.Input = impulseR;
                    writer.Format = WaveFormat.IEEE_FLOAT;
                    writer.BitsPerSample = 32;
                    writer.SampleRate = _sampleRate;
                    writer.Raw = true;
                    writer.Run();
                    writer.Close();

                    // Write the impulseFFT magnitude to disk
                    L = impulseRFFT.Length;
                    string impTempR = Path.GetFileNameWithoutExtension(inR) + "_imp" + ".dat";
                    _tempFiles.Add(impTempR);
                    writer = new WaveWriter(impTempR);
                    writer.Input = new CallbackSource(2, impulseR.SampleRate, delegate(long j)
                    {
                        if (j >= L / 2)
                        {
                            return null;
                        }
                        Complex si = impulseRFFT[j]; // +impulseRFFT[L - j - 1];
                        ISample s = new Sample2();
                        s[0] = si.Magnitude;
                        s[1] = si.Phase / Math.PI;
                        return s;
                    });
                    writer.Format = WaveFormat.IEEE_FLOAT;
                    writer.BitsPerSample = 32;
                    writer.SampleRate = _sampleRate;
                    writer.Raw = false;
                    writer.Run();
                    writer.Close();
                    writer = null;

                    impulseRFFT = null;
                    GC.Collect();

                    if (_doDirectFilters)
                    {
                        // Sliding low-pass filter over the impulse
                        stderr.WriteLine("  Filtering...");
                        filteredImpulseR = SlidingLowPass(impulseR, _peakPosR);

                        // Write PCM for the filtered impulse
                        impDirectR = Path.GetFileNameWithoutExtension(inR) + "_impfilt" + ".pcm";
                        if (!_keepTempFiles)
                        {
                            _tempFiles.Add(impDirectR);
                        }
                        writer = new WaveWriter(impDirectR);
                        writer.Input = filteredImpulseR;
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.BitsPerSample = 32;
                        writer.SampleRate = _sampleRate;
                        writer.Raw = false;
                        writer.Run();
                        writer.Close();
                        writer = null;
                        filteredImpulseR.Reset();
                    }

                    GC.Collect();

                    stderr.WriteLine("  Deconvolution: right impulse done.");
                    stderr.WriteLine();

                    // Join the left and right impulse files (truncated at 65536) into a WAV
                    // and normalize loudness for each channel
                    stderr.WriteLine("Splicing and normalizing (1)");
                    ChannelSplicer longstereoImpulse = new ChannelSplicer();

                    // (Don't normalize each channel's volume separately if _returnAll, it's just too expensive)
                    if (_returnAll)
                    {
                        buff = impulseL;
                    }
                    else
                    {
                        buff = new SoundBuffer(new SampleBuffer(impulseL).Subset(0, 131071));
                        g = Loudness.WeightedVolume(buff);
                        (buff as SoundBuffer).ApplyGain(1 / g);
                    }
                    longstereoImpulse.Add(buff);

                    if (_returnAll)
                    {
                        buff = impulseR;
                    }
                    else
                    {
                        buff = new SoundBuffer(new SampleBuffer(impulseR).Subset(0, 131071));
                        g = Loudness.WeightedVolume(buff);
                        (buff as SoundBuffer).ApplyGain(1 / g);
                    }
                    longstereoImpulse.Add(buff);

                    ISoundObj stereoImpulse = longstereoImpulse;

                    _impulseFiles.Add("Impulse_Response_Measured.wav: stereo impulse response from measurements");
                    writer = new WaveWriter("Impulse_Response_Measured.wav");
                    writer.Input = longstereoImpulse;
                    writer.Format = WaveFormat.IEEE_FLOAT;
                    writer.BitsPerSample = 32;
                    writer.SampleRate = _sampleRate;
                    writer.Normalization = -1;
                    writer.Raw = false;
                    writer.Run();
                    writer.Close();
                    writer = null;

                    if (_env)
                    {
                        // Also save the Hilbert envelope
                        HilbertEnvelope env = new HilbertEnvelope(8191);
                        env.Input = longstereoImpulse;
                        _impulseFiles.Add("Impulse_Response_Envelope.wav: Hilbert envelope of the impulse response");
                        writer = new WaveWriter("Impulse_Response_Envelope.wav");
                        writer.Input = env;
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.BitsPerSample = 32;
                        writer.SampleRate = _sampleRate;
                        writer.Normalization = -1;
                        writer.Raw = false;
                        writer.Run();
                        writer.Close();
                        writer = null;
                    }

                    if (_dbl)
                    {
                        // Create DBL files for Acourate
                        _impulseFiles.Add("PulseL.dbl: impulse response, raw data (64-bit float), left channel ");
                        _impulseFiles.Add("PulseR.dbl: impulse response, raw data (64-bit float), right channel");
                        _impulseFiles.Add("  (use skew=" + (_peakPosL - _peakPosR) + " for time alignment)");
                        WriteImpulseDBL(stereoImpulse, "PulseL.dbl", "PulseR.dbl");
                    }

                    if (_pcm)
                    {
                        // Create PCM files for Octave (etc)
                        _impulseFiles.Add("LUncorrected.pcm: impulse response, raw data (32-bit float), left channel");
                        _impulseFiles.Add("RUncorrected.pcm: impulse response, raw data (32-bit float), right channel");
                        WriteImpulsePCM(stereoImpulse, "LUncorrected.pcm", "RUncorrected.pcm");
                    }

                    stereoImpulse = null;
                    longstereoImpulse = null;
                    buff = null;
                    GC.Collect();

                    if (_doDirectFilters)
                    {
                        // Same for the filtered impulse response
                        stderr.WriteLine("Splicing and normalizing (2)");
                        ChannelSplicer longstereoImpulseF = new ChannelSplicer();

                        buff = new SoundBuffer(new SampleBuffer(filteredImpulseL).Subset(0, 131071));
                        double gL = Loudness.WeightedVolume(buff);
                        (buff as SoundBuffer).ApplyGain(1 / gL);
                        longstereoImpulseF.Add(buff);
                        FilterProfile lfgDirectL = new FilterProfile(buff, 0.5);

                        buff = new SoundBuffer(new SampleBuffer(filteredImpulseR).Subset(0, 131071));
                        double gR = Loudness.WeightedVolume(buff);
                        (buff as SoundBuffer).ApplyGain(1 / gR);
                        longstereoImpulseF.Add(buff);
                        FilterProfile lfgDirectR = new FilterProfile(buff, 0.5);

                        _impulseFiles.Add("Impulse_Response_Filtered.wav: approximation to direct-sound impulse response");
                        writer = new WaveWriter("Impulse_Response_Filtered.wav");
                        writer.Input = longstereoImpulseF;
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.BitsPerSample = 32;
                        writer.SampleRate = _sampleRate;
                        writer.Normalization = -1;
                        writer.Raw = false;
                        writer.Run();
                        writer.Close();
                        double gg = writer.Gain;
                        writer = null;
                        longstereoImpulseF = null;

                        ChannelSplicer longstereoImpulseD = new ChannelSplicer();

                        Mixer diffuse = new Mixer();
                        diffuse.Add(impulseL, 1.0);
                        diffuse.Add(filteredImpulseL, -1.0);
                        buff = new SoundBuffer(new SampleBuffer(diffuse).Subset(0, 131071));
                        (buff as SoundBuffer).ApplyGain(1 / gL);
                        longstereoImpulseD.Add(buff);
                        FilterProfile lfgDiffuseL = new FilterProfile(buff, 0.5);

                        diffuse = new Mixer();
                        diffuse.Add(impulseR, 1.0);
                        diffuse.Add(filteredImpulseR, -1.0);
                        buff = new SoundBuffer(new SampleBuffer(diffuse).Subset(0, 131071));
                        (buff as SoundBuffer).ApplyGain(1 / gR);
                        longstereoImpulseD.Add(buff);
                        FilterProfile lfgDiffuseR = new FilterProfile(buff, 0.5);

                        _impulseFiles.Add("Impulse_Response_Diffuse.wav: approximation to diffuse-field remnant");
                        writer = new WaveWriter("Impulse_Response_Diffuse.wav");
                        writer.Input = longstereoImpulseD;
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.BitsPerSample = 32;
                        writer.SampleRate = _sampleRate;
                        writer.Gain = gg;
                        writer.Raw = false;
                        writer.Run();
                        writer.Close();
                        writer = null;

                        // Filter the diffuse-field curve against double the diffuse-field curve
                        FilterImpulse fiDiffuse = new FilterImpulse(8192, HRTF.diffuseDiff0() * 2, FilterInterpolation.COSINE, sampleRate);
                        FastConvolver co = new FastConvolver(longstereoImpulseD, fiDiffuse);
                        SoundBuffer buffd = new SoundBuffer(co);
                        _impulseFiles.Add("Impulse_Response_Diffuse_Comp.wav: filtered diffuse-field remnant");
                        writer = new WaveWriter("Impulse_Response_Diffuse_Comp.wav");
                        writer.Input = buffd.Subset(4096);
                        writer.Format = WaveFormat.IEEE_FLOAT;
                        writer.BitsPerSample = 32;
                        writer.SampleRate = _sampleRate;
                        writer.Gain = gg;
                        writer.Raw = false;
                        writer.Run();
                        writer.Close();
                        writer = null;

                        longstereoImpulseD = null;

                        bool any = false;
                        string jsonFile = "Diff.json";
                        FileStream fs = new FileStream(jsonFile, FileMode.Create);
                        StreamWriter sw = new StreamWriter(fs);
                        sw.WriteLine("{");
                        FilterProfile lfgDiffL = lfgDirectL - lfgDiffuseL;
                        if (lfgDiffL != null)
                        {
                            if (any) sw.WriteLine(",");
                            any = true;
                            sw.Write(lfgDiffL.ToJSONString("DiffL", "Diffuse field relative to direct, left channel"));
                        }
                        FilterProfile lfgDiffR = lfgDirectR - lfgDiffuseR;
                        if (lfgDiffR != null)
                        {
                            if (any) sw.WriteLine(",");
                            any = true;
                            sw.Write(lfgDiffR.ToJSONString("DiffR", "Diffuse field relative to direct, right channel"));
                        }
                        sw.WriteLine("}");
                        sw.Close();
                        fs.Close();
                    }
                    buff = null;
                    GC.Collect();

                    System.Console.Error.WriteLine();

                    if (!_noDRC)
                    {
                        // Analyze the freq response
                        // and create targets
                        // target_full.txt and target_half.txt
                        stderr.WriteLine("Analyzing response curves.");
                        Prep(impTempL, impTempR, "Impulse_Response_Measured.wav", "NoCorrection");

                        // Call DRC to create the filters
                        // then splice the DRC left & right output files together
                        stderr.WriteLine("Preparing for DRC.");
                        if (DoDRC(impFileL, impFileR, impDirectL, impDirectR, _peakPosL, _peakPosR, "Impulse_Response_Measured.wav", "Impulse_Response_Filtered.wav"))
                        {
                            stderr.WriteLine("Success!");
                        }
                    }

                    // Report names of the impulse files created
                    if (_impulseFiles.Count == 0)
                    {
                        System.Console.Error.WriteLine("No impulse response files were created.");
                    }
                    if (_impulseFiles.Count > 0)
                    {
                        System.Console.Error.WriteLine("Impulse response files were created:");
                        foreach (string f in _impulseFiles)
                        {
                            string s = "  " + f;
                            System.Console.Error.WriteLine(s);
                        }
                    }

                    // Report names of the filter files created
                    if (_filterFiles.Count == 0 && !_noDRC)
                    {
                        System.Console.Error.WriteLine("No correction filter files were created.");
                    }
                    if (_filterFiles.Count > 0)
                    {
                        System.Console.Error.WriteLine("Correction filter files were created:");
                        foreach (string f in _filterFiles)
                        {
                            string s = "  " + f;
                            if (_copy)
                            {
                                try
                                {
                                    File.Copy(f, Path.Combine(_impulsesFolder, f), true);
                                    s += " (copied)";
                                }
                                catch (Exception e)
                                {
                                    s += " (not copied: " + e.Message + ")";
                                }
                            }
                            System.Console.Error.WriteLine(s);
                        }
                    }
                    if (_peakPosL == _peakPosR)
                    {
                        System.Console.Error.WriteLine();
                        System.Console.Error.WriteLine("Zero time difference between channels.  Are you sure the recordings are correct?");
                    }
                }
                catch (Exception e)
                {
                    stderr.WriteLine();
                    stderr.WriteLine(e.Message);
                    stderr.WriteLine(e.StackTrace);
                }
                finally
                {
                    foreach (string tempFile in _tempFiles)
                    {
                        try
                        {
                            File.Delete(tempFile);
                        }
                        catch (Exception) { /* ignore */ }
                    }
                }
            }
            stderr.Flush();
        }
예제 #2
0
        static ISoundObj GetSignalGenerator(double dBfs, out string desc)
        {
            double gain = MathUtil.gain(dBfs);
            ISoundObj signalGenerator = null;
            Sequencer seq;
            string description = "Unknown";
            switch (_siggen)
            {
                case "IDENT":
                    // Left-right identification: embedded resource
                    Assembly ass = Assembly.GetExecutingAssembly();
                    foreach (string s in ass.GetManifestResourceNames())
                    {
                        if (s.Contains("LeftRight"))
                        {
                            Stream res = ass.GetManifestResourceStream(s);
                            WaveReader rdr = new WaveReader(res);
                            // The stream is stereo, but we want to alternate
                            seq = new Sequencer();

                            for (int j = 0; j < 10; j++)
                            {
                                seq.Add(rdr, new List<double>(new double[] { gain, 0 }));
                                seq.Add(new NoiseGenerator(NoiseType.SILENCE, 2, 1.0, _inputSampleRate, 0.0, false));
                                seq.Add(rdr, new List<double>(new double[] { 0, gain }));
                                seq.Add(new NoiseGenerator(NoiseType.SILENCE, 2, 1.0, _inputSampleRate, 0.0, false));
                            }

                            signalGenerator = seq;
                            break;
                        }
                    }
                    /*
                    // Left-right identification signal: morse code
                    MorseCode envL = new MorseCode(" " + _sigparamA, 10, true);
                    ISoundObj sigL = new SweepGenerator(1, envL.LengthSeconds * 5, 220, 7040, _inputSampleRate, 0, false, gain, true);
                    envL.Input = sigL;

                    MorseCode envR = new MorseCode(" " + _sigparamB, 10, true);
                    ISoundObj sigR = new SweepGenerator(1, envR.LengthSeconds * 5, 7040, 220, _inputSampleRate, 0, false, gain, true);
                    envR.Input = sigR;

                    signalGenerator = new ChannelSplicer();
                    (signalGenerator as ChannelSplicer).Add(envL);
                    (signalGenerator as ChannelSplicer).Add(envR);
                    */
                    description = String.Format("Left/Right channel identification");
                    break;

                case "SWEEP":
                    seq = new Sequencer();
                    if (_sigparam1 == 0)
                    {
                        _sigparam1 = 45;
                    }
                    int lengthSamples = (int)(_sigparam1 * _inputSampleRate);
                    if (lengthSamples < 8388608)
                    {
                        // High-accuracy logarithmic sweep starting at 10Hz
                        int fade = (int)(_inputSampleRate / 20);
                        FFTSweepGenerator sg = new FFTSweepGenerator(2, lengthSamples, 10, _inputSampleRate / 2, _inputSampleRate, gain, false);
                        seq.Add(sg);
                        description = String.Format("Logarithmic sine sweep 10Hz to {0}Hz in {1} seconds", _inputSampleRate / 2, _sigparam1);
                    }
                    else
                    {
                        // Simple logarithmic sweep starting at 10Hz, windowed (uses much less memory!)
                        int fade = (int)(_inputSampleRate / 20);
                        BlackmanHarris bhwf = new BlackmanHarris(lengthSamples / 2, fade, (int)((lengthSamples / 2) - fade));
                        SweepGenerator sg = new SweepGenerator(2, lengthSamples, 10, _inputSampleRate / 2, _inputSampleRate, gain, false);
                        bhwf.Input = sg;
                        seq.Add(bhwf);
                        description = String.Format("Log sine sweep 10Hz to {0}Hz in {1} seconds", _inputSampleRate / 2, _sigparam1);
                    }
                    // Follow by 3 seconds of silence
                    seq.Add(new NoiseGenerator(NoiseType.SILENCE, 2, 3.0, _inputSampleRate, 0.0, false));
                    signalGenerator = seq;
                    break;

                case "SINE":
                    signalGenerator = new SineGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz sine", _sigparam1);
                    break;

                case "QUAD":
                    signalGenerator = new SineQuadGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz quadrature", _sigparam1);
                    break;

                case "SQUARE":
                    signalGenerator = new SquareGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz non-bandlimited square", _sigparam1);
                    break;

                case "BLSQUARE":
                    signalGenerator = new BandLimitedSquareGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz bandlimited square", _sigparam1);
                    break;

                case "TRIANGLE":
                    signalGenerator = new TriangleGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz non-bandlimited triangle", _sigparam1);
                    break;

                case "BLTRIANGLE":
                    signalGenerator = new BandLimitedTriangleGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz bandlimited triangle", _sigparam1);
                    break;

                case "SAWTOOTH":
                    signalGenerator = new SawtoothGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz non-bandlimited sawtooth", _sigparam1);
                    break;

                case "BLSAWTOOTH":
                    signalGenerator = new BandLimitedSawtoothGenerator(2, _inputSampleRate, _sigparam1, gain);
                    description = String.Format("{0}Hz bandlimited sawtooth", _sigparam1);
                    break;

                case "WHITE":
                    signalGenerator = new NoiseGenerator(NoiseType.WHITE, 2, int.MaxValue, _inputSampleRate, gain, true);
                    description = String.Format("White noise");
                    break;

                case "PINK":
                    bool mono = (_sigparam1 != 0 ? true : false);
                    signalGenerator = new NoiseGenerator(NoiseType.PINK, 2, int.MaxValue, _inputSampleRate, gain, mono);
                    description = String.Format("Pink noise {0}", mono ? "(mono)" : "(stereo)" );
                    break;

                case "INTERMODULATION":
                    double n = 1;
                    description = String.Format("Intermodulation test {0}Hz", _sigparam1);
                    if (_sigparam2 != 0) { n++; description = description + " + " + _sigparam2 + "Hz"; }
                    if (_sigparam3 != 0) { n++; description = description + " + " + _sigparam3 + "Hz"; }
                    signalGenerator = new Mixer();
                    (signalGenerator as Mixer).Add(new SineGenerator(2, _inputSampleRate, _sigparam1, gain), 1/n);
                    if (_sigparam2 != 0) (signalGenerator as Mixer).Add(new SineGenerator(2, _inputSampleRate, _sigparam2, gain), 1 / n);
                    if (_sigparam3 != 0) (signalGenerator as Mixer).Add(new SineGenerator(2, _inputSampleRate, _sigparam3, gain), 1 / n);
                    break;

                case "SHAPEDBURST":
                    description = String.Format("{0}Hz windowed (Blackman) over {1} cycles", _sigparam1, _sigparam2);
                    throw new NotImplementedException();
                    //break;

                default:
                    _siggen = null;
                    break;
            }
            if (IsSigGenEQ())
            {
                if (IsSigGenEQBoth())
                {
                    description = description + ", with EQ processing";
                }
                else if (IsSigGenEQL())
                {
                    description = description + ", with EQ processing in left channel";
                }
                else if (IsSigGenEQR())
                {
                    description = description + ", with EQ processing in right channel";
                }
                else
                {
                    description = description + ", with EQ processing";
                }
            }
            desc = description;
            return signalGenerator;
        }
예제 #3
0
        static ISoundObj DecodeBFormatCrossed(ISoundObj source)
        {
            // Stereo decode:
            // A shuffle of the X (front-back) and Y (left-right) channels
            // with some W mixed, so the effective mics become (hyper)cardioid instead of figure-8.
            // If _ambiCardioid=0, this is the same as Blumlein.
            // If _ambiCardioid=1, this is cardioid
            // Of any value between, for hypercardioid patterns.

            // Separate the WXY channels
            ISoundObj channelW = new SingleChannel(source, 0);
            ISoundObj channelX = new SingleChannel(source, 1, true);
            ISoundObj channelY = new SingleChannel(source, 2, true);

            // The _ambiMicAngle says angle between the virtual microphones (degrees)
            // e.g. if this is 100
            //   Left and right are each 50 degrees from forward.
            // These are implemented by mixing appropriate amounts of X and Y;
            //   X * cos(angle/2)
            //   Y * sin(angle/2)
            //
            // For default mic angle of 90 degrees, mulX=mulY=sqrt(2)/2
            //
            double mulX = Math.Cos(MathUtil.Radians(_ambiMicAngle / 2));
            double mulY = Math.Sin(MathUtil.Radians(_ambiMicAngle / 2));

            // Mix back together, adding appropriate amounts of W.
            // The W channel gain is conventionally sqrt(2)/2 relative to X and Y,
            Mixer mixerL = new Mixer();
            mixerL.Add(channelW, _ambiCardioid * MathUtil.INVSQRT2);
            mixerL.Add(channelX, mulX);
            mixerL.Add(channelY, mulY);

            Mixer mixerR = new Mixer();
            mixerR.Add(channelW, _ambiCardioid * MathUtil.INVSQRT2);
            mixerR.Add(channelX, mulX);
            mixerR.Add(channelY, -mulY);

            // output in stereo
            ChannelSplicer stereo = new ChannelSplicer();
            stereo.Add(mixerL);
            stereo.Add(mixerR);
            return stereo;
        }
예제 #4
0
        static ISoundObj DecodeBFormatUHJ(ISoundObj source)
        {
            ISoundObj input = source;
            uint sr = input.SampleRate;
            /*
            if (_ambiUseShelf)
            {
                // Shelf-filters
                // boost W at high frequencies, and boost X, Y at low frequencies
                FilterProfile lfgXY = new FilterProfile();
                lfgXY.Add(new FreqGain(_ambiShelfFreq / 2, 0));
                lfgXY.Add(new FreqGain(_ambiShelfFreq * 2, -1.25));
                FilterImpulse fiXY = new FilterImpulse(0, lfgXY, FilterInterpolation.COSINE, sr);

                FilterProfile lfgW = new FilterProfile();
                lfgW.Add(new FreqGain(_ambiShelfFreq / 2, 0));
                lfgW.Add(new FreqGain(_ambiShelfFreq * 2, 1.76));
                FilterImpulse fiW = new FilterImpulse(0, lfgW, FilterInterpolation.COSINE, sr);
            }
            if (_ambiUseDistance)
            {
                // Distance compensation filters
                // apply phase shift to X, Y at (very) low frequencies
                double fc = MathUtil.FcFromMetres(_ambiDistance);
                IIR1 discomp = new IIR1LP(sr, fc, 8192);    // tbd: chain this
            }
            */

            // Transformation filters
            //
            // Primary reference:
            // Gerzon 1985 "Ambisonics in Multichannel Broadcasting and Video"
            //
            // Coefficients from: http://en.wikipedia.org/wiki/Ambisonic_UHJ_format:
            // S = 0.9396926*W + 0.1855740*X
            // D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y
            // Left = (S + D)/2.0
            // Right = (S - D)/2.0
            // which makes
            // Left = (0.092787 + 0.2549302j)X + (0.4698463 - 0.17101005j)W + (0.3277258)Y
            // Right= (0.092787 - 0.2549302j)X + (0.4698463 + 0.17101005j)W - (0.3277258)Y
            //
            // Coefficients from: http://www.york.ac.uk/inst/mustech/3d_audio/ambis2.htm
            // Left = (0.0928 + 0.255j)X + (0.4699 - 0.171j)W + (0.3277)Y
            // Right= (0.0928 - 0.255j)X + (0.4699 + 0.171j)W - (0.3277)Y

            // The Mid-Side versions are simpler
            // L+R = (0.0928 + 0.255j)X + (0.4699 - 0.171j)W + (0.3277)Y + ((0.0928 - 0.255j)X + (0.4699 + 0.171j)W - (0.3277)Y)
            //     = (0.1856)X          + (0.9398)W
            // L-R = (0.0928 + 0.255j)X + (0.4699 - 0.171j)W + (0.3277)Y - ((0.0928 - 0.255j)X + (0.4699 + 0.171j)W - (0.3277)Y)
            //     =          (0.510j)X +          (0.342j)W + (0.6554)Y
            // but since we're delaying signal via convolution anyway, not *too* much extra processing to do in LR mode...

            // Separate the WXY channels
            ISoundObj channelW = new SingleChannel(input, 0);
            ISoundObj channelX = new SingleChannel(input, 1, true);
            ISoundObj channelY = new SingleChannel(input, 2, true);

            // Z not used; height is discarded in UHJ conversion.
            // Don't assume it's there; horizontal-only .AMB files won't have a fourth channel
            //          ISoundObj channelZ = new SingleChannel(input, 3);

            // Phase shift j is implemented with Hilbert transforms
            // so let's load up some filters, multiply by the appropriate coefficients.
            int len = 8191;
            PhaseMultiplier xl = new PhaseMultiplier(new Complex(0.0927870, 0.25493020), len, sr);
            PhaseMultiplier wl = new PhaseMultiplier(new Complex(0.4698463, -0.17101005), len, sr);
            PhaseMultiplier yl = new PhaseMultiplier(new Complex(0.3277258, 0.00000000), len, sr);
            PhaseMultiplier xr = new PhaseMultiplier(new Complex(0.0927870, -0.25493020), len, sr);
            PhaseMultiplier wr = new PhaseMultiplier(new Complex(0.4698463, 0.17101005), len, sr);
            PhaseMultiplier yr = new PhaseMultiplier(new Complex(-0.3277258, 0.00000000), len, sr);

            // The convolvers to filter
            FastConvolver cwl = new FastConvolver(channelW, wl);
            FastConvolver cxl = new FastConvolver(channelX, xl);
            FastConvolver cyl = new FastConvolver(channelY, yl);
            FastConvolver cwr = new FastConvolver(channelW, wr);
            FastConvolver cxr = new FastConvolver(channelX, xr);
            FastConvolver cyr = new FastConvolver(channelY, yr);

            // Sum to get the final output of these things:
            Mixer mixerL = new Mixer();
            mixerL.Add(cwl, 1.0);
            mixerL.Add(cxl, 1.0);
            mixerL.Add(cyl, 1.0);

            Mixer mixerR = new Mixer();
            mixerR.Add(cwr, 1.0);
            mixerR.Add(cxr, 1.0);
            mixerR.Add(cyr, 1.0);

            // output in stereo
            ChannelSplicer uhj = new ChannelSplicer();
            uhj.Add(mixerL);
            uhj.Add(mixerR);

            return uhj;
        }
예제 #5
0
        static ISoundObj RotateBFormat(ISoundObj source)
        {
            // Rotate a B-Format source
            ISoundObj channelW = new SingleChannel(source, 0);
            ISoundObj channelX = new SingleChannel(source, 1, true);
            ISoundObj channelY = new SingleChannel(source, 2, true);
            ISoundObj channelZ = new SingleChannel(source, 3, true);

            double rx = MathUtil.Radians(_ambiRotateX);
            double ry = MathUtil.Radians(_ambiRotateY);
            double rz = MathUtil.Radians(_ambiRotateZ);

            // Mixer W = new Mixer();
            Mixer X = new Mixer();
            Mixer Y = new Mixer();
            Mixer Z = new Mixer();

            // W is unchanged (omni)
            // W.Add(channelW, 1.0);

            // http://www.muse.demon.co.uk/fmhrotat.html

            // tilt
            // x1 = x * 1       + y * 0       + z * 0
            // y1 = x * 0       + y * Cos(rx) - z * Sin(rx)
            // z1 = x * 0       + y * Sin(rx) + z * Cos(rx)
            // tumble
            // x2 = x * Cos(ry) + y * 0       - z * Sin(ry)
            // y2 = x * 0       + y * 1       + z * 0
            // z2 = x * Sin(ry) + y * 0       + z * Cos(ry)
            // rotate
            // x3 = x * Cos(rz) - y * Sin(rz) + z * 0
            // y3 = x * Sin(rz) + y * Cos(rz) + z * 0
            // z3 = x * 0       + y * 0       + z * 1
            // (read that downwards to get:)

            X.Add(channelX, Math.Cos(ry) * Math.Cos(rz));
            X.Add(channelY, -Math.Sin(rz));
            X.Add(channelZ, -Math.Sin(ry));

            Y.Add(channelX, Math.Sin(rz));
            Y.Add(channelY, Math.Cos(rx) * Math.Cos(rz));
            Y.Add(channelZ, -Math.Sin(rx));

            Z.Add(channelX, Math.Sin(ry));
            Z.Add(channelY, Math.Sin(rz));
            Z.Add(channelZ, Math.Cos(rz) * Math.Cos(ry));

            ChannelSplicer ret = new ChannelSplicer();
            ret.Add(channelW);
            ret.Add(X);
            ret.Add(Y);
            ret.Add(Z);

            return ret;
        }