Модераторы: Partizan, gambit
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> DirectX.DirectSound определение частоты. Как получить частоту из буфера записи? 
:(
    Опции темы
tolan96
Дата 22.12.2009, 18:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 22.12.2009

Репутация: нет
Всего: нет



С наступающим, товарищи. smile 

Я хочу полететь в космос, стать президентом и самая главная цель в жизни - узнать, как определить частоту и, соответственно, ноту полученного звукового сигнала, скажем с микрофона.

Решил создать проект в C# с использованием DirectX.DirectSound. Разобрал пару примеров по DirectSound вложенных в DirectSDK и остановился на примере "CaptureSound". В нем данные из буфера считываются методом Read:
Код

CaptureData = (byte[])applicationBuffer.Read(NextCaptureOffset, typeof(byte), LockFlag.None, LockSize); //Дальше этот массив записывается в файл.

То есть мы получаем массив байт. И вот вопрос: "Как в этом массиве байт определить ноту?".

P.S. Я не ветеран в программировании, так что хотелось бы объяснений поподробнее... smile 
PM MAIL   Вверх
Ivankult
Дата 22.12.2009, 19:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 181
Регистрация: 8.9.2008

Репутация: нет
Всего: нет



С наступающим! Действительно пора новую ветку открывать:про звуко запись-чтение и обработку)) 5 тема за неделю!

Итак. Во первых-определи сколькими байтами кодируется 1 миллисекунда записи. Затем дели всю запись на равные куски каждый из которых равен 1 мСек. Затем создай массив эталона(каждую ноту запиши раза 4-5, все эталоны по длине равны 1 мСек). Потом берешь один кусок записи и сравниваешь с эталонами. Считаешь процент совпадения с эталоном. у какой ноты средний процент выше-та и нота. Поколдуй с допустимым процентом совпадения. Должно работать. Отпишись потом, что получится.
--------------------
Стив Макконнелл: «Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете».
PM MAIL ICQ   Вверх
tolan96
Дата 22.12.2009, 20:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 22.12.2009

Репутация: нет
Всего: нет



Хм...кажется представляю себе, как это должно выглядеть.
Вопрос на счет определения размера одной миллисекунды. Имеем к примеру: 
 smile 
Код

WaveFormat format;                                                           // Создаём звуковой формат
format.SamplesPerSecond = 44100;                                            //...не знаю как это описать...Количество герц на один семпл
format.BitsPerSample = 16;                                                  // Количество бит в каждом самле, в данном случае 2 байта
format.Channels = 2;                                                         // Количество каналов, в данном примере - стерео
format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8)); // Размер куска данных, ровно 4 байта
format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond; // Среднее количество байт, получаемых каждую секунду

Так вот, размер одной миилисекунды это:
Код

int bytesInMillisecond = format.AverageBytesPerSecond / 1000;                  // Получаем 176,4 байта

Правильно я понимаю? Поправьте, если не так...пожалуйста... smile 
PM MAIL   Вверх
tolan96
Дата 22.12.2009, 20:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 22.12.2009

Репутация: нет
Всего: нет



И еще. Идея с созданием семплов и сравнением с ними нашего семпла не подходит...так как должна отображаться полученная частота (как в гитарном тюнере), а не просто указываться, совпала ли нота или нет. К примеру я гитару подношу к микрофону, дергую струну и программа должна отобразить воспроизведенную частоту. Так собственно вопрос с самого начала стоял "Как узнать частоту получаемого звука?".

P.S. Извините, если запутал.
PM MAIL   Вверх
Ivankult
Дата 23.12.2009, 06:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 181
Регистрация: 8.9.2008

Репутация: нет
Всего: нет



Ну, меня вы не запутали. Методом сравнения с семплом можно определить примерную частоту с некоторой точностью(чем больше семплов тем больше точность). А стандартных методов не знаю(
--------------------
Стив Макконнелл: «Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете».
PM MAIL ICQ   Вверх
Heinzz
Дата 23.12.2009, 18:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 381
Регистрация: 12.12.2008
Где: .net

Репутация: 5
Всего: 9



Стандартных методов не знаю.
Мое предложение - выдернуть небольшой кусок сигнала (частота ведь может "плавать"), аппроксимировать его сплайнами, разложить полученную функцию в ряд Фурье => члены ряда будут представлять собой гармоники, член с максимальным коэффициентом = основная частота.
зы я в математике не силен, но мне кажется что этот путь возможен. Главное найти математические библиотеки для этого, если не найдете прикручивайте MatLab/MatCad.


--------------------
user posted image
PM MAIL   Вверх
tolan96
Дата 24.12.2009, 16:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 22.12.2009

Репутация: нет
Всего: нет



Пришла в голову идея: 
В DirectSound у буфера воспроизведения есть такие параметры как:
1)Frequency (частота)
2)Volume    (громкость)
Так вот. Может сработать переброс данных из буфера записи в буфер воспроизведения, и в нём уже смотреть полученную частоту. Но, помнится, у меня была проблема с созданием буфера воспроизведения, все параметры ему ставил, а компилятор ругался : "Значение не попадает в заданный диапозон". Искал решение проблемы, но находил только людей, натыкавшихся на те же грабли, а тех, кто подсказал бы как эти грабли обойти - небыло... smile 
PM MAIL   Вверх
tolan96
Дата 26.12.2009, 05:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 22.12.2009

Репутация: нет
Всего: нет



Нннннет, не то...Эта Frequency то же самое, что и параметр у звукового формата (SamplesPerSecond). Чтож попробую кое что дугое...
PM MAIL   Вверх
tolan96
Дата 9.1.2010, 21:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 22.12.2009

Репутация: нет
Всего: нет



Эх товарищи... smile 

Нашел один пример гитарного тюнера и разобрал его. Но только потом заметил, что результат выполнения программы не всегда точный...  smile 

Здесь оригинальный пример и мой упрощенный.

И вообщем, вот разобранный (и упрощенный) код:


Начало программы (обязательно добовляемые библиотеки: DirectSound, SafeHandles и Threading)
Код

using Microsoft.DirectX.DirectSound;
using Microsoft.Win32.SafeHandles;
using System.Threading;

namespace singer
{
    public partial class Form1 : Form
    {
        private CaptureBuffer buffer = null;
        private Capture capture = null;
        private WaveFormat format;
        public DeviceInformation devInfo;
        private CaptureBufferDescription description;
        private BufferPositionNotify position;
        private BufferPositionNotify[] positions;
        private AutoResetEvent positionEvent;
        private SafeWaitHandle positionEventHandle;
        private Notify notify;
        private ManualResetEvent terminated;
        private Thread thread;
        private WaitHandle[] handles;
        private int bufferSeconds = 3;
        private int notifyPointsInSecond = 10;
        private int waitHandleCount;
        private int bufferLenght;
        
        


        public Form1()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;  // Чтобы компилятор не ругался на доступ к элементам формы, из  потока, в котором они не были созданны
        }



Нажимаем на кнопочку и тем самым создаем новую форму с выбором устройства: 
Код

private void Device_btn_Click(object sender, EventArgs e)
        {
            SelectDevice sf = new SelectDevice(this);  // создаем форму и передаем ей идентификатор родителя
            if (sf.ShowDialog() == DialogResult.OK)
            {
                GetData();
            }
        }



Код формы, где мы выбираем устройство захвата:
Код

using Microsoft.DirectX.DirectSound;

namespace singer
{
    public partial class SelectDevice : Form
    {
        private CaptureDevicesCollection collection;  // объявляем коллекцию устройств
        Form1 f; // объявляем форму (такую же, как и родитель)

        public SelectDevice(Form1 f) // конструктор
        {
            InitializeComponent();
            this.f = f;   // теперь наша форма - это форма родитель (этому я у мужиков из DirectX'а научился...)
        }

        private void SelectDevice_Load(object sender, EventArgs e)
        {
            collection = new CaptureDevicesCollection(); // заполняем коллекцию 
            foreach (DeviceInformation d in collection) //  заполняем элемент формы listBox1 описаниями полученных устройств 
            {
                listBox1.Items.Add(d.Description);
                
            }
            listBox1.SelectedIndex = 0; // сразу же выделяем первый элемент, чтобы пользователь, инстинктивно кликнувший на ОК не получил ошибки
        }

        private void button2_Click(object sender, EventArgs e) // Нажимаем на ОК 
        {
           f.devInfo = collection[listBox1.SelectedIndex];  // получаем то, за чем мы сюда приходили....
        }
    }
}




Создаём буффер захвата и поток, который сразу же и запускаем:
Код

private void GetData()
        {
            
            format = new WaveFormat();
            format.Channels = 1;
            format.BitsPerSample = 16;
            format.SamplesPerSecond = 44100;
            format.FormatTag = WaveFormatTag.Pcm;
            format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8));
            format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond;

            bufferLenght = format.AverageBytesPerSecond * bufferSeconds;
            description = new CaptureBufferDescription();
            description.Format = format;
            description.BufferBytes = bufferLenght;

            capture = new Capture(devInfo.DriverGuid);
            buffer = new CaptureBuffer(description, capture);

            positionEvent = new AutoResetEvent(false);
            positionEventHandle = positionEvent.SafeWaitHandle;
            terminated = new ManualResetEvent(true);

            waitHandleCount = bufferSeconds * notifyPointsInSecond;
            positions = new BufferPositionNotify[waitHandleCount];
            for (int i = 0; i < waitHandleCount; i++)
            {
                position = new BufferPositionNotify();
                position.Offset = (i+1) * bufferLenght / positions.Length - 1; // (x-1)
                position.EventNotifyHandle = positionEventHandle.DangerousGetHandle();
                positions[i] = position;
            }
            notify = new Notify(buffer);
            notify.SetNotificationPositions(positions);

            terminated.Reset();

            thread = new Thread(new ThreadStart(ThreadLoop));
            thread.Name = "Sound Capture";
            thread.Start();
        }



Постоянно получаем данные и отправляем их на обработку
Код

private void ThreadLoop()
        {
            buffer.Start(true); // Запускаем зверь-машину с параметром true (то есть он будет работать циклически)
            try
            {
                int nextCapturePosition = 0;
                handles = new WaitHandle[] {terminated, positionEvent};
                while(WaitHandle.WaitAny(handles) > 0)
                {
                    int capturePosition, readPosition;
                    buffer.GetCurrentPosition(out capturePosition, out readPosition);

                    int lockSize = readPosition - nextCapturePosition;
                    if (lockSize < 0)
                    {
                        lockSize += bufferLenght;
                    }
                    if ((lockSize & 1) != 0)
                    {
                        lockSize--;
                    }
                    int itemsCount = lockSize >> 1;

                    short[] data = (short[])buffer.Read(nextCapturePosition,typeof(short),LockFlag.None, itemsCount); // Считываем ценные данные в наш массив
                    ProcessData(data);  // и посылаем его на обработку
                    nextCapturePosition = (nextCapturePosition + lockSize) % bufferLenght;
                }
            }
            finally
            {
                buffer.Stop();
            }
        }



Копируем short массив в double, получаем из него спектрограмму, а из нее частоту...
Код

        int maxFrequency = 1300;
        int minFrequency = 60;
        int sampleRate = 44100;
        private void ProcessData(short[] data)
        {
            double[] x = new double[data.Length];
            for (int i = 0; i < x.Length; i++)  //  копирование
            {
                x[i] = data[i];
            }
            double[] spectrogramm = Calculate(x); //  получаем спектрограмму
            int index = 0;
            double max = spectrogramm[0];
            int usefullMaxSpectr = Math.Min(spectrogramm.Length, (int)(maxFrequency * spectrogramm.Length/ sampleRate) + 1); //  не знаю за чем автор это сделал, взял бы просто длинну...
            for (int i = 1; i < usefullMaxSpectr; i++ )
            {
                if (max < spectrogramm[i])
                {
                    max = spectrogramm[i];
                    index = i;
                }
            }
            double frequency = (double)sampleRate * index / spectrogramm.Length; // высчитываем частоту
            if (frequency < minFrequency) frequency = 0;
            richTextBox1.Text = frequency.ToString();  // собственно, наглядное отображение искомого
        }




Черная магия  smile 
Код

private double[] Calculate(double[] x)
        {
            int lenght;
            int bitsInLenght;
            if (IsPowerOfTwo(x.Length))     // не понимаю что это (проверка на стерео сигнал что ли?)
            {
                lenght = x.Length;
                bitsInLenght = Log2(lenght) - 1;  // и что мы тут делаем?
            }
            else
            {
                bitsInLenght = Log2(x.Length);  // ничего не понимаю...
                lenght = 1 << bitsInLenght;
            }
            ComplexNumber[] data = new ComplexNumber[lenght];
            for (int i = 0; i < x.Length; i++ )
            {
                int j = ReverseBits( i, bitsInLenght);   // зачем реверс?
                data[j] = new ComplexNumber(x[i]);
            }
            // Cooley-Turkey
            for (int i = 0; i < bitsInLenght; i++ )    // самое сердце программы, и тут я вообще ничего не понимаю...
            {
                int m = 1 << i;
                int n = m * 2;
                double alpha = -(2 * Math.PI / n);

                for (int k = 0; k < m; k++)
                {
                    ComplexNumber oddPartMultiplier = new ComplexNumber(0, alpha * k).PoweredE();
                    for (int j = k; j < lenght; j += n )
                    {
                        ComplexNumber evenPart = data[j];
                        ComplexNumber oddPart = oddPartMultiplier * data[j + m];
                        data[j] = evenPart + oddPart;
                        data[j + m] = evenPart - oddPart;
                    }
                }
            }
            // calculate spectrogramm
            double[] spectrogramma = new double[lenght];
            for (int i = 0; i < spectrogramma.Length; i++)
            {
                spectrogramma[i] = data[i].AbsPower2();
            }
            return spectrogramma;
                
        }
//==========Log2============================
        private int Log2(int n)
        {
            int i = 0;
            while (n > 0)
            {
                ++i;
                n >>= 1;
            }
            return i;
        }
//==========ReverseBits=======================
        private int ReverseBits(int n, int bitsCount)
        {
            int reversed = 0;
            for (int i = 0; i < bitsCount; i++)
            {
                int nextBit = n & 1;
                n >>= 1;
                reversed <<= 1;
                reversed |= nextBit;
            }
            return reversed;
        }
//===========IsPowerOfTwo====================
        private bool IsPowerOfTwo(int n)
        {
            return n > 1 && (n & (n - 1)) == 0;
        }
//=========================================
        struct ComplexNumber 
        {
            public double Re;
            public double Im;
            public ComplexNumber(double re)
            {
                this.Re = re;
                this.Im = 0;
            }
            public ComplexNumber(double re, double im)
            {
                this.Re = re;
                this.Im = im;
            }
            public static ComplexNumber operator *( ComplexNumber n1, ComplexNumber n2)
            {
                return new ComplexNumber(n1.Re * n2.Re - n1.Im * n2.Im, n1.Re * n2.Im + n1.Im * n2.Re);
            }
            public static ComplexNumber operator +( ComplexNumber n1, ComplexNumber n2)
            {
                return new ComplexNumber(n1.Re + n2.Re, n1.Im + n2.Im);
            }
            public static ComplexNumber operator -( ComplexNumber n1, ComplexNumber n2)
            {
                return new ComplexNumber(n1.Re - n2.Re, n1.Im - n2.Im);
            }
            public static ComplexNumber operator -( ComplexNumber n )
            {
                return new ComplexNumber(-n.Re, -n.Im);
            }
            public static implicit operator ComplexNumber(double n)
            {
                return new ComplexNumber(n, 0);
            }
            public double Power2()
            {
                return Re * Re - Im * Im;
            }
            public double AbsPower2()
            {
                return Re * Re + Im * Im;
            }
            public override string ToString()
            {
                return String.Format("{0}+i*{1}", Re, Im);
            }
            public ComplexNumber PoweredE()
            {
                double e = Math.Exp(Re);
                return new ComplexNumber(e * Math.Cos(Im), e * Math.Sin(Im));
            }
        }

PM MAIL   Вверх
tolan96
Дата 12.1.2010, 13:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 27
Регистрация: 22.12.2009

Репутация: нет
Всего: нет



Еще вариант:  smile 

Скачать с сайта mitov.com audio библиотеки и с их помощью создать анализатор частот. Для этого нужно на toolBar прикрепить AudioLab и AudioLabEssential (храняться в папке "bin" установленных библиотек), затем добавить на форму "spectrum" и "dsAudioIn", установив dsAudioIn'у в параметр OutputPin - spectrum. Затем, для получения частоты заходим в Events spectrum'а и создаем обработчик единственного события. И в параметре Args.Frequency храниться наша частота.

Вот мой пример и генератор частот (сделанный, опять таки используя AudioLab  smile ).

П.С. Нужно поковыряться с параметрами, чтобы добиться более-менее нормального результата... smile 
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.0982 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.