Exemplo n.º 1
0
        private string WriteLRC(List <int> predKeys, PitchEnum pitchEnum, string outputPath)
        {
            // key推定値が一つもないときエラー。
            int keyCount = 0;

            foreach (var k in predKeys)
            {
                if (0 <= k)
                {
                    ++keyCount;
                }
            }
            if (keyCount == 0)
            {
                return("Error: KeyClassifier failed to predict any key on this audio file!\n");
            }

            int lastKey = -1;

            using (var sw = new StreamWriter(outputPath)) {
                for (int i = 0; i < predKeys.Count(); ++i)
                {
                    int key = predKeys[i];

                    if (pitchEnum == PitchEnum.BaroquePitch)
                    {
                        // コンサートピッチの評価値をバロックピッチに変換する。
                        key = mKeyClassifier.KeyIdxToBaroquePitch(key);
                    }

                    if (key == lastKey)
                    {
                        continue;
                    }

                    // 値が変化したので書き込む。

                    if (key < 0)
                    {
                        double timeSec = (double)i * WINDOW_LENGTH / 2 / SAMPLE_RATE;
                        sw.WriteLine("[{0}] -", SecondToTimeStr(timeSec));
                    }
                    else
                    {
                        // 同じkeyがKEY_COUNTER個連続したら確定するため
                        // KEY_COUNTER-1個遅延して出るので、その分時間を過去にする。
                        double timeSec = (double)(i - (KEY_COUNTER - 1)) * WINDOW_LENGTH / 2 / SAMPLE_RATE;
                        sw.WriteLine("[{0}]{1}", SecondToTimeStr(timeSec), mKeyClassifier.KeyIdxToStr(key));
                    }
                    lastKey = key;
                }
            }

            // 成功。
            return("");
        }
Exemplo n.º 2
0
        /// <summary>
        /// 調を調べてLRCファイルを出力する。
        /// </summary>
        /// <returns>エラーの文字列。成功のとき空文字列。</returns>
        public string Classify(string inputAudioPath, string outputLrcPath, PitchEnum pitchEnum, BackgroundWorker bw)
        {
            WWMFReaderCs.WWMFReader.Metadata meta;
            LargeArray <byte> dataByteAry;

            int hr = WWMFReaderCs.WWMFReader.ReadHeaderAndData(inputAudioPath, out meta, out dataByteAry);

            if (hr < 0)
            {
                return(string.Format("Error: Read failed {0} {1}\n", hr, inputAudioPath));
            }

            if (meta.sampleRate != SAMPLE_RATE || meta.bitsPerSample != 16 || meta.numChannels != 2)
            {
                return(string.Format("Error: File format is not {0}Hz 16bit 2ch. ({1}Hz {2}bit {3}ch) {4}\n", SAMPLE_RATE, meta.sampleRate, meta.bitsPerSample, meta.numChannels, inputAudioPath));
            }

            if (bw != null)
            {
                bw.ReportProgress(0, string.Format("Read {0}\nProcessing...\n", inputAudioPath));
            }
            mReportSW.Start();

            // ステレオをモノラル float値にする。
            var dataF = StereoToMono(dataByteAry);

            // 鍵盤の周波数binn番号。
            var    fIdx = new List <int>();
            double f    = 110.0; // 110Hz, A2

            while (f < 1320.0)
            {
                fIdx.Add((int)(f * (((double)WINDOW_LENGTH / 2) / (SAMPLE_RATE / 2))));
                f *= Math.Pow(2, 1.0 / 12);
            }

            // Hann窓 w。
            double[] wD = WWWindowFunc.HannWindow(WINDOW_LENGTH);
            float[]  w  = Array.ConvertAll(wD, xD => (float)xD);

            var fft = new WWRadix2FftF(WINDOW_LENGTH);

            mKeyHistory = new List <int>();

            // Keyの推定値predKeysを作成する。-1のとき不明。
            var predKeys = new List <int>();

            for (long i = 0; i < dataF.LongLength - (WINDOW_LENGTH * TEMPORAL_WIDTH); i += WINDOW_LENGTH / 2)
            {
                var x = new List <float>();

                // TEMPORAL_WIDTH (=8)個のFFTを実行する。
                for (int j = 0; j < TEMPORAL_WIDTH; ++j)
                {
                    // 窓関数を掛けてFFTし、大きさを取って実数配列にし、鍵盤の周波数binnの値をxに追加。
                    var v  = PrepareSampleDataForFFT(dataF, i + j * WINDOW_LENGTH, w);
                    var vC = WWComplexF.FromRealArray(v);
                    var fC = fft.ForwardFft(vC).ToArray();
                    var fR = WWComplexF.ToMagnitudeRealArray(fC);
                    foreach (var k in fIdx)
                    {
                        x.Add(fR[k]);
                    }
                }

                // keyを推定する。
                var keyP = mKeyClassifier.Classify(x.ToArray());

                // keyCounter個同じkeyが連続したら確定する。
                var key = FilterKey(keyP);
                predKeys.Add(key);
                //Console.WriteLine("{0}, {1} {2}", (double)i / SAMPLE_RATE, mKeyClassifier.KeyIdxToStr(keyP), key);

                if (1000 < mReportSW.ElapsedMilliseconds)
                {
                    int percentage = (int)(100 * i / dataF.LongLength);
                    if (bw == null)
                    {
                        Console.Write("{0}% \r", percentage);
                    }
                    else
                    {
                        bw.ReportProgress(percentage, "");
                    }
                    mReportSW.Restart();
                }
            }

            if (bw == null)
            {
                Console.WriteLine("     \r");
            }

            mReportSW.Stop();

            {
                var r = WriteLRC(predKeys, pitchEnum, outputLrcPath);
                return(r);
            }
        }