- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
嗨,萨雷姆
背景
我有一个应用程序可以检测到有人说“Hi Sarem”是一种电子锁。我想做一些像“Hi Siri”这样的东西,但既然是这样,我就去做了一些不同的事情,比如“Hi Sarem”。
实现
该代码从麦克风采样音频,拟合 FFT,然后检查三个连续频率,因此您可以触发它,例如吹口哨或在钢琴上弹奏正确的三个音符。这些频率需要在一定时间内相互触发,并且可以使用 slider 进行配置。该代码包含设置时间和公差等所需的参数。三个 slider 代表“Hi-Sa-rem”中的三个“音符”。
用户界面
此处的图像给出了 UI 的概念。当检测到相关频率时,子弹会变成红色,一旦检测到整个序列,大的就会变成红色。顶部的 slider 充当监视器,持续监视“听到”的频率,因此您可以使用它来校准音符。
问题
我对此有一些问题。准确性是一个重要因素,但不是主要因素。 (我想如果我有一个更可怕的妈妈,这可能会更准确,也可以在午餐时间完成,但那是另一个故事......)
所以这里是 - 主要问题。
这在设备上运行良好,但在模拟器上我在日志中得到以下内容
2020-07-26 18:47:13.543219+0200 HiSarem[68826:1238118] [plugin] AddInstanceForFactory: No factory registered for id <CFUUID 0x600000788320> F8BB1C28-BAE8-11D6-9C31-00039315CD46
2020-07-26 18:47:13.575866+0200 HiSarem[68826:1238118] No exclusivity (null)
我怀疑这与访问权限有关,但我不确定。我到处找我知道的,但对我来说,错误会提示工厂没有注册是没有意义的。另外,为什么它在设备上而不是在模拟器上工作?现在我确实打印出我无法获得对设备的独占访问权限,但即使没有请求或锁定麦克风,我仍然会遇到问题。
ViewController
那个
单 View 应用程序 会给出,我确实描述了 UI 是如何连接到它的。因此,您应该能够将其简单地粘贴到项目中并在需要时运行它。这是一个测试项目,并不完善,但本着 MRE 的精神,您拥有所有代码。
#import <AVKit/AVKit.h>
#import <Accelerate/Accelerate.h>
#import "ViewController.h"
// Amplitute threshold
#define THRESHOLD 500
// Maximum frequency
#define MAXFREQ 7000
// Tolerance (% so 0.1 is 10%)
#define TOL 0.1
// Reset if no match within so many millis
#define RESETMIL 1500
#define BIGRESETMIL 5000
@interface ViewController () < AVCaptureAudioDataOutputSampleBufferDelegate >
@property (weak, nonatomic) IBOutlet UISlider * monitorSlider;
@property (weak, nonatomic) IBOutlet UISlider * phrase1Slider;
@property (weak, nonatomic) IBOutlet UISlider * phrase2Slider;
@property (weak, nonatomic) IBOutlet UISlider * phrase3Slider;
@property (weak, nonatomic) IBOutlet UILabel * phrase1Label;
@property (weak, nonatomic) IBOutlet UILabel * phrase2Label;
@property (weak, nonatomic) IBOutlet UILabel * phrase3Label;
@property (weak, nonatomic) IBOutlet UILabel * successLabel;
@property (nonatomic) BOOL busy;
@property (nonatomic, strong) AVCaptureSession * avSession;
@property (nonatomic, strong) AVCaptureInput * avInput;
@property (nonatomic, strong) AVCaptureDevice * avDevice;
@property (nonatomic, strong) AVCaptureOutput * avOutput;
@property (nonatomic) double prevF;
@property (nonatomic) NSDate * prevTime;
@end
@implementation ViewController
+ ( NSString * ) offText
{
return @"⚫️";
}
+ ( NSString * ) onText
{
return @"🔴";
}
// See if we can turn on for a given frequency
- ( BOOL ) turnOn:( double ) f
want:( double ) w
{
double wLo = w * ( 1 - TOL );
double wHi = w * ( 1 + TOL );
return self.prevF < wLo && f >= wLo && f <= wHi;
}
// Update the value
- ( void ) measure:( int ) s
n:( int ) n
{
// Convert
double f = 44100.0 * s / n;
if ( f <= MAXFREQ )
{
self.monitorSlider.value = f;
// See where we are with the sliders
if ( [self.phrase1Label.text isEqualToString:ViewController.offText] )
{
// See if we can turn on 1
if ( [self turnOn:f want:self.phrase1Slider.value] )
{
self.phrase1Label.text = ViewController.onText;
// Match
self.prevTime = NSDate.date;
}
}
else if ( [self.phrase2Label.text isEqualToString:ViewController.offText] )
{
// See if we can turn on 2
if ( [self turnOn:f want:self.phrase2Slider.value] )
{
self.phrase2Label.text = ViewController.onText;
// Match
self.prevTime = NSDate.date;
}
}
else if ( [self.phrase3Label.text isEqualToString:ViewController.offText] )
{
// See if we can turn on 3
if ( [self turnOn:f want:self.phrase3Slider.value] )
{
self.phrase3Label.text = ViewController.onText;
self.successLabel.text = ViewController.onText;
// Big match
self.prevTime = NSDate.date;
}
}
}
// Reset if we do not get a match fast enough
if ( self.prevTime )
{
NSTimeInterval d = [NSDate.date timeIntervalSinceDate:self.prevTime] * 1000;
if ( d > RESETMIL )
{
self.phrase1Label.text = ViewController.offText;
self.phrase2Label.text = ViewController.offText;
self.phrase3Label.text = ViewController.offText;
}
if ( d > BIGRESETMIL )
{
self.successLabel.text = ViewController.offText;
}
}
}
- ( void ) viewDidLoad
{
super.viewDidLoad;
}
- ( void ) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if ( self.requestPermission )
{
self.startCapture;
}
}
- ( void ) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if ( self.avSession )
{
self.avSession.stopRunning;
self.avSession = nil;
}
}
- ( BOOL ) requestPermission
{
if ( AVAudioSession.sharedInstance.recordPermission == AVAudioSessionRecordPermissionGranted )
{
return YES;
}
else if ( AVAudioSession.sharedInstance.recordPermission == AVAudioSessionRecordPermissionDenied )
{
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"No ears"
message:@"I can not hear you - please change it quickly"
preferredStyle:UIAlertActionStyleDefault];
[alert addAction:[UIAlertAction actionWithTitle:@"Apologies"
style:UIAlertActionStyleDefault
handler:nil]];
[self presentViewController:alert
animated:YES
completion:nil];
return NO;
}
else
{
[AVAudioSession.sharedInstance requestRecordPermission:^ ( BOOL granted ) {
if ( granted )
{
self.startCapture;
}
}];
return NO;
}
}
- ( void ) startCapture
{
if ( ! self.busy )
{
self.busy = YES;
// Create the capture session.
NSError * avErr;
AVCaptureSession * captureSession = [[AVCaptureSession alloc] init];
// Default anyhow
captureSession.sessionPreset = AVCaptureSessionPresetHigh;
// Lookup the default audio device.
AVCaptureDevice * audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
if ( [audioDevice lockForConfiguration: & avErr] )
{
// Wrap the audio device in a capture device input.
AVCaptureDeviceInput * audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice
error: & avErr];
audioDevice.unlockForConfiguration;
if ( audioInput )
{
// If the input can be added, add it to the session.
if ( [captureSession canAddInput:audioInput] )
{
[captureSession addInput:audioInput];
AVCaptureAudioDataOutput * audioOutput = [[AVCaptureAudioDataOutput alloc] init];
if ( [captureSession canAddOutput:audioOutput] )
{
[audioOutput setSampleBufferDelegate:self
queue:dispatch_queue_create ( "ears", NULL )];
[captureSession addOutput:audioOutput];
// Do on background
dispatch_async ( dispatch_queue_create ( "spotty", NULL ), ^ {
NSLog ( @"Come to papa" );
captureSession.startRunning;
// Done
dispatch_async ( dispatch_get_main_queue (), ^ {
self.busy = NO;
self.avSession = captureSession;
self.avDevice = audioDevice;
self.avInput = audioInput;
self.avOutput = audioOutput;
} );
} );
}
else
{
NSLog ( @"Not today : add output" );
self.busy = NO;
}
}
else
{
NSLog( @"Sorry : add input" );
self.busy = NO;
}
}
else
{
NSLog( @"Ooops %@", avErr );
self.busy = NO;
}
}
else
{
NSLog( @"No exclusivity %@", avErr );
self.busy = NO;
}
}
}
#pragma mark -
#pragma mark Audio capture delegate
- ( void ) captureOutput:( AVCaptureOutput * ) output
didOutputSampleBuffer:( CMSampleBufferRef ) sampleBuffer
fromConnection:( AVCaptureConnection * ) connection
{
CMItemCount n = CMSampleBufferGetNumSamples ( sampleBuffer );
// We have our standards
if ( n == 1024 )
{
AudioBufferList audioBufferList;
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer (
sampleBuffer,
NULL,
& audioBufferList,
sizeof ( audioBufferList ),
NULL,
NULL,
kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
& sampleBuffer
);
// Loop buffers
for ( int b = 0; b < audioBufferList.mNumberBuffers; b ++ )
{
// Evaluate samples
[self fft:audioBufferList.mBuffers [ b ].mData];
}
// Release the baby ... I mean buffer
CFRelease ( sampleBuffer );
}
}
- ( void ) fft:( SInt16 * ) samples
{
// In place so r and i are both input and output
COMPLEX_SPLIT c;
float r [ 512 ];
float i [ 512 ];
c.realp = r;
c.imagp = i;
// Load it and calculate maximum amplitute along the way
int amp = 0;
for ( int s = 0; s < 512; s ++ )
{
SInt16 ev = samples [ s * 2 ];
SInt16 od = samples [ s * 2 + 1 ];
// Convert to float
r [ s ] = ( float ) ev;
i [ s ] = ( float ) od;
if ( amp < ev )
{
amp = ev;
}
if ( amp < od )
{
amp = od;
}
}
// Only proceed if we have a big enough amplitute
if ( amp > THRESHOLD )
{
FFTSetup fft = vDSP_create_fftsetup ( 10, kFFTRadix2 );
if ( fft )
{
// FFT!
vDSP_fft_zrip ( fft, & c, 1, 10, FFT_FORWARD );
// Get frequency
int maxS = 0;
float maxF = 0;
for ( int s = 1; s < 512; s ++ )
{
float f = r [ s ] * r [ s ] + i [ s ] * i [ s ];
if ( f > maxF )
{
maxF = f;
maxS = s;
}
}
// Dealloc
vDSP_destroy_fftsetup ( fft );
// Done
dispatch_async ( dispatch_get_main_queue (), ^ {
[self measure:maxS
n:1024];
} );
}
}
}
@end
为什么这在设备上运行良好但在模拟器上拒绝?
最佳答案
欢迎来到仅使用真实设备进行调试的世界,因为涉及音频并且模拟器可能对此很挑剔。
请记住,您希望将 AVCaptureXYZ 指针设置为 nil/NULL
在分配任何东西给他们之前。音频是 C 业务,Objective-C 不是调用快速快速工作的方法的理想语言。即使它有效..
还没有什么新东西。
此外,您可能在打开任何 session 之前需要一个设备,因此 AVCaptureSession 可以在 AVCaptureDevice 启动之后进行。我知道文档告诉对方。
但是当没有设备时你不需要 session ,对吧? :)
在写 dispatch_async(...
时, 做 self->_busy
而不是 self.busy
.和dispatch_async(dispatch_get_main_queue(),^{})
是线程业务,把它放在它所属的地方,围绕访问 UIKit 的东西。在 -(void)measure:(int)samples n:(int)n
内的示例中.
帮自己一个忙,改变 objective-C -(void)fft:(SInt16 *)samples;
到
void fft(SInt16* samples, int *result) {
//do fast fourier transformation
}
如果您需要访问
自我 在这个函数中,你实际上正在做一些接近错误的事情。避免在音频线程中使用 ObjC 方法调用。给
void*
怎么样?指向此函数的指针变量,以使自己可以从函数内部访问。或者将引用指针传递给函数以更改给定变量的内容。或者让它返回结果。
SInt16 amp = 0;
int s=0;
SInt16 evens;
SInt16 odds;
while ( s < 512 ) {
evens = samples[s * 2 ];
odds = samples[s * 2 + 1];
r[s] = (float)evens;
i[s] = (float)odds;
amp = MAX(amp,MAX(odds,evens));
s++;
}
并在委托(delegate)方法
-captureOutput:didOutputSampleBuffer:fromConnection:
CMItemCount numSamplesInBuffer = CMSampleBufferGetNumSamples(sampleBuffer);
// works only with 1024 samples
if ( numSamplesInBuffer == 1024 ) {
AudioBufferList audioBufferList;
CMBlockBufferRef buffer = CMSampleBufferGetDataBuffer(sampleBuffer);
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer,
NULL,
&audioBufferList,
sizeof(audioBufferList),
NULL,
NULL,
kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
&buffer //now its correct pointer
);
//provide variable for feedback
int result = 0;
// Loop buffers
int b = 0;
for (; b < audioBufferList.mNumberBuffers; b ++) {
// Evaluate samples
// use C if possible, don't call ObjC in functions if possible
fft(audioBufferList.mBuffers[b].mData, &result);
}
// later Release the baby ... I mean buffer <- yes buffer :)
CFRelease(buffer);
[self measure:result n:1024];
}
关于ios - 音频检测器可以在设备上工作,但不能在模拟器上工作......和准确性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63103407/
我是 javascript 的新手(今天开始弄乱它)。 我正在尝试更改名为“bar”的元素(div)的高度。条形图将成为图表的一部分。 我可以毫无问题地将按钮连接到更改栏高度的函数。一切正常,除了条形
错误 -> “UIVIew”没有名为“addSubView”的成员 override func viewDidLoad() { super.viewDidLoad() // Do an
我在命令行工具项目中复制并粘贴了 main.swift 下面链接中的代码。 How do you use CGEventTapCreate in Swift? 它构建没有错误,但是当我运行时, gua
我在尝试编译我的代码时遇到以下错误。 ERROR! ..\myCode\CPOI.cpp:68:41: error: cannot dynamic_cast 'screenType' (of type
我正在尝试将多个字符串连接到一个我已为其分配内存的字符串指针。这是一个例子: char *finalNumString = malloc(sizeof(char)*1024); finalNumStr
我在使用 dup2() 和 pipe() 时遇到问题。 当我尝试将管道的写入端 dup2 到 STDOUT_FILENO 时,我收到了 EBADF。 我用 gdb 在 dup2(pout[1], ST
首先,我应该说我运行的是 Windows 7。 因此,今天早上我尝试像往常一样从我的存储库中提取数据,但我做不到。我得到了错误: The authenticity of host 'github.co
刚开始在虚拟环境中运行Python,乱用Django,无法激活虚拟环境。 花了最后 4 个小时尝试在本地终端/VS 代码上激活虚拟环境 (venv),但没有成功。 避免使用“sudo pip inst
Tidyverse 的粉丝经常给出使用小标题而不是数据框的几个优点。它们中的大多数似乎旨在保护用户免于犯错误。例如,与数据框不同,小标题: 不需要 ,drop=FALSE不从数据中删除维度的论据。 不
我一直在对 Elm 应用程序进行 docker 化时遇到问题。据我所知,我已经创建了一个完整且有效的 Docker 文件……但它不起作用。 我会解释的。 所以我的脚本在 3 个文件中运行。 首先是启动
我可以在 Controller 中使用@Autowired,例如 @RestController public class Index { @Autowired HttpServlet
我定义了一个方法和一个函数: def print(str:String) = println val intToString = (n:Int) => n.toString 现在我想创作它们。 我的问
当我控制台单独记录变量“pokemons”时,它确实返回一个数组。但是当我尝试映射它时,出现错误: TypeError: pokemons.map is not a function 我的代码: im
每当我尝试在 Python 解释器中导入 smtplib 时,都会收到此错误: ImportError: cannot import name fix_eols 我该如何解决这个问题? 编辑:这是完整
我正在使用 Meteor.js 开发一个项目,但在使用 Handlebar 时遇到了一些问题:我想检索集合的最后一项,并显示字段:其中包含 html 的文本: 这是我的javascript代码: Te
你好,我想使用 Service 实现 GestureDetector 但是我有这个错误The method onTouchEvent(MotionEvent) of type GestureServi
我正在尝试在 Controller bean 中 Autowiring 接口(interface) 在我放置的上下文配置文件中 和 我的 Controller 类是 @Controller pub
我试图在 mainwindow.cpp 中包含 QtSvg,但是当我编译时它说无法打开包含文件:QtSvg。我已经在我的 *.pro 文件中添加了这个(QT += svg)。我可以知道可能是什么问题吗
鉴于以下 PostgreSQL 代码,我认为这段代码不容易受到 SQL 注入(inject)攻击: _filter 'day' _start 1 _end 10 _sort 'article_name
我想执行以下操作。这在 MySQL 中是非法的。 PostGRESQL 中关联的 CTE(“with”子句)有效。这里的假设是 MySQL 中的子查询不是完全限定的 CTE。 请注意:这个查询显然非常
我是一名优秀的程序员,十分优秀!