Используя Audio Graph, изучите шум окружающей среды и отфильтруйте в приложении UWP - PullRequest
0 голосов
/ 05 февраля 2019

Это расширенный вопрос отсюда Использование UWP монитора живого звука и обнаружения звука стрельбы из пистолета / хлопка

Благодаря Дернис Я наконец-то получил работающий коддля мониторинга живого звука и запуска событий, когда количество децибел превышает определенный диапазон.

Это прекрасно работает, когда мы запускаем его в офисе / закрытой / тихой области.

Но когда я беру приложение вна открытой дороге будут слышны сигналы уличного движения, шум ветра, люди говорят о звуках и другие шумы, а события УДАР неправильно идентифицированы.

  • Я бы хотел реализовать что-то вроде Lean Environment кнопка.Прежде чем приложение начнет мониторинг, пользователь нажимает « Lean Environment », который распознает уровни чувствительности и устанавливает фильтрацию для моего живого звука, а затем я запускаю мониторинг ударов.
  • Если он не добавляетслишком большая нагрузка, я хотел бы записать звук в файл.

Любая помощь по началу работы будет принята.

OnNavigatedTo

protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
           //other logic
            await CreateInputDeviceNodeAsync(_deviceId);
        }

CreateInputDeviceNodeAsync

public async Task<bool> CreateInputDeviceNodeAsync(string deviceId)
        {
            Console.WriteLine("Creating AudioGraphs");
            // Create an AudioGraph with default settings
            AudioGraphSettings graphSettings = new AudioGraphSettings(AudioRenderCategory.Media)
            {
                EncodingProperties = new AudioEncodingProperties
                {
                    Subtype = "Float",
                    SampleRate = 48000,
                    ChannelCount = 2,
                    BitsPerSample = 32,
                    Bitrate = 3072000
                }
            };

            CreateAudioGraphResult audioGraphResult = await AudioGraph.CreateAsync(graphSettings);

            if (audioGraphResult.Status != AudioGraphCreationStatus.Success)
            {
                _rootPage.NotifyUser("Cannot create graph", NotifyType.ErrorMessage);
                return false;
            }

            _audioGraph = audioGraphResult.Graph;
            AudioGraphSettings audioGraphSettings =
                new AudioGraphSettings(AudioRenderCategory.GameChat)
                {
                    EncodingProperties = AudioEncodingProperties.CreatePcm(48000, 2, 32),
                    DesiredSamplesPerQuantum = 990,
                    QuantumSizeSelectionMode = QuantumSizeSelectionMode.ClosestToDesired
                };
            _frameOutputNode = _audioGraph.CreateFrameOutputNode(_audioGraph.EncodingProperties);
            _quantum = 0;
            _audioGraph.QuantumStarted += Graph_QuantumStarted;
            LoudNoise += BlowDetected;

            DeviceInformation selectedDevice = null;

            if (!string.IsNullOrWhiteSpace(_deviceId))
                selectedDevice = await DeviceInformation.CreateFromIdAsync(_deviceId);
            if (selectedDevice == null)
            {
                string device = Windows.Media.Devices.MediaDevice.GetDefaultAudioCaptureId(
                    Windows.Media.Devices.AudioDeviceRole.Default);
                if (!string.IsNullOrWhiteSpace(device))
                    selectedDevice = await DeviceInformation.CreateFromIdAsync(device);
                else
                {
                    _rootPage.NotifyUser($"Could not select Audio Device {device}", NotifyType.ErrorMessage);
                    return false;
                }
            }
            CreateAudioDeviceInputNodeResult result =
               await _audioGraph.CreateDeviceInputNodeAsync(MediaCategory.Media, audioGraphSettings.EncodingProperties,
                   selectedDevice);
            if (result.Status != AudioDeviceNodeCreationStatus.Success)
            {
                _rootPage.NotifyUser("Cannot create device output node", NotifyType.ErrorMessage);
                return false;
            }

            _selectedMicrophone = selectedDevice.Name;

            _deviceInputNode = result.DeviceInputNode;
            _deviceInputNode.AddOutgoingConnection(_frameOutputNode);
            _frameOutputNode.Start();
            _audioGraph.Start();
            return true;
        }

Graph_QuantumStarted

private void Graph_QuantumStarted(AudioGraph sender, object args)
        {
            if (++_quantum % 2 != 0) return;
            AudioFrame frame = _frameOutputNode.GetFrame();
            float[] dataInFloats;
            using (AudioBuffer buffer = frame.LockBuffer(AudioBufferAccessMode.Write))
            using (IMemoryBufferReference reference = buffer.CreateReference())
                unsafe
                {
                    // Get the buffer from the AudioFrame
                    // ReSharper disable once SuspiciousTypeConversion.Global
                    ((IMemoryBufferByteAccess) reference).GetBuffer(out byte* dataInBytes,
                        out var capacityInBytes);

                    var dataInFloat = (float*) dataInBytes;
                    dataInFloats = new float[capacityInBytes / sizeof(float)];

                    for (var i = 0; i < capacityInBytes / sizeof(float); i++)
                    {
                        dataInFloats[i] = dataInFloat[i];
                    }
                }

            double decibels = dataInFloats.Aggregate<float, double>(0f, (current, sample) => current + Math.Abs(sample));

            decibels = 20 * Math.Log10(decibels / dataInFloats.Length);
            _decibelList.Add(decibels);

            if (double.IsInfinity(decibels) || decibels < _threshold) return;//-45
            if (_watch != null && _watch.Elapsed <= TimeSpan.FromSeconds(1)) return;
            LoudNoise?.Invoke(this, decibels);
            _watch = Stopwatch.StartNew();
        }

1 Ответ

0 голосов
/ 07 февраля 2019

Это всего лишь статистика.Возможно, вы захотите собрать, по крайней мере, 50 кадров (1 секунда) данных перед тем, как они действительно будут работать (возможно, пусть пользователь решит, удерживая и отпуская кнопку).Тогда вы, вероятно, захотите определить, где обычно находится уровень децибел.Я могу придумать 3 способа сделать это.

private void Graph_QuantumStarted(AudioGraph sender, object args)
        {
            ...
            double decibels = dataInFloats.Aggregate<float, double>(0f, (current, sample) => current + Math.Abs(sample)); // I dislike the fact that the decibels variable is initially inaccurate, but it's your codebase.
            decibels = 20 * Math.Log10(decibels / dataInFloats.Length);


            if (scanning) // class variable (bool), you can set it from the UI thread like this
            {
                _decibelList.Add(decibels); // I assume you made this a class variable
            }
            else if (decibels == Double.NaN)
            {
                // Code by case below
            }
            else if (decibels > _sensitivity) //_sensitivity is a class variable(double), initialized to Double.NaN
            {
                LoudNoise?.Invoke(this, true); // Calling events is a wee bit expensive, you probably want to handle the sensitivity before Invoking it, I'm also going to do it like that to make this demo simpler
            }
        }
  1. Если вы можете контролировать, убедитесь, что нет достаточно громкого всплеска, и вы хотите, чтобы он сработал, вы можете просто взять максимальное значение всех этих кадров и сказать, что оно превышает чувствительность maxDecibels + Math.Abs(maxDecibels* 0.2) (децибелы могут быть отрицательными, следовательно, Abs).
double maxDecibels = _decibelList.OrderByDescending(x => x)[0];
_sensitivity = maxDecibels + Math.Abs(maxDecibels* 0.2);
Если вы не можете контролировать время всплеска, вы можете собрать эти кадры, отсортировать их и получить их [24] (из вашего списка 100 предметов) и сказать, что это чувствительность.
sensitivity = _decibelList.OrderByDescending(x => x)[24]; // If you do a variable time you can just take Count/4 - 1 as the index
(я думаю, что это лучше, но я действительно не знаю статистики) Пройдите по списку децибел фрейма и отследите среднюю разницу в значении и индекс того, что изменило его больше всего.После этого найдите максимальное значение после этого индекса и скажите, что 75% изменений - это чувствительность.(Не используйте LinkedList для этого)
int greatestChange, changeIndex = 0;
double p = Double.NaN; // Previous
for (int i = 0; i < _decibelList.Count(); i++)
{
    if (p != Double.Nan)
    {
        change = Math.Abs(_decibelList[i] - p);
        if (Math.Abs(change > greatestChange)
        {
            greatestChange = change;
            changeIndex = i;
        } 
    }
    p = _decibelList[i];
}

int i = changeIndex;
p = Double.NaN; // reused
double c= Double.NaN; // Current
do
{
    p = c != Double.NaN ? c : _decibelList[i];
    c = _decibelList[++i];
} while (c < p);

_sensitivity = ((3 * c) + _decibelList[changeIndex]) / 4;

Примечание: Вы можете (отчасти) устранить необходимость сортировки, имея LinkedList и вставляя в подходящее место

...