示例#1
0
        /// <summary>
        /// 排他モード用。ビットデプスとチャンネル数を変更する。
        /// </summary>
        private byte[] PcmDepthChannelConvert(byte[] from, int fromOffs, int fromBytes,
                                              DeviceFormat fromFormat, DeviceFormat toFormat)
        {
            System.Diagnostics.Debug.Assert(fromFormat.SampleRate == toFormat.SampleRate);

            int numFrames = fromBytes / (fromFormat.NumChannels * WasapiCS.SampleFormatTypeToUseBitsPerSample(fromFormat.SampleFormat) / 8);

            var pcmFrom = new PcmDataLib.PcmData();

            pcmFrom.SetFormat(fromFormat.NumChannels, WasapiCS.SampleFormatTypeToUseBitsPerSample(fromFormat.SampleFormat),
                              WasapiCS.SampleFormatTypeToValidBitsPerSample(fromFormat.SampleFormat),
                              fromFormat.SampleRate, PcmDataLib.PcmData.ValueRepresentationType.SInt, numFrames);
            pcmFrom.SetSampleLargeArray(new WWUtil.LargeArray <byte>(from, fromOffs, fromBytes));

            // ビットデプスを変更する。
            var conv  = new WasapiPcmUtil.PcmFormatConverter(fromFormat.NumChannels);
            var pcmTo = conv.Convert(pcmFrom, toFormat.SampleFormat,
                                     new WasapiPcmUtil.PcmFormatConverter.BitsPerSampleConvArgs(WasapiPcmUtil.NoiseShapingType.None));

            // チャンネル数を変更する。
            var toBytes = WasapiPcmUtil.PcmFormatConverter.ChangeChannelCount(
                fromFormat.SampleFormat, WasapiCS.StreamType.PCM,
                fromFormat.NumChannels, pcmTo.GetSampleLargeArray().ToArray(),
                toFormat.NumChannels);

            return(toBytes);
        }
示例#2
0
        // この関数は音量制限を行わない。呼び出し側で必要に応じて音量を制限する。
        private bool PhaseRotationDo(FirWorkerArgs argsFWA, PcmData pcmDataIn, out PcmData pcmDataOutput)
        {
            PhaseRotationWorkerArgs args = argsFWA as PhaseRotationWorkerArgs;

            var     conv        = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels);
            PcmData pcmDataReal = conv.Convert(pcmDataIn, Wasapi.WasapiCS.SampleFormatType.Sdouble, null);

            pcmDataOutput = new PcmData();
            pcmDataOutput.CopyFrom(pcmDataIn);

            PcmData pcmDataImaginary = new PcmData();

            pcmDataImaginary.CopyFrom(pcmDataReal);

            var dft = new WWDirectComputeCS.WWDftCpu();

            var hilb = WWHilbert.HilbertFirCoeff(args.hilbertFilterType, args.firLength);

            System.Diagnostics.Debug.Assert(hilb.Length == args.firLength);

            // 窓関数
            double [] window;
            if (args.windowFunc == WindowFuncType.Blackman)
            {
                WWWindowFunc.BlackmanWindow(args.firLength, out window);
            }
            else
            {
                WWWindowFunc.KaiserWindow(args.firLength, args.kaiserAlpha, out window);
            }

            // FIR coeffの個数は、window.Length個。
            // ヒルベルト変換パラメータは未来から過去の方向に並んでいるので左右をひっくり返す。
            double [] coeff = new double[args.firLength];
            for (int i = 0; i < coeff.Length; ++i)
            {
                int pos = coeff.Length - i - 1;
                coeff[i] = hilb[pos] * window[i];
            }

            for (int ch = 0; ch < pcmDataImaginary.NumChannels; ++ch)
            {
                var pcm1ch = new double[pcmDataImaginary.NumFrames];
                for (long i = 0; i < pcm1ch.Length; ++i)
                {
                    pcm1ch[i] = pcmDataImaginary.GetSampleValueInDouble(ch, i);
                }

                // 少しずつFIRする。
                var fir = new WWFirCpu();
                fir.Setup(coeff, pcm1ch);

                const int FIR_SAMPLE = 65536;
                for (int offs = 0; offs < pcm1ch.Length; offs += FIR_SAMPLE)
                {
                    int nSample = FIR_SAMPLE;
                    if (pcm1ch.Length < offs + nSample)
                    {
                        nSample = pcm1ch.Length - offs;
                    }

                    var pcmFir = new double[nSample];
                    fir.Do(offs - window.Length / 2, nSample, pcmFir);

                    // 結果を出力に書き込む。
                    for (long i = 0; i < pcmFir.Length; ++i)
                    {
                        var re = pcmFir[i];
                        pcmDataImaginary.SetSampleValueInDouble(ch, i + offs,
                                                                (float)(re));
                    }

                    // 進捗Update。
                    int percentage = (int)(
                        (100L * ch / pcmDataImaginary.NumChannels) +
                        (100L * (offs + 1) / pcm1ch.Length / pcmDataImaginary.NumChannels));
                    mPRWorker.ReportProgress(percentage);
                }
                fir.Unsetup();
            }

            // 音の位相を回転。
            for (int ch = 0; ch < pcmDataReal.NumChannels; ++ch)
            {
                for (long pos = 0; pos < pcmDataReal.NumFrames; ++pos)
                {
                    // 解析信号の各サンプル値を極座標表現に変換。オリジナルの長さと位相を得る。
                    // 長さをそのままに位相を回転し、回転後の実数成分を出力する。

                    double x = pcmDataReal.GetSampleValueInDouble(ch, pos);
                    double y = pcmDataImaginary.GetSampleValueInDouble(ch, pos);

                    double norm  = Math.Sqrt(x * x + y * y);
                    double theta = Math.Atan2(y, x);

                    double re = norm * Math.Cos(theta + args.phaseRadian);
                    pcmDataOutput.SetSampleValueInDouble(ch, pos, re);
                }
            }
            return(true);
        }
        private void m_AnalyticSignalWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            AnalyticSignalWorkerArgs args = e.Argument as AnalyticSignalWorkerArgs;

            Stopwatch sw = new Stopwatch();
            sw.Start();

            // pcmファイルを読み込んでサンプル配列pcm1chを作成。
            PcmData pcmDataIn = null;
            try {
                pcmDataIn = ReadWavFile(args.inputPath);
            } catch (IOException ex) {
                e.Result = string.Format("WAVファイル {0} 読み込み失敗\r\n{1}", args.inputPath, ex);
            }
            if (null == pcmDataIn) {
                e.Result = string.Format("WAVファイル {0} 読み込み失敗", args.inputPath);
            }

            var formatConv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels);
            PcmData pcmDataReal = formatConv.Convert(pcmDataIn, Wasapi.WasapiCS.SampleFormatType.Sdouble, null);

            PcmData pcmDataImaginary = new PcmData();
            pcmDataImaginary.CopyFrom(pcmDataReal);

            var dft = new WWDirectComputeCS.WWDftCpu();

            var hilb = WWHilbert.HilbertFirCoeff(args.hilbertFilterType, args.firLength);
            System.Diagnostics.Debug.Assert(hilb.Length == args.firLength);

            // 窓関数
            double [] window;
            if (args.windowFunc == WindowFuncType.Blackman) {
                WWWindowFunc.BlackmanWindow(args.firLength, out window);
            } else {
                WWWindowFunc.KaiserWindow(args.firLength, args.kaiserAlpha, out window);
            }

            // FIR coeffの個数は、window.Length個。
            // ヒルベルト変換パラメータは未来から過去の方向に並んでいるので左右をひっくり返す。
            double [] coeff = new double[args.firLength];
            for (int i=0; i < coeff.Length; ++i) {
                int pos = coeff.Length - i - 1;
                coeff[i] = hilb[pos] * window[i];
            }

            for (int ch=0; ch < pcmDataImaginary.NumChannels; ++ch) {
                var pcm1ch = new double[pcmDataImaginary.NumFrames];
                for (long i=0; i < pcm1ch.Length; ++i) {
                    pcm1ch[i] = pcmDataImaginary.GetSampleValueInDouble(ch, i);
                }

                // 少しずつFIRする。
                var fir = new WWFirCpu();
                fir.Setup(coeff, pcm1ch);

                const int FIR_SAMPLE = 65536;
                for (int offs=0; offs < pcm1ch.Length; offs += FIR_SAMPLE) {
                    int nSample = FIR_SAMPLE;
                    if (pcm1ch.Length < offs + nSample) {
                        nSample = pcm1ch.Length - offs;
                    }

                    var pcmFir = new double[nSample];
                    fir.Do(offs - window.Length / 2, nSample, pcmFir);

                    // 結果を出力に書き込む。
                    for (long i=0; i < pcmFir.Length; ++i) {
                        var re = pcmFir[i];
                        pcmDataImaginary.SetSampleValueInDouble(ch, i + offs,
                            (float)(re));
                    }

                    // 進捗Update。
                    int percentage = (int)(
                        (100L * ch / pcmDataImaginary.NumChannels) +
                        (100L * (offs + 1) / pcm1ch.Length / pcmDataImaginary.NumChannels));
                    m_ASWorker.ReportProgress(percentage);
                }
                fir.Unsetup();
            }

            // 解析信号を出力。
            m_analyticSignalList.Clear();
            for (int ch=0; ch < pcmDataReal.NumChannels; ++ch) {
                double [] signal = new double[pcmDataImaginary.NumFrames * 2];
                for (long pos=0; pos < pcmDataReal.NumFrames; ++pos) {
                    signal[pos * 2 + 0] = pcmDataReal.GetSampleValueInDouble(ch, pos);
                    signal[pos * 2 + 1] = pcmDataImaginary.GetSampleValueInDouble(ch, pos);
                }
                m_analyticSignalList.Add(signal);
            }

            sw.Stop();
            e.Result = "";
        }
        private void m_USAQworker_DoWork(object sender, DoWorkEventArgs e)
        {
            // System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest;

            USWorkerArgs args = (USWorkerArgs)e.Argument;

            PcmData pcmDataIn = ReadWavFile(args.inputPath);
            if (null == pcmDataIn) {
                e.Result = string.Format("WAVファイル 読み込み失敗: {0}", args.inputPath);
                return;
            }

            // ファイル読み込み完了。
            if (args.addJitter) {
                // ジッター負荷の場合、サンプリング周波数は変更しない。
                args.resampleFrequency = pcmDataIn.SampleRate;
            }

            if (args.resampleFrequency < pcmDataIn.SampleRate) {
                e.Result = string.Format("エラー: ダウンサンプルは対応していません {0} from={1} to={2}",
                    args.inputPath, pcmDataIn.SampleRate, args.resampleFrequency);
                return;
            }
            if (0x7fff0000L < pcmDataIn.NumFrames * 4 * pcmDataIn.NumChannels * args.resampleFrequency / pcmDataIn.SampleRate) {
                e.Result = string.Format("エラー: リサンプル後のファイルサイズが2GBを超えそうなので中断しました {0}",
                    args.inputPath);
                return;
            }

            m_USAQworker.ReportProgress(1);

            var conv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels);
            pcmDataIn = conv.Convert(pcmDataIn, WasapiCS.BitAndFormatToSampleFormatType(32, 32, WasapiCS.BitFormatType.SFloat), null);
            PcmData pcmDataOut = new PcmData();
            pcmDataOut.CopyFrom(pcmDataIn);
            int sampleTotalTo = (int)(args.resampleFrequency * pcmDataIn.NumFrames / pcmDataIn.SampleRate);
            {   // PcmDataOutのサンプルレートとサンプル数を更新する。
                byte[] outSampleArray = new byte[(long)sampleTotalTo * pcmDataOut.NumChannels * 4];
                pcmDataOut.SetSampleArray(sampleTotalTo, outSampleArray);
                pcmDataOut.SampleRate = args.resampleFrequency;
                outSampleArray = null;
            }

            // 再サンプルテーブル作成
            args.resamplePosArray = null;
            args.fractionArray = null;
            if (args.addJitter) {
                // ジッター付加の場合、サンプルレートは変更しない。
                args.resamplePosArray = new int[pcmDataIn.NumFrames];
                args.fractionArray = new double[pcmDataIn.NumFrames];
                /*
                 sampleRate        == 96000 Hz
                 jitterFrequency   == 50 Hz
                 jitterPicoseconds == 1 ps の場合

                 サンプル位置posのθ= 2 * PI * pos * 50 / 96000 (ラジアン)

                 サンプル間隔= 1/96000秒 = 10.4 μs

                 1ms = 10^-3秒
                 1μs= 10^-6秒
                 1ns = 10^-9秒
                 1ps = 10^-12秒

                  1psのずれ                     x サンプルのずれ
                 ───────────── = ─────────
                  10.4 μs(1/96000)sのずれ      1 サンプルのずれ

                 1psのサンプルずれA = 10^-12 ÷ (1/96000) (サンプルのずれ)

                 サンプルを採取する位置= pos + Asin(θ)

                 */

                args.thetaCoefficientSeqJitter = 2.0 * Math.PI * args.sequentialJitterFrequency / pcmDataIn.SampleRate;
                args.ampSeqJitter = 1.0e-12 * pcmDataIn.SampleRate * args.sequentialJitterPicoseconds;
                args.ampTpdfJitter = 1.0e-12 * pcmDataIn.SampleRate * args.tpdfJitterPicoseconds;
                args.ampRpdfJitter = 1.0e-12 * pcmDataIn.SampleRate * args.rpdfJitterPicoseconds;

                PrepareResamplePosArray(
                    args, pcmDataIn.SampleRate, pcmDataOut.SampleRate,
                    (int)pcmDataIn.NumFrames, sampleTotalTo,
                    args.resamplePosArray, args.fractionArray);
            }

            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Start();

            int hr = 0;
            if (args.device == ProcessDevice.Gpu) {
                hr = GpuUpsample(args, pcmDataIn, pcmDataOut);
            } else {
                hr = CpuUpsample(args, pcmDataIn, pcmDataOut);
            }

            // args.resamplePosArrayは中でコピーされるのでここで不要になる。
            args.resamplePosArray = null;
            args.fractionArray = null;

            if (m_USAQworker.CancellationPending) {
                e.Result = string.Format("キャンセル完了。");
                e.Cancel = true;
                return;
            }
            if (hr < 0) {
                e.Result = string.Format("Upsample エラー 0x{0:X8}", hr);
                return;
            }
            sw.Stop();

            // 成功した。レベル制限する。
            float scale = pcmDataOut.LimitLevelOnFloatRange();

            if (args.outputVRT != PcmData.ValueRepresentationType.SFloat) {
                // ビットフォーマット変更。
                var formatConv = new WasapiPcmUtil.PcmFormatConverter(pcmDataOut.NumChannels);
                pcmDataOut = formatConv.Convert(pcmDataOut,
                        WasapiCS.BitAndFormatToSampleFormatType(args.outputBitsPerSample, args.outputBitsPerSample, (WasapiCS.BitFormatType)args.outputVRT), null);
            }

            try {
                WriteWavFile(pcmDataOut, args.outputPath);
            } catch (IOException ex) {
                // 書き込みエラー。
                e.Result = ex.ToString();
                return;
            }

            e.Result = string.Format("書き込み成功。処理時間 {0}秒\r\n",
                sw.ElapsedMilliseconds * 0.001);
            if (scale < 1.0f) {
                e.Result = string.Format("書き込み成功。処理時間 {0}秒。" +
                    "レベルオーバーのため音量調整{1}dB({2}倍)しました。\r\n",
                    sw.ElapsedMilliseconds * 0.001,
                    20.0 * Math.Log10(scale), scale);
            }
            m_USAQworker.ReportProgress(100);
        }
        // この関数は音量制限を行わない。呼び出し側で必要に応じて音量を制限する。
        private bool PhaseRotationDo(FirWorkerArgs argsFWA, PcmData pcmDataIn, out PcmData pcmDataOutput)
        {
            PhaseRotationWorkerArgs args = argsFWA as PhaseRotationWorkerArgs;

            var conv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels);
            PcmData pcmDataReal = conv.Convert(pcmDataIn, Wasapi.WasapiCS.SampleFormatType.Sdouble, null);

            pcmDataOutput = new PcmData();
            pcmDataOutput.CopyFrom(pcmDataIn);

            PcmData pcmDataImaginary = new PcmData();
            pcmDataImaginary.CopyFrom(pcmDataReal);

            var dft = new WWDirectComputeCS.WWDftCpu();

            var hilb = WWHilbert.HilbertFirCoeff(args.hilbertFilterType, args.firLength);
            System.Diagnostics.Debug.Assert(hilb.Length == args.firLength);

            // 窓関数
            double [] window;
            if (args.windowFunc == WindowFuncType.Blackman) {
                WWWindowFunc.BlackmanWindow(args.firLength, out window);
            } else {
                WWWindowFunc.KaiserWindow(args.firLength, args.kaiserAlpha, out window);
            }

            // FIR coeffの個数は、window.Length個。
            // ヒルベルト変換パラメータは未来から過去の方向に並んでいるので左右をひっくり返す。
            double [] coeff = new double[args.firLength];
            for (int i=0; i < coeff.Length; ++i) {
                int pos = coeff.Length - i - 1;
                coeff[i] = hilb[pos] * window[i];
            }

            for (int ch=0; ch < pcmDataImaginary.NumChannels; ++ch) {
                var pcm1ch = new double[pcmDataImaginary.NumFrames];
                for (long i=0; i < pcm1ch.Length; ++i) {
                    pcm1ch[i] = pcmDataImaginary.GetSampleValueInDouble(ch, i);
                }

                // 少しずつFIRする。
                var fir = new WWFirCpu();
                fir.Setup(coeff, pcm1ch);

                const int FIR_SAMPLE = 65536;
                for (int offs=0; offs < pcm1ch.Length; offs += FIR_SAMPLE) {
                    int nSample = FIR_SAMPLE;
                    if (pcm1ch.Length < offs + nSample) {
                        nSample = pcm1ch.Length - offs;
                    }

                    var pcmFir = new double[nSample];
                    fir.Do(offs - window.Length / 2, nSample, pcmFir);

                    // 結果を出力に書き込む。
                    for (long i=0; i < pcmFir.Length; ++i) {
                        var re = pcmFir[i];
                        pcmDataImaginary.SetSampleValueInDouble(ch, i + offs,
                            (float)(re));
                    }

                    // 進捗Update。
                    int percentage = (int)(
                        (100L * ch / pcmDataImaginary.NumChannels) +
                        (100L * (offs + 1) / pcm1ch.Length / pcmDataImaginary.NumChannels));
                    mPRWorker.ReportProgress(percentage);
                }
                fir.Unsetup();

            }

            // 音の位相を回転。
            for (int ch=0; ch < pcmDataReal.NumChannels; ++ch) {
                for (long pos=0; pos < pcmDataReal.NumFrames; ++pos) {

                    // 解析信号の各サンプル値を極座標表現に変換。オリジナルの長さと位相を得る。
                    // 長さをそのままに位相を回転し、回転後の実数成分を出力する。

                    double x = pcmDataReal.GetSampleValueInDouble(ch, pos);
                    double y = pcmDataImaginary.GetSampleValueInDouble(ch, pos);

                    double norm = Math.Sqrt(x * x + y * y);
                    double theta = Math.Atan2(y, x);

                    double re = norm * Math.Cos(theta + args.phaseRadian);
                    pcmDataOutput.SetSampleValueInDouble(ch, pos, re);
                }
            }
            return true;
        }
示例#6
0
        private void m_USAQworker_DoWork(object sender, DoWorkEventArgs e)
        {
            // System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest;

            USWorkerArgs args = (USWorkerArgs)e.Argument;

            PcmData pcmDataIn = ReadWavFile(args.inputPath);

            if (null == pcmDataIn)
            {
                e.Result = string.Format("WAVファイル 読み込み失敗: {0}", args.inputPath);
                return;
            }

            // ファイル読み込み完了。
            if (args.addJitter)
            {
                // ジッター負荷の場合、サンプリング周波数は変更しない。
                args.resampleFrequency = pcmDataIn.SampleRate;
            }

            if (args.resampleFrequency < pcmDataIn.SampleRate)
            {
                e.Result = string.Format("エラー: ダウンサンプルは対応していません {0} from={1} to={2}",
                                         args.inputPath, pcmDataIn.SampleRate, args.resampleFrequency);
                return;
            }
            if (0x7fff0000L < pcmDataIn.NumFrames * 4 * pcmDataIn.NumChannels * args.resampleFrequency / pcmDataIn.SampleRate)
            {
                e.Result = string.Format("エラー: リサンプル後のファイルサイズが2GBを超えそうなので中断しました {0}",
                                         args.inputPath);
                return;
            }

            m_USAQworker.ReportProgress(1);

            var conv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels);

            pcmDataIn = conv.Convert(pcmDataIn, WasapiCS.BitAndFormatToSampleFormatType(32, 32, WasapiCS.BitFormatType.SFloat), null);
            PcmData pcmDataOut = new PcmData();

            pcmDataOut.CopyFrom(pcmDataIn);
            int sampleTotalTo = (int)(args.resampleFrequency * pcmDataIn.NumFrames / pcmDataIn.SampleRate);

            {   // PcmDataOutのサンプルレートとサンプル数を更新する。
                byte[] outSampleArray = new byte[(long)sampleTotalTo * pcmDataOut.NumChannels * 4];
                pcmDataOut.SetSampleArray(sampleTotalTo, outSampleArray);
                pcmDataOut.SampleRate = args.resampleFrequency;
                outSampleArray        = null;
            }

            // 再サンプルテーブル作成
            args.resamplePosArray = null;
            args.fractionArray    = null;
            if (args.addJitter)
            {
                // ジッター付加の場合、サンプルレートは変更しない。
                args.resamplePosArray = new int[pcmDataIn.NumFrames];
                args.fractionArray    = new double[pcmDataIn.NumFrames];

                /*
                 * sampleRate        == 96000 Hz
                 * jitterFrequency   == 50 Hz
                 * jitterPicoseconds == 1 ps の場合
                 *
                 * サンプル位置posのθ= 2 * PI * pos * 50 / 96000 (ラジアン)
                 *
                 * サンプル間隔= 1/96000秒 = 10.4 μs
                 *
                 * 1ms = 10^-3秒
                 * 1μs= 10^-6秒
                 * 1ns = 10^-9秒
                 * 1ps = 10^-12秒
                 *
                 * 1psのずれ                     x サンプルのずれ
                 * ───────────── = ─────────
                 * 10.4 μs(1/96000)sのずれ      1 サンプルのずれ
                 *
                 * 1psのサンプルずれA = 10^-12 ÷ (1/96000) (サンプルのずれ)
                 *
                 * サンプルを採取する位置= pos + Asin(θ)
                 *
                 */

                args.thetaCoefficientSeqJitter = 2.0 * Math.PI * args.sequentialJitterFrequency / pcmDataIn.SampleRate;
                args.ampSeqJitter  = 1.0e-12 * pcmDataIn.SampleRate * args.sequentialJitterPicoseconds;
                args.ampTpdfJitter = 1.0e-12 * pcmDataIn.SampleRate * args.tpdfJitterPicoseconds;
                args.ampRpdfJitter = 1.0e-12 * pcmDataIn.SampleRate * args.rpdfJitterPicoseconds;

                PrepareResamplePosArray(
                    args, pcmDataIn.SampleRate, pcmDataOut.SampleRate,
                    (int)pcmDataIn.NumFrames, sampleTotalTo,
                    args.resamplePosArray, args.fractionArray);
            }

            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Start();

            int hr = 0;

            if (args.device == ProcessDevice.Gpu)
            {
                hr = GpuUpsample(args, pcmDataIn, pcmDataOut);
            }
            else
            {
                hr = CpuUpsample(args, pcmDataIn, pcmDataOut);
            }

            // args.resamplePosArrayは中でコピーされるのでここで不要になる。
            args.resamplePosArray = null;
            args.fractionArray    = null;

            if (m_USAQworker.CancellationPending)
            {
                e.Result = string.Format("キャンセル完了。");
                e.Cancel = true;
                return;
            }
            if (hr < 0)
            {
                e.Result = string.Format("Upsample エラー 0x{0:X8}", hr);
                return;
            }
            sw.Stop();

            // 成功した。レベル制限する。
            float scale = pcmDataOut.LimitLevelOnFloatRange();

            if (args.outputVRT != PcmData.ValueRepresentationType.SFloat)
            {
                // ビットフォーマット変更。
                var formatConv = new WasapiPcmUtil.PcmFormatConverter(pcmDataOut.NumChannels);
                pcmDataOut = formatConv.Convert(pcmDataOut,
                                                WasapiCS.BitAndFormatToSampleFormatType(args.outputBitsPerSample, args.outputBitsPerSample, (WasapiCS.BitFormatType)args.outputVRT), null);
            }

            try {
                WriteWavFile(pcmDataOut, args.outputPath);
            } catch (IOException ex) {
                // 書き込みエラー。
                e.Result = ex.ToString();
                return;
            }

            e.Result = string.Format("書き込み成功。処理時間 {0}秒\r\n",
                                     sw.ElapsedMilliseconds * 0.001);
            if (scale < 1.0f)
            {
                e.Result = string.Format("書き込み成功。処理時間 {0}秒。" +
                                         "レベルオーバーのため音量調整{1}dB({2}倍)しました。\r\n",
                                         sw.ElapsedMilliseconds * 0.001,
                                         20.0 * Math.Log10(scale), scale);
            }
            m_USAQworker.ReportProgress(100);
        }
示例#7
0
        private void PreparePcmData()
        {
            // mPcmSync : 長さ2秒、同期をするために頭に1回小さいクリック音を入れる。これを連続再生する。
            // mPcmReady : 長さ2秒、頭に小さいクリック音がある。このPCMの直後にテストデータを再生する。

            const int SYNC_PCM_SECONDS = 1;

            {
                mPcmSync = new PcmDataLib.PcmData();
                mPcmSync.SetFormat(NUM_CHANNELS,
                                   WasapiCS.SampleFormatTypeToUseBitsPerSample(mPlaySampleFormat),
                                   WasapiCS.SampleFormatTypeToValidBitsPerSample(mPlaySampleFormat),
                                   mSampleRate,
                                   PcmDataLib.PcmData.ValueRepresentationType.SInt, SYNC_PCM_SECONDS * mSampleRate);
                var syncData = new LargeArray <byte>((WasapiCS.SampleFormatTypeToUseBitsPerSample(mPlaySampleFormat) / 8) * NUM_CHANNELS * mPcmSync.NumFrames);
                mPcmSync.SetSampleLargeArray(syncData);
            }

            {
                mPcmReady = new PcmDataLib.PcmData();
                mPcmReady.CopyFrom(mPcmSync);
                var readyData = new LargeArray <byte>((WasapiCS.SampleFormatTypeToUseBitsPerSample(mPlaySampleFormat) / 8) * NUM_CHANNELS * mPcmSync.NumFrames);
                mPcmReady.SetSampleLargeArray(readyData);
            }

            switch (mPlaySampleFormat)
            {
            case WasapiCS.SampleFormatType.Sint16:
                mPcmSync.SetSampleValueInInt32(0, 0, 0x00040000);
                mPcmReady.SetSampleValueInInt32(0, 0, 0x00030000);
                break;

            case WasapiCS.SampleFormatType.Sint24:
            case WasapiCS.SampleFormatType.Sint32V24:
                mPcmSync.SetSampleValueInInt32(0, 0, 0x00000400);
                mPcmReady.SetSampleValueInInt32(0, 0, 0x00000300);
                break;

            default:
                System.Diagnostics.Debug.Assert(false);
                break;
            }

            // mPcmTest : テストデータ。このPCMデータを再生し、再生データと録音データが一致するかどうかを調べる。
            if (mUseFile)
            {
                var conv = new WasapiPcmUtil.PcmFormatConverter(NUM_CHANNELS);
                mPcmTest = conv.Convert(mPlayPcmData, mPlaySampleFormat,
                                        new WasapiPcmUtil.PcmFormatConverter.BitsPerSampleConvArgs(WasapiPcmUtil.NoiseShapingType.None));
                mNumTestFrames = mPcmTest.NumFrames;
            }
            else
            {
                mPcmTest = new PcmDataLib.PcmData();
                mPcmTest.CopyHeaderInfoFrom(mPcmSync);
                var randData = new LargeArray <byte>((long)(WasapiCS.SampleFormatTypeToUseBitsPerSample(mPlaySampleFormat) / 8) * NUM_CHANNELS * mNumTestFrames);
                var fragment = new byte[4096];

                for (long i = 0; i < randData.LongLength; i += fragment.Length)
                {
                    long count = fragment.Length;
                    if (randData.LongLength < i + count)
                    {
                        count = randData.LongLength - i;
                    }

                    mRand.NextBytes(fragment);
                    randData.CopyFrom(fragment, 0, i, (int)count);
                }
                mPcmTest.SetSampleLargeArray(mNumTestFrames, randData);
            }

            // 録音データ置き場。
            mCapturedPcmData = new LargeArray <byte>((long)(WasapiCS.SampleFormatTypeToUseBitsPerSample(mRecSampleFormat) / 8)
                                                     * NUM_CHANNELS * (mNumTestFrames + (4 * SYNC_PCM_SECONDS) * mSampleRate));
        }
示例#8
0
        private bool FirDoLoadConvertSave(FirWorkerArgs args, FirDelegate Do, out string result)
        {
            // pcmファイルを読み込んでサンプル配列pcm1chを作成。
            PcmData pcmDataIn = null;

            try {
                pcmDataIn = ReadWavFile(args.inputPath);
            } catch (IOException ex) {
                result = string.Format("WAVファイル {0} 読み込み失敗\r\n{1}", args.inputPath, ex);
                return(false);
            }
            if (null == pcmDataIn)
            {
                result = string.Format("WAVファイル {0} 読み込み失敗", args.inputPath);
                return(false);
            }

            var formatConv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels);

            pcmDataIn = formatConv.Convert(pcmDataIn, Wasapi.WasapiCS.SampleFormatType.Sdouble, null);

            PcmData pcmDataOutput;

            if (!Do(args, pcmDataIn, out pcmDataOutput))
            {
                result = "FIR処理失敗";
                return(false);
            }

            // サンプルフォーマットが整数型の時だけ音量制限処理する。
            double scale    = 1.0;
            double maxLevel = 0.0f;

            if (args.valueRepresentationType == PcmData.ValueRepresentationType.SInt)
            {
                scale = pcmDataOutput.LimitLevelOnDoubleRange();
            }
            else
            {
                double maxV, minV;
                pcmDataOutput.FindMaxMinValueOnDoubleBuffer(out maxV, out minV);
                maxLevel = Math.Max(Math.Abs(maxV), Math.Abs(minV));
            }

            PcmData pcmDataWrite = formatConv.Convert(pcmDataOutput,
                                                      Wasapi.WasapiCS.BitAndFormatToSampleFormatType(args.outputBitsPerSample, args.outputBitsPerSample, (Wasapi.WasapiCS.BitFormatType)args.valueRepresentationType), null);

            bool writeResult = false;

            try {
                writeResult = WriteWavFile(pcmDataWrite, args.outputPath);
            } catch (IOException ex) {
                result = string.Format("WAVファイル書き込み失敗: {0}\r\n{1}", args.outputPath, ex);
                return(false);
            }
            if (!writeResult)
            {
                result = string.Format("WAVファイル書き込み失敗: {0}", args.outputPath);
                return(false);
            }

            if (args.valueRepresentationType == PcmData.ValueRepresentationType.SInt)
            {
                if (scale == 1.0)
                {
                    result = string.Format("WAVファイル書き込み成功: {0}", args.outputPath);
                }
                else
                {
                    result = string.Format("WAVファイル書き込み成功: {0}\r\nレベルオーバーのため音量を{1:0.######}倍しました({2:0.######}dB)",
                                           args.outputPath, scale, 20.0 * Math.Log10(scale));
                }
            }
            else
            {
                result = string.Format("WAVファイル書き込み成功: {0}\r\nサンプル値の絶対値の最大値 {1:0.######}",
                                       args.outputPath, maxLevel);
            }
            return(true);
        }
        private void m_AnalyticSignalWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            AnalyticSignalWorkerArgs args = e.Argument as AnalyticSignalWorkerArgs;

            Stopwatch sw = new Stopwatch();

            sw.Start();

            // pcmファイルを読み込んでサンプル配列pcm1chを作成。
            PcmData pcmDataIn = null;

            try {
                pcmDataIn = ReadWavFile(args.inputPath);
            } catch (IOException ex) {
                e.Result = string.Format("WAVファイル {0} 読み込み失敗\r\n{1}", args.inputPath, ex);
            }
            if (null == pcmDataIn)
            {
                e.Result = string.Format("WAVファイル {0} 読み込み失敗", args.inputPath);
            }

            var     formatConv  = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels);
            PcmData pcmDataReal = formatConv.Convert(pcmDataIn, Wasapi.WasapiCS.SampleFormatType.Sdouble, null);

            PcmData pcmDataImaginary = new PcmData();

            pcmDataImaginary.CopyFrom(pcmDataReal);

            var dft = new WWDirectComputeCS.WWDftCpu();

            var hilb = WWHilbert.HilbertFirCoeff(args.hilbertFilterType, args.firLength);

            System.Diagnostics.Debug.Assert(hilb.Length == args.firLength);

            // 窓関数
            double [] window;
            if (args.windowFunc == WindowFuncType.Blackman)
            {
                WWWindowFunc.BlackmanWindow(args.firLength, out window);
            }
            else
            {
                WWWindowFunc.KaiserWindow(args.firLength, args.kaiserAlpha, out window);
            }

            // FIR coeffの個数は、window.Length個。
            // ヒルベルト変換パラメータは未来から過去の方向に並んでいるので左右をひっくり返す。
            double [] coeff = new double[args.firLength];
            for (int i = 0; i < coeff.Length; ++i)
            {
                int pos = coeff.Length - i - 1;
                coeff[i] = hilb[pos] * window[i];
            }

            for (int ch = 0; ch < pcmDataImaginary.NumChannels; ++ch)
            {
                var pcm1ch = new double[pcmDataImaginary.NumFrames];
                for (long i = 0; i < pcm1ch.Length; ++i)
                {
                    pcm1ch[i] = pcmDataImaginary.GetSampleValueInDouble(ch, i);
                }

                // 少しずつFIRする。
                var fir = new WWFirCpu();
                fir.Setup(coeff, pcm1ch);

                const int FIR_SAMPLE = 65536;
                for (int offs = 0; offs < pcm1ch.Length; offs += FIR_SAMPLE)
                {
                    int nSample = FIR_SAMPLE;
                    if (pcm1ch.Length < offs + nSample)
                    {
                        nSample = pcm1ch.Length - offs;
                    }

                    var pcmFir = new double[nSample];
                    fir.Do(offs - window.Length / 2, nSample, pcmFir);

                    // 結果を出力に書き込む。
                    for (long i = 0; i < pcmFir.Length; ++i)
                    {
                        var re = pcmFir[i];
                        pcmDataImaginary.SetSampleValueInDouble(ch, i + offs,
                                                                (float)(re));
                    }

                    // 進捗Update。
                    int percentage = (int)(
                        (100L * ch / pcmDataImaginary.NumChannels) +
                        (100L * (offs + 1) / pcm1ch.Length / pcmDataImaginary.NumChannels));
                    m_ASWorker.ReportProgress(percentage);
                }
                fir.Unsetup();
            }

            // 解析信号を出力。
            m_analyticSignalList.Clear();
            for (int ch = 0; ch < pcmDataReal.NumChannels; ++ch)
            {
                double [] signal = new double[pcmDataImaginary.NumFrames * 2];
                for (long pos = 0; pos < pcmDataReal.NumFrames; ++pos)
                {
                    signal[pos * 2 + 0] = pcmDataReal.GetSampleValueInDouble(ch, pos);
                    signal[pos * 2 + 1] = pcmDataImaginary.GetSampleValueInDouble(ch, pos);
                }
                m_analyticSignalList.Add(signal);
            }

            sw.Stop();
            e.Result = "";
        }
示例#10
0
        private bool FirDoLoadConvertSave(FirWorkerArgs args, FirDelegate Do, out string result)
        {
            // pcmファイルを読み込んでサンプル配列pcm1chを作成。
            PcmData pcmDataIn = null;
            try {
                pcmDataIn = ReadWavFile(args.inputPath);
            } catch (IOException ex) {
                result = string.Format("WAVファイル {0} 読み込み失敗\r\n{1}", args.inputPath, ex);
                return false;
            }
            if (null == pcmDataIn) {
                result = string.Format("WAVファイル {0} 読み込み失敗", args.inputPath);
                return false;
            }

            var formatConv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels);
            pcmDataIn = formatConv.Convert(pcmDataIn, Wasapi.WasapiCS.SampleFormatType.Sdouble, null);

            PcmData pcmDataOutput;

            if (!Do(args, pcmDataIn, out pcmDataOutput)) {
                result = "FIR処理失敗";
                return false;
            }

            // サンプルフォーマットが整数型の時だけ音量制限処理する。
            double scale = 1.0;
            double maxLevel = 0.0f;
            if (args.valueRepresentationType == PcmData.ValueRepresentationType.SInt) {
                scale = pcmDataOutput.LimitLevelOnDoubleRange();
            } else {
                double maxV, minV;
                pcmDataOutput.FindMaxMinValueOnDoubleBuffer(out maxV, out minV);
                maxLevel = Math.Max(Math.Abs(maxV), Math.Abs(minV));
            }

            PcmData pcmDataWrite = formatConv.Convert(pcmDataOutput,
                    Wasapi.WasapiCS.BitAndFormatToSampleFormatType(args.outputBitsPerSample, args.outputBitsPerSample, (Wasapi.WasapiCS.BitFormatType)args.valueRepresentationType), null);

            bool writeResult = false;
            try {
                writeResult = WriteWavFile(pcmDataWrite, args.outputPath);
            } catch (IOException ex) {
                result = string.Format("WAVファイル書き込み失敗: {0}\r\n{1}", args.outputPath, ex);
                return false;
            }
            if (!writeResult) {
                result = string.Format("WAVファイル書き込み失敗: {0}", args.outputPath);
                return false;
            }

            if (args.valueRepresentationType == PcmData.ValueRepresentationType.SInt) {
                if (scale == 1.0) {
                    result = string.Format("WAVファイル書き込み成功: {0}", args.outputPath);
                } else {
                    result = string.Format("WAVファイル書き込み成功: {0}\r\nレベルオーバーのため音量を{1:0.######}倍しました({2:0.######}dB)",
                            args.outputPath, scale, 20.0 * Math.Log10(scale));
                }
            } else {
                result = string.Format("WAVファイル書き込み成功: {0}\r\nサンプル値の絶対値の最大値 {1:0.######}",
                        args.outputPath, maxLevel);
            }
            return true;
        }