iOS录音wav caf 及mp3转码 音频的曲线

一 : 定义一个全局变量AVAudioRecorder的录音对象,并启动录音

  1. PCM格式设置如下:
//设置属性的字典
recordSetting = [[NSMutableDictionary alloc] init];

//1.格式
    [recordSetting setObject:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
    //2.采样率
    [recordSetting setObject:[NSNumber numberWithInt:1600] forKey:AVSampleRateKey];
    //3.声道 要转换mp3格式声道就必须设置为2
    [recordSetting setObject:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
    //4.采样位数
    [recordSetting setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];

非PCM格式 比如IMA4

    [recordSetting setValue :[NSNumber numberWithInt:kAudioFormatAppleIMA4] forKey:AVFormatIDKey];
    [recordSetting setValue:[NSNumber numberWithFloat:1600.0] forKey:AVSampleRateKey];
    [recordSetting setValue:[NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey];

2.设置路径及其url,并清除上一次缓存录音文件,录取新的文件

NSString *recprderPath = [NSHomeDirectory() stringByAppendingPathComponent:@"recorder.wav"];
//路劲另一个方法:[NSString stringWithFormat:@"%@/Documents/MySound.caf", NSHomeDirectory()]];
}
NSURL *url = [NSURL fileURLWithPath:recprderPath];

NSError *err = nil;
    err = nil;
    //从路径下检查是否有文件
    NSData *audioData = [NSData dataWithContentsOfFile:[url path] options: 0 error:&err];
    if(audioData)
    {
    //如果有使用文件管理移除上一次录音文件
        NSFileManager *fm = [NSFileManager defaultManager];
        [fm removeItemAtPath:[url path] error:&err];
    }
    err = nil;
//用字典定义的属性配置录音(初始化)
    recorder = [[ AVAudioRecorder alloc] initWithURL:url settings:recordSetting error:&err];

//如果录音配置失败
    if(!recorder){
    //打印错误信息
        NSLog(@"recorder: %@ %ld %@", [err domain], [err code], [[err userInfo] description]);
        UIAlertView *alert =
        [[UIAlertView alloc] initWithTitle: @"Warning"
                                   message: [err localizedDescription]
                                  delegate: nil
                         cancelButtonTitle:@"OK"
                         otherButtonTitles:nil];
        [alert show];
        return;
    }

    [recorder setDelegate:self];//设置代理
    [recorder prepareToRecord];//准备录音
    recorder.meteringEnabled = YES;//开启音量检测
    [recorder recordForDuration:(NSTimeInterval) 180.0];//录音有效时长


//设置定时器,每0.05秒调用updateMeters的方法画出音频曲线
timer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(updateMeters) userInfo:nil repeats:YES];
//这里的updateMeters是重写父类方法

二 : 关于定时器启动 以及音频曲线
音频曲线示例

1. 写父类方法  获得当前音频功率值,调用averagePowerForChannel之前你必须调用这个方法

- (void)updateMeters {
    [recorder updateMeters];
//如果录音的分贝小于-60并且时间超过180秒提交录音
   if (([recorder averagePowerForChannel:0] < -60.0) && (recordTime > 180.0)) {
        [self commitRecording];
        return;
    }
    recordTime += 0.05;//和定时器时间一样
    [self addSoundMeterItem:[recorder averagePowerForChannel:0]];
}

2. Sound meter operations

//定义一个宏和soundMeters[i]数值要一样
#define SOUND_METER_COUNT       40

- (void)shiftSoundMeterLeft {
    for(int i=0; i<SOUND_METER_COUNT - 1; i++) {
        soundMeters[i] = soundMeters[i+1];
    }
}

- (void)addSoundMeterItem:(int)lastValue {
    [self shiftSoundMeterLeft];
    [self shiftSoundMeterLeft];
    soundMeters[SOUND_METER_COUNT - 1] = lastValue;
    soundMeters[SOUND_METER_COUNT - 2] = lastValue;
    [self setNeedsDisplay];
}

3. 设置取消按钮,即图中的标注中,以及其方法

#define HUD_SIZE                220
#define CANCEL_BUTTON_HEIGHT    40

        //麦克风位置
        CGRect hudRect;
        hudRect = CGRectMake(self.center.x - (HUD_SIZE / 2), self.center.y - (HUD_SIZE / 2) - 20, HUD_SIZE, HUD_SIZE);
        int x = (frame.size.width - HUD_SIZE) / 2;
        btnCancel = [[UIButton alloc] initWithFrame:CGRectMake(x, hudRect.origin.y + HUD_SIZE - CANCEL_BUTTON_HEIGHT, HUD_SIZE, CANCEL_BUTTON_HEIGHT)];
        [btnCancel setTitle:NSLocalizedString(@"标注中", nil) forState:UIControlStateNormal];
        [btnCancel addTarget:self action:@selector(cancelled:) forControlEvents:UIControlEventTouchUpInside];
        btnCancel.enabled = NO;
        [self addSubview:btnCancel];

//取消按钮点击事件
- (void)cancelled:(id)sender {
    self.alpha = 0.0;
    [self setNeedsDisplay];

    [timer invalidate];
    [self cancelRecording];
}

//取消事件
- (void)cancelRecording {
  //配合代理使用事件
    if ([self.delegate respondsToSelector:@selector(voiceRecordCancelledByUser:)]) {
        [self.delegate voiceRecordCancelledByUser:self];
    }

    [recorder stop];
}


4. 画出录音曲线  以及  
- (void)drawRect:(CGRect)rect {
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = UIGraphicsGetCurrentContext();//上下文

    UIColor *strokeColor = [UIColor colorWithRed:0.886 green:0.0 blue:0.0 alpha:0.8];
    UIColor *fillColor = [UIColor colorWithRed:0.5827 green:0.5827 blue:0.5827 alpha:1.0];
    UIColor *gradientColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8];
    UIColor *color = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];


    NSArray *gradientColors = [NSArray arrayWithObjects:
                               (id)fillColor.CGColor,
                               (id)gradientColor.CGColor, nil];
    CGFloat gradientLocations[] = {0, 1};
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)gradientColors, gradientLocations);

    UIBezierPath *border = [UIBezierPath bezierPathWithRoundedRect:hudRect cornerRadius:10.0];
    CGContextSaveGState(context);
    [border addClip];
    CGContextDrawRadialGradient(context, gradient,
                                CGPointMake(hudRect.origin.x+HUD_SIZE/2, 120), 10,
                                CGPointMake(hudRect.origin.x+HUD_SIZE/2, 195), 215,
                                kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);

    CGContextRestoreGState(context);
    [strokeColor setStroke];
    border.lineWidth = 3.0;
    [border stroke];

    // 画出音频波浪 
    [[UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.4] set];

    CGContextSetLineWidth(context, 3.0);
    CGContextSetLineJoin(context, kCGLineJoinRound);

    int baseLine = 250;//不同的机型 定义不一样  距离screen上端距离可能是310mm
    int multiplier = 1;
    int maxLengthOfWave = 50;
    int maxValueOfMeter = 70;
    for(CGFloat x = SOUND_METER_COUNT - 1; x >= 0; x--)
    {
        multiplier = ((int)x % 2) == 0 ? 1 : -1;

        CGFloat y = baseLine + ((maxValueOfMeter * (maxLengthOfWave - abs(soundMeters[(int)x]))) / maxLengthOfWave) * multiplier;

        if(x == SOUND_METER_COUNT - 1) {
            CGContextMoveToPoint(context, x * (HUD_SIZE / SOUND_METER_COUNT) + hudRect.origin.x + 10, y);
            CGContextAddLineToPoint(context, x * (HUD_SIZE / SOUND_METER_COUNT) + hudRect.origin.x + 7, y);
        }
        else {
            CGContextAddLineToPoint(context, x * (HUD_SIZE / SOUND_METER_COUNT) + hudRect.origin.x + 10, y);
            CGContextAddLineToPoint(context, x * (HUD_SIZE / SOUND_METER_COUNT) + hudRect.origin.x + 7, y);
        }
    }

    CGContextStrokePath(context);

    // 画出标题   即图中大字标注中
    [color setFill];
    [self.title drawInRect:CGRectInset(hudRect, 0, 20) withFont:[UIFont systemFontOfSize:30.0] lineBreakMode:UILineBreakModeWordWrap alignment:UITextAlignmentCenter];//标题的位置文字大小 等等

    [imgMicrophone drawAtPoint:CGPointMake(hudRect.origin.x + hudRect.size.width/2 - imgMicrophone.size.width/2, hudRect.origin.y + hudRect.size.height/2 - imgMicrophone.size.height/2)];

    [[UIColor colorWithWhite:0.8 alpha:1.0] setFill];
    UIBezierPath *line = [UIBezierPath bezierPath];
    [line moveToPoint:CGPointMake(hudRect.origin.x, hudRect.origin.y + HUD_SIZE - CANCEL_BUTTON_HEIGHT)];
    [line addLineToPoint:CGPointMake(hudRect.origin.x + HUD_SIZE, hudRect.origin.y + HUD_SIZE - CANCEL_BUTTON_HEIGHT)];
    [line setLineWidth:3.0];
    [line stroke];
}

三 : 录音完成之后 开始转码 mp3

//导入头文件lame.h和其静态库
- (BOOL)audio_PCMtoMP3
{
    NSString *mp3FilePath = [NSHomeDirectory() stringByAppendingPathComponent:@"mp3FilePath.mp3"];
    mp3Path = mp3FilePath;
    NSFileManager * fileManager=[ NSFileManager defaultManager ];

    if ([fileManager removeItemAtPath :mp3FilePath error : nil ])
    {
        NSLog ( @" 删除 " );
    }

    @try {

        int read, write;

        FILE *pcm = fopen ([recorderFilePath cStringUsingEncoding : 1 ], "rb" );
        //source 被 转换的音频文件位置

        if (pcm == NULL )
        {

            NSLog ( @"file not found" );

        }
        else
        {

            fseek (pcm, 4 * 1024 , SEEK_CUR );
            //skip file header

            FILE *mp3 = fopen ([mp3FilePath cStringUsingEncoding : 1 ], "wb" );
            //output 输出生成的 Mp3 文件位置

            const int PCM_SIZE = 8192 ;

            const int MP3_SIZE = 8192 ;

            short int pcm_buffer[PCM_SIZE* 2 ];

            unsigned char mp3_buffer[MP3_SIZE];



            lame_t lame  = lame_init ();

            lame_set_num_channels (lame, 1 ); // 设置 1 为单通道,默认为 2 双通道

            lame_set_in_samplerate (lame, 1600.0 ); //11025.0取频  采样

            //lame_set_VBR(lame, vbr_default);

            lame_set_brate (lame, 8 );

            lame_set_mode (lame, 3 );

            lame_set_quality (lame, 2 ); /* 2=high 5 = medium 7=low 音 质 */

            lame_init_params (lame);



            do {

                read = fread (pcm_buffer, 2 * sizeof ( short int ), PCM_SIZE, pcm);

                if (read == 0 )

                    write = lame_encode_flush (lame, mp3_buffer, MP3_SIZE);

                else

                    write = lame_encode_buffer_interleaved (lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);



                fwrite (mp3_buffer, write, 1 , mp3);



            } while (read != 0 );



            lame_close (lame);//释放lame

            fclose (mp3);

            fclose (pcm);

            return YES ;

        }

        return NO ;

    }

    @catch (NSException *exception) {

        NSLog ( @"%@" ,[exception description ]);

        return NO ;

    }

    @finally {

        NSLog ( @" 转码执行完成 " );

    }

}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ios录音caf文件转MP3文件,以兼容android 注意音频参数的设置,如果声音异常,请调整参数。 code: AVAudioSession *session = [AVAudioSession sharedInstance]; NSError *sessionError; [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError]; _sampleRate = 11025;//8000;//44100; _quality = AVAudioQualityLow; if(session == nil) NSLog(@"Error creating session: %@", [sessionError description]); else [session setActive:YES error:nil]; NSString *cafFilePath = strCaf; // NSString *mp3FileName = @"Mp3File"; // mp3FileName = [mp3FileName stringByAppendingString:@".mp3"]; NSString *mp3FilePath = strMP3Path;//[[NSHomeDirectory() stringByAppendingFormat:@"/Documents/"] stringByAppendingPathComponent:mp3FileName]; @try { int read, write; FILE *pcm = fopen([cafFilePath cStringUsingEncoding:1], "rb"); //source fseek(pcm, 4*1024, SEEK_CUR); //skip file header FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb"); //output const int PCM_SIZE = 8192; const int MP3_SIZE = 8192; short int pcm_buffer[PCM_SIZE*2]; unsigned char mp3_buffer[MP3_SIZE]; lame_t lame = lame_init(); lame_set_in_samplerate(lame, _sampleRate); lame_set_VBR(lame, vbr_default); lame_init_params(lame); do { read = fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm); if (read == 0) write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE); else write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE); fwrite(mp3_buffer, write, 1, mp3); } while (read != 0); lame_close(lame); fclose(mp3); fclose(pcm); } @catch (NSException *exception) { NSLog(@"%@",[exception description]); } @finally { }
以下是一个使用HTML和JavaScript制作的音频播放器的示例代码,你可以将其复制到HTML文件中并在浏览器中打开以查看效果。请确保音频文件与HTML文件在同一目录下,并将音频文件名替换为你自己的音频文件名。 ```html <!DOCTYPE html> <html> <head> <title>音播放器</title> <style> /* 播放器样式 */ #player { width: 500px; margin: 0 auto; text-align: center; background-color: #f2f2f2; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px #ccc; } #player h1 { font-size: 36px; margin-bottom: 20px; } #player audio { width: 100%; margin-bottom: 20px; } #player button { font-size: 24px; margin-right: 10px; background-color: #4CAF50; color: white; border: none; border-radius: 5px; padding: 10px 20px; cursor: pointer; } #player button:hover { background-color: #3e8e41; } #player button:active { background-color: #3e8e41; box-shadow: 0 5px #666; transform: translateY(4px); } </style> </head> <body> <div id="player"> <h1>音频播放器</h1> <audio id="audio" src="your_audio_file.mp3"></audio> <button id="prev" onclick="prevTrack()">‹</button> <button id="play" onclick="playPause()">播放</button> <button id="next" onclick="nextTrack()">›</button> <br> <span id="currentTime">0:00</span> / <span id="duration">0:00</span> <br> <input type="range" id="seekSlider" value="0" onchange="seekTo()"> </div> <script> // 音频文件列表 var tracks = [ { "name": "歌曲1", "file": "your_audio_file1.mp3" }, { "name": "歌曲2", "file": "your_audio_file2.mp3" }, { "name": "歌曲3", "file": "your_audio_file3.mp3" } ]; // 当前播放的音频文件索引 var currentTrack = 0; // 获取音频元素 var audio = document.getElementById("audio"); // 获取播放按钮元素 var playButton = document.getElementById("play"); // 获取上一曲按钮元素 var prevButton = document.getElementById("prev"); // 获取下一曲按钮元素 var nextButton = document.getElementById("next"); // 获取当前时间元素 var currentTime = document.getElementById("currentTime"); // 获取总时长元素 var duration = document.getElementById("duration"); // 获取进度条元素 var seekSlider = document.getElementById("seekSlider"); // 更新播放时间和进度条 audio.addEventListener("timeupdate", function() { currentTime.innerHTML = formatTime(audio.currentTime); duration.innerHTML = formatTime(audio.duration); seekSlider.value = audio.currentTime / audio.duration * 100; }); // 格式化时间 function formatTime(time) { var minutes = Math.floor(time / 60); var seconds = Math.floor(time % 60); if (seconds < 10) { seconds = "0" + seconds; } return minutes + ":" + seconds; } // 播放/暂停 function playPause() { if (audio.paused) { audio.play(); playButton.innerHTML = "暂停"; } else { audio.pause(); playButton.innerHTML = "播放"; } } // 上一曲 function prevTrack() { currentTrack--; if (currentTrack < 0) { currentTrack = tracks.length - 1; } audio.src = tracks[currentTrack].file; audio.play(); playButton.innerHTML = "暂停"; } // 下一曲 function nextTrack() { currentTrack++; if (currentTrack >= tracks.length) { currentTrack = 0; } audio.src = tracks[currentTrack].file; audio.play(); playButton.innerHTML = "暂停"; } // 跳转到指定时间 function seekTo() { var seekToTime = audio.duration * seekSlider.value / 100; audio.currentTime = seekToTime; } // 初始化 audio.src = tracks[currentTrack].file; </script> </body> </html> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值