21

我正在开发一个 iOS 应用程序,该应用程序上有一个带有麦克风的按钮(以及其他功能)。当用户按下麦克风时,它会突出显示,应用程序现在应该开始从设备的麦克风录制声音并发送到服务器(专用于应用程序的服务器,由我认识的人开发,所以我可以影响它的设计)。

我正在寻找最简单但最坚固的方法来做到这一点,即我不需要开发复杂的流媒体解决方案或 VoIP 功能,除非它与其他任何事情一样简单。

主要问题是我们不知道用户将录制声音多长时间,但我们希望确保声音连续发送到服务器,我们不希望等到用户完成录制。如果数据以块的形式到达服务器是可以的,但是我们不希望错过用户可能正在记录的任何信息,因此一个块必须在前一个结束的地方继续,依此类推。

我们的第一个想法是创建例如 10 秒的声音片段“块”并将它们连续发送到服务器。有没有我错过的更好/更简单的流媒体解决方案?

我的问题是,在 iOS 上解决此任务的最简单但仍然可靠的方法是什么?

有没有办法从 AVAudioRecorder 正在运行的录音中提取声音块,而无需实际停止录音?

4

2 回答 2

20

实际上,拥有固定大小的块而不是固定时间可能更容易。然后你可以有两个缓冲区,一个是你当前用样本数据填充的。当活动缓冲区已满时,然后切换以填充另一个缓冲区,同时向发送者线程发送信号,该线程获取第一个缓冲区并将其发送到服务器。

您当然可以改用固定时间,但是您需要确保缓冲区足够大以保存所有样本,或者使用可以在需要时增加大小的动态缓冲区。但是,双缓冲和发送线程仍然可以相同。

于 2012-07-27T11:41:05.443 回答
20

在本教程中查看这个
,录制的声音将保存在 soundFileURL,然后您只需使用该内容创建一个 nsdata,然后将其发送到您的服务器。
希望这有帮助。

编辑:
我刚刚创建了一个包含 3 个按钮的版本,REC、SEND 和 Stop:
REC:将开始录制到文件中。
SEND :将记录在该文件中的内容保存在 NSData 中,并将其发送到服务器,然后重新开始记录。
和 STOP : 将停止录制。
这是代码:在您的 .h 文件中:

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>

@interface ViewController : UIViewController <AVAudioRecorderDelegate>

@property (nonatomic, retain) AVAudioRecorder *audioRecorder;
@property (nonatomic, retain) IBOutlet UIButton *recordButton;
@property (nonatomic, retain) IBOutlet UIButton *stopButton;
@property (nonatomic, retain) IBOutlet UIButton *sendButton;

@property BOOL stoped;

- (IBAction)startRec:(id)sender;
- (IBAction)sendToServer:(id)sender;
- (IBAction)stop:(id)sender;
@end

并在 .m 文件中:

#import "ViewController.h"

@implementation ViewController
@synthesize audioRecorder;
@synthesize recordButton,sendButton,stopButton;
@synthesize stoped;

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
sendButton.enabled = NO;
stopButton.enabled = NO;
stoped = YES;

NSArray *dirPaths;
NSString *docsDir;

dirPaths = NSSearchPathForDirectoriesInDomains(
                                               NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
NSString *soundFilePath = [docsDir
                           stringByAppendingPathComponent:@"tempsound.caf"];

NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];

NSDictionary *recordSettings = [NSDictionary 
                                dictionaryWithObjectsAndKeys:
                                [NSNumber numberWithInt:AVAudioQualityMin],
                                AVEncoderAudioQualityKey,
                                [NSNumber numberWithInt:16], 
                                AVEncoderBitRateKey,
                                [NSNumber numberWithInt: 2], 
                                AVNumberOfChannelsKey,
                                [NSNumber numberWithFloat:44100.0], 
                                AVSampleRateKey,
                                nil];

NSError *error = nil;

audioRecorder = [[AVAudioRecorder alloc]
                 initWithURL:soundFileURL
                 settings:recordSettings
                 error:&error];
audioRecorder.delegate = self;
if (error)
{
    NSLog(@"error: %@", [error localizedDescription]);

} else {
    [audioRecorder prepareToRecord];
}
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (BOOL) sendAudioToServer :(NSData *)data {
    NSData *d = [NSData dataWithData:data];
    //now you'll just have to send that NSData to your server

    return YES;
}
-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
NSLog(@"stoped");
    if (!stoped) {
        NSData *data = [NSData dataWithContentsOfURL:recorder.url];
        [self sendAudioToServer:data];
        [recorder record];
NSLog(@"stoped sent and restarted");
    }
}
- (IBAction)startRec:(id)sender {
if (!audioRecorder.recording)
{
    sendButton.enabled = YES;
    stopButton.enabled = YES;
    [audioRecorder record];
}
}

- (IBAction)sendToServer:(id)sender {
stoped = NO;
[audioRecorder stop];
}

- (IBAction)stop:(id)sender {
stopButton.enabled = NO;
sendButton.enabled = NO;
recordButton.enabled = YES;

stoped = YES;
if (audioRecorder.recording)
{
    [audioRecorder stop];
}
}
@end

祝你好运。

于 2012-08-07T12:04:55.630 回答