第九天 iOS音频技术

时间:2021-11-30 13:48:04

1. 

AQRecorder

    mRecordFormat.mFormatID = inFormatID;
    if (inFormatID == kAudioFormatLinearPCM)
    {
        // if we want pcm, default to signed 16-bit little-endian
        mRecordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
        mRecordFormat.mBitsPerChannel = 16;
        mRecordFormat.mBytesPerPacket = mRecordFormat.mBytesPerFrame = (mRecordFormat.mBitsPerChannel / 8) * mRecordFormat.mChannelsPerFrame;
        mRecordFormat.mFramesPerPacket = 1;
    }
    
//    else  {
//        
//        mRecordFormat.mSampleRate = 44100;//8000.0;//44100.0;
//        
//        mRecordFormat.mFormatID = kAudioFormatMPEG4AAC; // kAudioFormatMPEG4AAC_HE does not work. Can't find `AudioClassDescription`. `mFormatFlags` is set to 0.
//        mRecordFormat.mFormatFlags = kMPEG4Object_AAC_LC; // Format-specific flags to specify details of the format. Set to 0 to indicate no format flags. See “Audio Data Format Identifiers” for the flags that apply to each format.
//        mRecordFormat.mBytesPerPacket = 0; // The number of bytes in a packet of audio data. To indicate variable packet size, set this field to 0. For a format that uses variable packet size, specify the size of each packet using an AudioStreamPacketDescription structure.
//        mRecordFormat.mFramesPerPacket = 0; // The number of frames in a packet of audio data. For uncompressed audio, the value is 1. For variable bit-rate formats, the value is a larger fixed number, such as 1024 for AAC. For formats with a variable number of frames per packet, such as Ogg Vorbis, set this field to 0.
//        mRecordFormat.mBytesPerFrame = 0; // The number of bytes from the start of one frame to the start of the next frame in an audio buffer. Set this field to 0 for compressed formats. ...
//        mRecordFormat.mChannelsPerFrame = 1; // The number of channels in each frame of audio data. This value must be nonzero.
//        mRecordFormat.mBitsPerChannel = 0; // ... Set this field to 0 for compressed formats.
//        mRecordFormat.mReserved = 0; // Pads the structure out to force an even 8-byte alignment. Must be set to 0.
//    }
        url = CFURLCreateWithString(kCFAllocatorDefault, (CFStringRef)inRecordFile, NULL);
        
        // create the audio file
        OSStatus status = AudioFileCreateWithURL(url, kAudioFileCAFType, &mRecordFormat, kAudioFileFlags_EraseFile, &mRecordFile);
        CFRelease(url);
// ____________________________________________________________________________________
// AudioQueue callback function, called when an input buffers has been filled.
void AQRecorder::MyInputBufferHandler(    void *                                inUserData,
                                      AudioQueueRef                        inAQ,
                                      AudioQueueBufferRef                    inBuffer,
                                      const AudioTimeStamp *                inStartTime,
                                      UInt32                                inNumPackets,
                                      const AudioStreamPacketDescription*    inPacketDesc)
{
    AQRecorder *aqr = (AQRecorder *)inUserData;
    try {
        if (inNumPackets > 0) {
            // write packets to file
            XThrowIfError(AudioFileWritePackets(aqr->mRecordFile, FALSE, inBuffer->mAudioDataByteSize,
                                                inPacketDesc, aqr->mRecordPacket, &inNumPackets, inBuffer->mAudioData),
                          "AudioFileWritePackets failed");
            aqr->mRecordPacket += inNumPackets;
        }
        
        // if we're not stopping, re-enqueue the buffe so that it gets filled again
        if (aqr->IsRunning())
            XThrowIfError(AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL), "AudioQueueEnqueueBuffer failed");
    } catch (CAXException e) {
        char buf[256];
        fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
    }
}

AQPlayer

void AQPlayer::SetupNewQueue() 
{
    XThrowIfError(AudioQueueNewOutput(&mDataFormat, AQPlayer::AQBufferCallback, this, 
                                        CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &mQueue), "AudioQueueNew failed");

...

void AQPlayer::AQBufferCallback(void *                    inUserData,
                                AudioQueueRef            inAQ,
                                AudioQueueBufferRef        inCompleteAQBuffer) 
{
    AQPlayer *THIS = (AQPlayer *)inUserData;

    if (THIS->mIsDone) return;

    UInt32 numBytes;
    UInt32 nPackets = THIS->GetNumPacketsToRead();
    OSStatus result = AudioFileReadPackets(THIS->GetAudioFileID(), false, &numBytes, inCompleteAQBuffer->mPacketDescriptions, THIS->GetCurrentPacket(), &nPackets, 
                                           inCompleteAQBuffer->mAudioData);
    if (result)
        printf("AudioFileReadPackets failed: %d", (int)result);
    if (nPackets > 0) {
        inCompleteAQBuffer->mAudioDataByteSize = numBytes;        
        inCompleteAQBuffer->mPacketDescriptionCount = nPackets;        
        AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, 0, NULL);
        THIS->mCurrentPacket = (THIS->GetCurrentPacket() + nPackets);
    } 

 

 

 

2.levelmeter

- (void)updateLevelMeter:(id)sender {
    /*
    if (self.delegate) {
        UInt32 dataSize = sizeof(AudioQueueLevelMeterState);
        AudioQueueGetProperty([self.decapsulator Queue], kAudioQueueProperty_CurrentLevelMeter, levelMeterStates, &dataSize);
        if ([self.delegate respondsToSelector:@selector(levelMeterChanged:)]) {
            [self.delegate levelMeterChanged:levelMeterStates[0].mPeakPower];
        }
        
    }*/
}
    //self.timerLevelMeter = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(updateLevelMeter:) userInfo:nil repeats:YES];
    __weak __typeof(self)weakSelf = self;
    MLAudioMeterObserver *meterObserver = [[MLAudioMeterObserver alloc]init];
    meterObserver.actionBlock = ^(NSArray *levelMeterStates,MLAudioMeterObserver *meterObserver){
        NSLog(@"volume:%f",[MLAudioMeterObserver volumeForLevelMeterStates:levelMeterStates]);
        
        if ([weakSelf.delegate respondsToSelector:@selector(levelMeterChanged:)]) {
            [weakSelf.delegate levelMeterChanged:[MLAudioMeterObserver volumeForLevelMeterStates:levelMeterStates]];
        }
    };
    meterObserver.errorBlock = ^(NSError *error,MLAudioMeterObserver *meterObserver){
        //[[[UIAlertView alloc]initWithTitle:@"错误" message:error.userInfo[NSLocalizedDescriptionKey] delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil]show];
    };
    self.meterObserver = meterObserver;
    self.meterObserver.audioQueue = player->Queue();

3.

linesview

- (void)levelMeterChanged:(float)levelMeter {
    dispatch_async(dispatch_get_main_queue(), ^{
            //self.levelMeter.progress = levelMeter;
        NSLog(@"%.2f",levelMeter*1);
        
        [self.levelMeterLineView1 addMeter:levelMeter*1];
        [self.levelMeterLineView2 addMeter:levelMeter*1];
    });

}
-(void)addMeter:(float)meter
{
    if (high) {
        meter = meter*0.6 + 0.4;
    } else {
        meter = meter*0.6 + 0.35;
    }
    high = !high;
    
    [_meters addObject:@(meter)];
    
    if (_meters.count > 10) {
        [_meters removeObjectAtIndex:0];
    }
    [self setNeedsDisplay];
}

DXRecordView

- (void)levelMeterChanged:(float)levelMeter {
    dispatch_async(dispatch_get_main_queue(), ^{
        //self.levelMeter.progress = levelMeter;
        NSLog(@"%.2f",levelMeter*1);
        
        float showMeter = levelMeter*0.6 + 0.35;
        
        [_recordView setVoiceImageWithLowPassResults:showMeter];

    });
    
}
-(void)setVoiceImageWithLowPassResults:(double)lowPassResults
{
    CGRect frame = _meterImageView.frame;
    frame.size.height = 39*lowPassResults;
    frame.origin.y  = 22+5.5+39*(1-lowPassResults);
    _meterImageView.frame = frame;

4.recordButton 按住说话

    //录制
    self.recordButton = [[UIButton alloc] initWithFrame:CGRectMake(24, kVerticalPadding, CGRectGetWidth(self.bounds)-(24 * 2), kInputTextViewMinHeight)];
    self.recordButton.titleLabel.font = [UIFont systemFontOfSize:15.0];
    [self.recordButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
    [self.recordButton setBackgroundImage:[UIImage imageNamed:@"btn_long_round"] forState:UIControlStateNormal];
    [self.recordButton setBackgroundImage:[UIImage imageNamed:@"btn_long_round_hl"] forState:UIControlStateHighlighted];
    [self.recordButton setTitle:LOCALIZATION(@"按住说话") forState:UIControlStateNormal];
    [self.recordButton setTitle:LOCALIZATION(@"松开结束") forState:UIControlStateHighlighted];
    [self.recordButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
    //self.recordButton.hidden = YES;
    [self.recordButton addTarget:self action:@selector(recordButtonTouchDown) forControlEvents:UIControlEventTouchDown];
    [self.recordButton addTarget:self action:@selector(recordButtonTouchUpOutside) forControlEvents:UIControlEventTouchUpOutside];
    [self.recordButton addTarget:self action:@selector(recordButtonTouchUpInside) forControlEvents:UIControlEventTouchUpInside];
    [self.recordButton addTarget:self action:@selector(recordDragOutside) forControlEvents:UIControlEventTouchDragExit];
    [self.recordButton addTarget:self action:@selector(recordDragInside) forControlEvents:UIControlEventTouchDragEnter];

5.EMChatAudioBubbleView

- (void)setModel:(MessageModel *)model
{
    [super setModel:model];
    
    _timeLabel.text = [NSString stringWithFormat:@"%d'",self.model.time];
    
    if (self.model.isSender) {
        [_isReadView setHidden:YES];
        _animationImageView.image = [UIImage imageNamed:SENDER_ANIMATION_IMAGEVIEW_IMAGE_DEFAULT];
        _animationImageView.animationImages = _senderAnimationImages;
    }
    else{
        if (model.isPlayed) {
            [_isReadView setHidden:YES];
        }else{
            [_isReadView setHidden:NO];
        }

        _animationImageView.image = [UIImage imageNamed:RECEIVER_ANIMATION_IMAGEVIEW_IMAGE_DEFAULT];
        _animationImageView.animationImages = _recevierAnimationImages;
    }
    
    if (self.model.isPlaying)
    {
        [self startAudioAnimation];
    }else {
        [self stopAudioAnimation];
    }
}

 

@interface MiniCourseViewTableViewCell : UITableViewCell<UITableViewDataSource,UITableViewDelegate,CommentVoiceDelegate>

@property(nonatomic, strong) NSString * MiniContent;
@property(nonatomic, strong) UILabel *contentLabel,*numberLabel,* nameLabel,* timeLabel;
@property(nonatomic, strong) NSMutableArray * commentModelArray;
@property(nonatomic, strong) UIImageView * headImageView;
@property(nonatomic, strong) UIButton * laudButton;

@property (nonatomic, strong) UITableView * tableView;
@property (nonatomic, strong) PlayVoiceButton *playVoiceButton;
@property (nonatomic, strong)id <ReplyVoiceDelegate> delegate;


@property(nonatomic, strong) CourseReplay * replyModel;

+ (float)getHeightWithString:(NSString *)string fontSize:(int)size contenViewWidth:(CGFloat)width;

@end
 

 

@interface FollowTalkThingCommentTableViewCell : UITableViewCell

@property (nonatomic, strong) UIImageView *userImageView;
@property (nonatomic, strong) UILabel *nameLabel;
@property (nonatomic, strong) UILabel *timeLabel;
@property (nonatomic, strong) UIButton *zanButton;

@property (strong, nonatomic)  UILabel *answerLabel,*replyLabel;
@property (nonatomic, strong) PlayVoiceButton *voiceButton;
@property (strong, nonatomic)  UIView *answerView;

@property (strong, nonatomic) UILabel *scoreLabel;

@property (nonatomic, strong)id <playVoiceButton> delegate;

+(CGSize)cellBubbleSizeWithContent:(NSString *)content;

@property (strong, nonatomic)  id model;

@end