Commit f016bd75 authored by lmj_521aiau@163.com's avatar lmj_521aiau@163.com

Audio unit

parent 5aa6add5
......@@ -14,6 +14,25 @@
A94D935924F7969600A886C0 /* CMNetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A94D935824F7969600A886C0 /* CMNetworkManager.swift */; };
A94D935B24F7977400A886C0 /* PhoneSystemKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = A94D935A24F7977400A886C0 /* PhoneSystemKit.swift */; };
A94D935E24F7AF2300A886C0 /* SHLocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A94D935D24F7AF2300A886C0 /* SHLocationManager.swift */; };
A94DD56024FDF29700B1B5A2 /* libmp3lame.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A94DD53A24FDF29700B1B5A2 /* libmp3lame.a */; };
A94DD56124FDF29700B1B5A2 /* XBAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD53D24FDF29700B1B5A2 /* XBAudioPlayer.m */; };
A94DD56224FDF29700B1B5A2 /* XBAudioTool.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD53F24FDF29700B1B5A2 /* XBAudioTool.m */; };
A94DD56324FDF29700B1B5A2 /* XBPCMPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD54024FDF29700B1B5A2 /* XBPCMPlayer.m */; };
A94DD56424FDF29700B1B5A2 /* XBAudioUnitRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD54124FDF29700B1B5A2 /* XBAudioUnitRecorder.m */; };
A94DD56524FDF29700B1B5A2 /* XBDataWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD54324FDF29700B1B5A2 /* XBDataWriter.m */; };
A94DD56624FDF29700B1B5A2 /* XBExtAudioFileRef.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD54424FDF29700B1B5A2 /* XBExtAudioFileRef.m */; };
A94DD56724FDF29700B1B5A2 /* XBAudioUnitPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD54524FDF29700B1B5A2 /* XBAudioUnitPlayer.m */; };
A94DD56824FDF29700B1B5A2 /* MP3Encoder.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD54724FDF29700B1B5A2 /* MP3Encoder.m */; };
A94DD56924FDF29700B1B5A2 /* XBAudioUnitMixer.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD54B24FDF29700B1B5A2 /* XBAudioUnitMixer.m */; };
A94DD56A24FDF29700B1B5A2 /* XBAudioPCMDataReader.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD55124FDF29700B1B5A2 /* XBAudioPCMDataReader.m */; };
A94DD56B24FDF29700B1B5A2 /* XBAudioConverterPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD55424FDF29700B1B5A2 /* XBAudioConverterPlayer.m */; };
A94DD56C24FDF29700B1B5A2 /* XBAudioFormatConversion.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD55624FDF29700B1B5A2 /* XBAudioFormatConversion.m */; };
A94DD56D24FDF29700B1B5A2 /* XBAudioUnitMixerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD55824FDF29700B1B5A2 /* XBAudioUnitMixerTest.m */; };
A94DD56E24FDF29700B1B5A2 /* XBAudioFileDataReader.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD55924FDF29700B1B5A2 /* XBAudioFileDataReader.m */; };
A94DD56F24FDF29700B1B5A2 /* XBAACEncoder_system.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD55E24FDF29700B1B5A2 /* XBAACEncoder_system.m */; };
A94DD57024FDF29700B1B5A2 /* XBAudioDataBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD55F24FDF29700B1B5A2 /* XBAudioDataBuffer.m */; };
A94DD57324FDF9D200B1B5A2 /* SHMp3RecordManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD57224FDF9D200B1B5A2 /* SHMp3RecordManager.m */; };
A94DD57624FDFB4700B1B5A2 /* ExtAudioFileMixer.m in Sources */ = {isa = PBXBuildFile; fileRef = A94DD57524FDFB4700B1B5A2 /* ExtAudioFileMixer.m */; };
A950F5A824F36F55007AB63E /* SHRecordListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A950F5A724F36F55007AB63E /* SHRecordListViewController.swift */; };
A950F5AA24F3727A007AB63E /* SHRecordListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A950F5A924F3727A007AB63E /* SHRecordListCell.swift */; };
A950F5AC24F39EC1007AB63E /* SHRecordShowViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A950F5AB24F39EC1007AB63E /* SHRecordShowViewController.swift */; };
......@@ -30,6 +49,8 @@
A95B3FCD24F50B2F00FABDD1 /* SHRecentDeleteCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95B3FCC24F50B2F00FABDD1 /* SHRecentDeleteCell.swift */; };
A95B3FD024F525AD00FABDD1 /* SwiftHoledView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95B3FCF24F525AD00FABDD1 /* SwiftHoledView.swift */; };
A95B3FD224F5261100FABDD1 /* SHRecordGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95B3FD124F5261100FABDD1 /* SHRecordGuideView.swift */; };
A95C2FDA24FC97BD00C685CE /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A95C2FD924FC97BD00C685CE /* CloudKit.framework */; };
A95C2FDC24FCBF5400C685CE /* SHCloudManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95C2FDB24FCBF5400C685CE /* SHCloudManager.swift */; };
A95CDF6824E0E8B50066DAE6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95CDF6724E0E8B50066DAE6 /* AppDelegate.swift */; };
A95CDF6F24E0E8B50066DAE6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A95CDF6D24E0E8B50066DAE6 /* Main.storyboard */; };
A95CDF7124E0E8B80066DAE6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A95CDF7024E0E8B80066DAE6 /* Assets.xcassets */; };
......@@ -81,6 +102,7 @@
A95CE03624E1729B0066DAE6 /* SHRecordWaveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95CE03524E1729B0066DAE6 /* SHRecordWaveView.swift */; };
A95CE03824E17BAF0066DAE6 /* SHTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95CE03724E17BAF0066DAE6 /* SHTimer.swift */; };
A95CE03A24E2AE6A0066DAE6 /* SHRecordModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95CE03924E2AE6A0066DAE6 /* SHRecordModel.swift */; };
A960947524FD25D800121D32 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A960947424FD25D800121D32 /* AVFoundation.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
......@@ -109,6 +131,45 @@
A94D935824F7969600A886C0 /* CMNetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CMNetworkManager.swift; sourceTree = "<group>"; };
A94D935A24F7977400A886C0 /* PhoneSystemKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhoneSystemKit.swift; sourceTree = "<group>"; };
A94D935D24F7AF2300A886C0 /* SHLocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SHLocationManager.swift; sourceTree = "<group>"; };
A94DD53A24FDF29700B1B5A2 /* libmp3lame.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libmp3lame.a; sourceTree = "<group>"; };
A94DD53B24FDF29700B1B5A2 /* lame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lame.h; sourceTree = "<group>"; };
A94DD53D24FDF29700B1B5A2 /* XBAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBAudioPlayer.m; sourceTree = "<group>"; };
A94DD53E24FDF29700B1B5A2 /* XBAudioPCMDataReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBAudioPCMDataReader.h; sourceTree = "<group>"; };
A94DD53F24FDF29700B1B5A2 /* XBAudioTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBAudioTool.m; sourceTree = "<group>"; };
A94DD54024FDF29700B1B5A2 /* XBPCMPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBPCMPlayer.m; sourceTree = "<group>"; };
A94DD54124FDF29700B1B5A2 /* XBAudioUnitRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBAudioUnitRecorder.m; sourceTree = "<group>"; };
A94DD54224FDF29700B1B5A2 /* XBAudioConverterPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBAudioConverterPlayer.h; sourceTree = "<group>"; };
A94DD54324FDF29700B1B5A2 /* XBDataWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBDataWriter.m; sourceTree = "<group>"; };
A94DD54424FDF29700B1B5A2 /* XBExtAudioFileRef.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBExtAudioFileRef.m; sourceTree = "<group>"; };
A94DD54524FDF29700B1B5A2 /* XBAudioUnitPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBAudioUnitPlayer.m; sourceTree = "<group>"; };
A94DD54724FDF29700B1B5A2 /* MP3Encoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MP3Encoder.m; sourceTree = "<group>"; };
A94DD54824FDF29700B1B5A2 /* MP3Encoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MP3Encoder.h; sourceTree = "<group>"; };
A94DD54924FDF29700B1B5A2 /* XBAudioFileDataReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBAudioFileDataReader.h; sourceTree = "<group>"; };
A94DD54A24FDF29700B1B5A2 /* XBAudioUnitMixerTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBAudioUnitMixerTest.h; sourceTree = "<group>"; };
A94DD54B24FDF29700B1B5A2 /* XBAudioUnitMixer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBAudioUnitMixer.m; sourceTree = "<group>"; };
A94DD54C24FDF29700B1B5A2 /* XBAudioFormatConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBAudioFormatConversion.h; sourceTree = "<group>"; };
A94DD54D24FDF29700B1B5A2 /* Header_audio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Header_audio.h; sourceTree = "<group>"; };
A94DD54E24FDF29700B1B5A2 /* XBAudioDataBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBAudioDataBuffer.h; sourceTree = "<group>"; };
A94DD54F24FDF29700B1B5A2 /* XBPCMPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBPCMPlayer.h; sourceTree = "<group>"; };
A94DD55024FDF29700B1B5A2 /* XBAudioTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBAudioTool.h; sourceTree = "<group>"; };
A94DD55124FDF29700B1B5A2 /* XBAudioPCMDataReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBAudioPCMDataReader.m; sourceTree = "<group>"; };
A94DD55224FDF29700B1B5A2 /* XBAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBAudioPlayer.h; sourceTree = "<group>"; };
A94DD55324FDF29700B1B5A2 /* XBDataWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBDataWriter.h; sourceTree = "<group>"; };
A94DD55424FDF29700B1B5A2 /* XBAudioConverterPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBAudioConverterPlayer.m; sourceTree = "<group>"; };
A94DD55524FDF29700B1B5A2 /* XBAudioUnitRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBAudioUnitRecorder.h; sourceTree = "<group>"; };
A94DD55624FDF29700B1B5A2 /* XBAudioFormatConversion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBAudioFormatConversion.m; sourceTree = "<group>"; };
A94DD55724FDF29700B1B5A2 /* XBAudioUnitMixer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBAudioUnitMixer.h; sourceTree = "<group>"; };
A94DD55824FDF29700B1B5A2 /* XBAudioUnitMixerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBAudioUnitMixerTest.m; sourceTree = "<group>"; };
A94DD55924FDF29700B1B5A2 /* XBAudioFileDataReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBAudioFileDataReader.m; sourceTree = "<group>"; };
A94DD55A24FDF29700B1B5A2 /* XBAudioUnitPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBAudioUnitPlayer.h; sourceTree = "<group>"; };
A94DD55B24FDF29700B1B5A2 /* XBExtAudioFileRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBExtAudioFileRef.h; sourceTree = "<group>"; };
A94DD55D24FDF29700B1B5A2 /* XBAACEncoder_system.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBAACEncoder_system.h; sourceTree = "<group>"; };
A94DD55E24FDF29700B1B5A2 /* XBAACEncoder_system.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBAACEncoder_system.m; sourceTree = "<group>"; };
A94DD55F24FDF29700B1B5A2 /* XBAudioDataBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBAudioDataBuffer.m; sourceTree = "<group>"; };
A94DD57124FDF9D200B1B5A2 /* SHMp3RecordManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SHMp3RecordManager.h; sourceTree = "<group>"; };
A94DD57224FDF9D200B1B5A2 /* SHMp3RecordManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SHMp3RecordManager.m; sourceTree = "<group>"; };
A94DD57424FDFB4600B1B5A2 /* ExtAudioFileMixer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExtAudioFileMixer.h; sourceTree = "<group>"; };
A94DD57524FDFB4700B1B5A2 /* ExtAudioFileMixer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExtAudioFileMixer.m; sourceTree = "<group>"; };
A950F5A724F36F55007AB63E /* SHRecordListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SHRecordListViewController.swift; sourceTree = "<group>"; };
A950F5A924F3727A007AB63E /* SHRecordListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SHRecordListCell.swift; sourceTree = "<group>"; };
A950F5AB24F39EC1007AB63E /* SHRecordShowViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SHRecordShowViewController.swift; sourceTree = "<group>"; };
......@@ -126,6 +187,9 @@
A95B3FCC24F50B2F00FABDD1 /* SHRecentDeleteCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SHRecentDeleteCell.swift; sourceTree = "<group>"; };
A95B3FCF24F525AD00FABDD1 /* SwiftHoledView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftHoledView.swift; sourceTree = "<group>"; };
A95B3FD124F5261100FABDD1 /* SHRecordGuideView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SHRecordGuideView.swift; sourceTree = "<group>"; };
A95C2FD824FC97A400C685CE /* 速记大师.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "速记大师.entitlements"; sourceTree = "<group>"; };
A95C2FD924FC97BD00C685CE /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
A95C2FDB24FCBF5400C685CE /* SHCloudManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SHCloudManager.swift; sourceTree = "<group>"; };
A95CDF6424E0E8B50066DAE6 /* 速记大师.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "速记大师.app"; sourceTree = BUILT_PRODUCTS_DIR; };
A95CDF6724E0E8B50066DAE6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
A95CDF6E24E0E8B50066DAE6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
......@@ -188,6 +252,7 @@
A95CE03524E1729B0066DAE6 /* SHRecordWaveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SHRecordWaveView.swift; sourceTree = "<group>"; };
A95CE03724E17BAF0066DAE6 /* SHTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SHTimer.swift; sourceTree = "<group>"; };
A95CE03924E2AE6A0066DAE6 /* SHRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SHRecordModel.swift; sourceTree = "<group>"; };
A960947424FD25D800121D32 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
C47F440803385C784B8FFF5E /* Pods-ShorthandMaster.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShorthandMaster.release.xcconfig"; path = "Target Support Files/Pods-ShorthandMaster/Pods-ShorthandMaster.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
......@@ -196,7 +261,10 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A960947524FD25D800121D32 /* AVFoundation.framework in Frameworks */,
A94DD56024FDF29700B1B5A2 /* libmp3lame.a in Frameworks */,
79DC3D2A364BA7FB577BAFB2 /* Pods_ShorthandMaster.framework in Frameworks */,
A95C2FDA24FC97BD00C685CE /* CloudKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -229,6 +297,8 @@
A3066D62EDBE925B6124F41A /* Frameworks */ = {
isa = PBXGroup;
children = (
A960947424FD25D800121D32 /* AVFoundation.framework */,
A95C2FD924FC97BD00C685CE /* CloudKit.framework */,
3DAE7010C13FC81DF2891ABE /* Pods_ShorthandMaster.framework */,
);
name = Frameworks;
......@@ -241,6 +311,73 @@
path = Goods;
sourceTree = "<group>";
};
A94DD53924FDF29700B1B5A2 /* lame */ = {
isa = PBXGroup;
children = (
A94DD53A24FDF29700B1B5A2 /* libmp3lame.a */,
A94DD53B24FDF29700B1B5A2 /* lame.h */,
);
path = lame;
sourceTree = "<group>";
};
A94DD53C24FDF29700B1B5A2 /* AudioTool */ = {
isa = PBXGroup;
children = (
A94DD54D24FDF29700B1B5A2 /* Header_audio.h */,
A94DD54624FDF29700B1B5A2 /* Mp3Encoder */,
A94DD55C24FDF29700B1B5A2 /* AACEncoder */,
A94DD57424FDFB4600B1B5A2 /* ExtAudioFileMixer.h */,
A94DD57524FDFB4700B1B5A2 /* ExtAudioFileMixer.m */,
A94DD54224FDF29700B1B5A2 /* XBAudioConverterPlayer.h */,
A94DD55424FDF29700B1B5A2 /* XBAudioConverterPlayer.m */,
A94DD54E24FDF29700B1B5A2 /* XBAudioDataBuffer.h */,
A94DD55F24FDF29700B1B5A2 /* XBAudioDataBuffer.m */,
A94DD54924FDF29700B1B5A2 /* XBAudioFileDataReader.h */,
A94DD55924FDF29700B1B5A2 /* XBAudioFileDataReader.m */,
A94DD54C24FDF29700B1B5A2 /* XBAudioFormatConversion.h */,
A94DD55624FDF29700B1B5A2 /* XBAudioFormatConversion.m */,
A94DD53E24FDF29700B1B5A2 /* XBAudioPCMDataReader.h */,
A94DD55124FDF29700B1B5A2 /* XBAudioPCMDataReader.m */,
A94DD55224FDF29700B1B5A2 /* XBAudioPlayer.h */,
A94DD53D24FDF29700B1B5A2 /* XBAudioPlayer.m */,
A94DD55024FDF29700B1B5A2 /* XBAudioTool.h */,
A94DD53F24FDF29700B1B5A2 /* XBAudioTool.m */,
A94DD55724FDF29700B1B5A2 /* XBAudioUnitMixer.h */,
A94DD54B24FDF29700B1B5A2 /* XBAudioUnitMixer.m */,
A94DD54A24FDF29700B1B5A2 /* XBAudioUnitMixerTest.h */,
A94DD55824FDF29700B1B5A2 /* XBAudioUnitMixerTest.m */,
A94DD55A24FDF29700B1B5A2 /* XBAudioUnitPlayer.h */,
A94DD54524FDF29700B1B5A2 /* XBAudioUnitPlayer.m */,
A94DD55524FDF29700B1B5A2 /* XBAudioUnitRecorder.h */,
A94DD54124FDF29700B1B5A2 /* XBAudioUnitRecorder.m */,
A94DD55324FDF29700B1B5A2 /* XBDataWriter.h */,
A94DD54324FDF29700B1B5A2 /* XBDataWriter.m */,
A94DD55B24FDF29700B1B5A2 /* XBExtAudioFileRef.h */,
A94DD54424FDF29700B1B5A2 /* XBExtAudioFileRef.m */,
A94DD54F24FDF29700B1B5A2 /* XBPCMPlayer.h */,
A94DD54024FDF29700B1B5A2 /* XBPCMPlayer.m */,
);
path = AudioTool;
sourceTree = "<group>";
};
A94DD54624FDF29700B1B5A2 /* Mp3Encoder */ = {
isa = PBXGroup;
children = (
A94DD54724FDF29700B1B5A2 /* MP3Encoder.m */,
A94DD54824FDF29700B1B5A2 /* MP3Encoder.h */,
);
path = Mp3Encoder;
sourceTree = "<group>";
};
A94DD55C24FDF29700B1B5A2 /* AACEncoder */ = {
isa = PBXGroup;
children = (
A94DD55D24FDF29700B1B5A2 /* XBAACEncoder_system.h */,
A94DD55E24FDF29700B1B5A2 /* XBAACEncoder_system.m */,
);
path = AACEncoder;
sourceTree = "<group>";
};
A950F5AD24F4E003007AB63E /* Mine */ = {
isa = PBXGroup;
children = (
......@@ -256,6 +393,8 @@
A950F5B224F4E5A4007AB63E /* Other */ = {
isa = PBXGroup;
children = (
A94DD53C24FDF29700B1B5A2 /* AudioTool */,
A94DD53924FDF29700B1B5A2 /* lame */,
A95B3FCE24F525AD00FABDD1 /* SwiftHoledView */,
A950F5B324F4E5BA007AB63E /* PopView */,
);
......@@ -307,6 +446,7 @@
A95CDF6624E0E8B50066DAE6 /* ShorthandMaster */ = {
isa = PBXGroup;
children = (
A95C2FD824FC97A400C685CE /* 速记大师.entitlements */,
A94D935524F75CE700A886C0 /* Goods */,
A950F5B224F4E5A4007AB63E /* Other */,
A950F5AD24F4E003007AB63E /* Mine */,
......@@ -423,6 +563,7 @@
A95CDFEE24E0F42E0066DAE6 /* SHUserAccountManager.swift */,
A95CDFF024E0F42F0066DAE6 /* CRContactTools.swift */,
A95CDFF124E0F42F0066DAE6 /* CRDateFormatter.swift */,
A95C2FDB24FCBF5400C685CE /* SHCloudManager.swift */,
);
path = Managers;
sourceTree = "<group>";
......@@ -499,6 +640,8 @@
A94D935124F7502700A886C0 /* SHRecordExportAlertView.swift */,
A94D935324F7503E00A886C0 /* SHRecordExportAlertView.xib */,
A94D935D24F7AF2300A886C0 /* SHLocationManager.swift */,
A94DD57124FDF9D200B1B5A2 /* SHMp3RecordManager.h */,
A94DD57224FDF9D200B1B5A2 /* SHMp3RecordManager.m */,
);
path = Record;
sourceTree = "<group>";
......@@ -686,6 +829,7 @@
files = (
A950F5A824F36F55007AB63E /* SHRecordListViewController.swift in Sources */,
A94D935E24F7AF2300A886C0 /* SHLocationManager.swift in Sources */,
A94DD56524FDF29700B1B5A2 /* XBDataWriter.m in Sources */,
A95CDFDA24E0EBF10066DAE6 /* CRSnippetCode.swift in Sources */,
A95CE01124E0F42F0066DAE6 /* MBProgressHUD+MJ.m in Sources */,
A95CE00924E0F42F0066DAE6 /* CRContactTools.swift in Sources */,
......@@ -696,6 +840,7 @@
A95CDFBF24E0EBF10066DAE6 /* UIWindow+Extension.swift in Sources */,
A95CDFE524E0EE4B0066DAE6 /* SHBaseNavigationController.swift in Sources */,
A95CDFDD24E0EBF10066DAE6 /* SwiftKeyChain.swift in Sources */,
A94DD56E24FDF29700B1B5A2 /* XBAudioFileDataReader.m in Sources */,
A95CDFC224E0EBF10066DAE6 /* UIView+Extension.swift in Sources */,
A95CDFCD24E0EBF10066DAE6 /* UIView+Nib.swift in Sources */,
A95CE03A24E2AE6A0066DAE6 /* SHRecordModel.swift in Sources */,
......@@ -705,6 +850,8 @@
A95CDFC324E0EBF10066DAE6 /* UIImage+Extension.swift in Sources */,
A950F5B624F4E64A007AB63E /* UIView+CornerRadii.m in Sources */,
A95CE03224E1521F0066DAE6 /* SHRecordViewController.swift in Sources */,
A95C2FDC24FCBF5400C685CE /* SHCloudManager.swift in Sources */,
A94DD56624FDF29700B1B5A2 /* XBExtAudioFileRef.m in Sources */,
A95CDFC624E0EBF10066DAE6 /* String+Extension.swift in Sources */,
A95CE00D24E0F42F0066DAE6 /* CRIAPProductModel.swift in Sources */,
A95B3FD224F5261100FABDD1 /* SHRecordGuideView.swift in Sources */,
......@@ -716,9 +863,14 @@
A95CDFDF24E0ECD20066DAE6 /* SHBaseViewController.swift in Sources */,
A95CDFE324E0EE1A0066DAE6 /* SHBaseTabBarController.swift in Sources */,
A95CDFBD24E0EBF10066DAE6 /* UIDevice+Extension.swift in Sources */,
A94DD56324FDF29700B1B5A2 /* XBPCMPlayer.m in Sources */,
A95CDFD924E0EBF10066DAE6 /* iPhoneSystemKit.swift in Sources */,
A94DD56824FDF29700B1B5A2 /* MP3Encoder.m in Sources */,
A94DD56924FDF29700B1B5A2 /* XBAudioUnitMixer.m in Sources */,
A95CDF6824E0E8B50066DAE6 /* AppDelegate.swift in Sources */,
A94DD56124FDF29700B1B5A2 /* XBAudioPlayer.m in Sources */,
A95CDFC924E0EBF10066DAE6 /* UINavigationBar+Extension.swift in Sources */,
A94DD56224FDF29700B1B5A2 /* XBAudioTool.m in Sources */,
A95CE00724E0F42F0066DAE6 /* SHUserAccountManager.swift in Sources */,
A95CDFBE24E0EBF10066DAE6 /* UIColor+Extension.swift in Sources */,
A95CE00A24E0F42F0066DAE6 /* CRDateFormatter.swift in Sources */,
......@@ -726,26 +878,36 @@
A95CDFC424E0EBF10066DAE6 /* Double+Extension.swift in Sources */,
A95CDFD424E0EBF10066DAE6 /* CRMemberIAPViewController.swift in Sources */,
A95B3FCD24F50B2F00FABDD1 /* SHRecentDeleteCell.swift in Sources */,
A94DD57324FDF9D200B1B5A2 /* SHMp3RecordManager.m in Sources */,
A94DD56D24FDF29700B1B5A2 /* XBAudioUnitMixerTest.m in Sources */,
A94DD56C24FDF29700B1B5A2 /* XBAudioFormatConversion.m in Sources */,
A95CDFE724E0EE7A0066DAE6 /* SHGuideViewController.swift in Sources */,
A950F5AC24F39EC1007AB63E /* SHRecordShowViewController.swift in Sources */,
A950F5BC24F4E66B007AB63E /* PopBaseView.swift in Sources */,
A94DD57024FDF29700B1B5A2 /* XBAudioDataBuffer.m in Sources */,
A95CDFC824E0EBF10066DAE6 /* UITabBar+Extension.swift in Sources */,
A95CDFC124E0EBF10066DAE6 /* UIButton+Extension.swift in Sources */,
A94D935B24F7977400A886C0 /* PhoneSystemKit.swift in Sources */,
A94DD57624FDFB4700B1B5A2 /* ExtAudioFileMixer.m in Sources */,
A95CDFDC24E0EBF10066DAE6 /* CRConstants.swift in Sources */,
A95CE00F24E0F42F0066DAE6 /* AESCipher.m in Sources */,
A950F5AF24F4E06E007AB63E /* SHMineViewController.swift in Sources */,
A95CE02F24E151340066DAE6 /* UIButton+Category.m in Sources */,
A94D935924F7969600A886C0 /* CMNetworkManager.swift in Sources */,
A94DD56724FDF29700B1B5A2 /* XBAudioUnitPlayer.m in Sources */,
A94DD56F24FDF29700B1B5A2 /* XBAACEncoder_system.m in Sources */,
A95CE03624E1729B0066DAE6 /* SHRecordWaveView.swift in Sources */,
A950F5AA24F3727A007AB63E /* SHRecordListCell.swift in Sources */,
A94DD56B24FDF29700B1B5A2 /* XBAudioConverterPlayer.m in Sources */,
A95CDFC524E0EBF10066DAE6 /* UIView+Frame.swift in Sources */,
A95CDFDB24E0EBF10066DAE6 /* CRUserDefaults.swift in Sources */,
A94DD56424FDF29700B1B5A2 /* XBAudioUnitRecorder.m in Sources */,
A95CDFD724E0EBF10066DAE6 /* CRGuideCollectionViewCell.swift in Sources */,
A95B3FCB24F507A900FABDD1 /* SHDeleteDetailsViewController.swift in Sources */,
A95B3FD024F525AD00FABDD1 /* SwiftHoledView.swift in Sources */,
A95B3FC924F502DD00FABDD1 /* SHSettingViewController.swift in Sources */,
A95CDFCC24E0EBF10066DAE6 /* UIView+IBLayer.swift in Sources */,
A94DD56A24FDF29700B1B5A2 /* XBAudioPCMDataReader.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -919,16 +1081,23 @@
baseConfigurationReference = 64E548B725F65DFC619F8D08 /* Pods-ShorthandMaster.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "ShorthandMaster/速记大师.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 8F4PP38DC3;
GCC_INPUT_FILETYPE = automatic;
GCC_PREFIX_HEADER = "$(SRCROOT)/ShorthandMaster/PrefixHeader.pch";
INFOPLIST_FILE = ShorthandMaster/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/ShorthandMaster/Other/Lame",
"$(PROJECT_DIR)/ShorthandMaster/Other/lame",
);
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ShorthandMaster.www;
PRODUCT_NAME = "速记大师";
......@@ -944,16 +1113,23 @@
baseConfigurationReference = C47F440803385C784B8FFF5E /* Pods-ShorthandMaster.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "ShorthandMaster/速记大师.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 8F4PP38DC3;
GCC_INPUT_FILETYPE = automatic;
GCC_PREFIX_HEADER = "$(SRCROOT)/ShorthandMaster/PrefixHeader.pch";
INFOPLIST_FILE = ShorthandMaster/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/ShorthandMaster/Other/Lame",
"$(PROJECT_DIR)/ShorthandMaster/Other/lame",
);
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ShorthandMaster.www;
PRODUCT_NAME = "速记大师";
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1160"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A95CDF6324E0E8B50066DAE6"
BuildableName = "&#x901f;&#x8bb0;&#x5927;&#x5e08;.app"
BlueprintName = "ShorthandMaster"
ReferencedContainer = "container:ShorthandMaster.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A95CDF7924E0E8B80066DAE6"
BuildableName = "ShorthandMasterTests.xctest"
BlueprintName = "ShorthandMasterTests"
ReferencedContainer = "container:ShorthandMaster.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A95CDF8424E0E8B80066DAE6"
BuildableName = "ShorthandMasterUITests.xctest"
BlueprintName = "ShorthandMasterUITests"
ReferencedContainer = "container:ShorthandMaster.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A95CDF6324E0E8B50066DAE6"
BuildableName = "&#x901f;&#x8bb0;&#x5927;&#x5e08;.app"
BlueprintName = "ShorthandMaster"
ReferencedContainer = "container:ShorthandMaster.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A95CDF6324E0E8B50066DAE6"
BuildableName = "&#x901f;&#x8bb0;&#x5927;&#x5e08;.app"
BlueprintName = "ShorthandMaster"
ReferencedContainer = "container:ShorthandMaster.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
......@@ -10,5 +10,23 @@
<integer>8</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>A95CDF6324E0E8B50066DAE6</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>A95CDF7924E0E8B80066DAE6</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>A95CDF8424E0E8B80066DAE6</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
//
// XBAACEncoder_system.h
// XBVoiceTool
//
// Created by xxb on 2018/11/29.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
///编码后的数据再回调里提供给外部
typedef void (^AACEncodeCompleteBlock)(NSData * encodedData, NSError* error);
@interface XBAACEncoder_system : NSObject
- (id)initWithInputAudioStreamDesc:(AudioStreamBasicDescription)inputAudioStreamDesc;
/**
编码pcm数据
*/
- (void)encodePCMData:(void *)pcmData len:(int)len completionBlock:(AACEncodeCompleteBlock)completionBlock;
/**
编码CMSampleBufferRef数据
*/
- (void)encodeSampleBuffer:(CMSampleBufferRef)sampleBuffer completionBlock:(AACEncodeCompleteBlock)completionBlock;
@end
//
// XBAACEncoder_system.m
// XBVoiceTool
//
// Created by xxb on 2018/11/29.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBAACEncoder_system.h"
@interface XBAACEncoder_system()
@property (nonatomic) AudioConverterRef audioConverter;
@property (nonatomic) uint8_t *aacBuffer;
@property (nonatomic) NSUInteger aacBufferSize;
@property (nonatomic) char *pcmBuffer;
@property (nonatomic) size_t pcmBufferSize;
@property (nonatomic) dispatch_queue_t encoderQueue;
@property (nonatomic) dispatch_queue_t callbackQueue;
@property (nonatomic) AudioStreamBasicDescription inputAudioStreamDesc;
@end
@implementation XBAACEncoder_system
#pragma mark - 生命周期
- (id)init
{
if (self = [super init])
{
_encoderQueue = dispatch_queue_create("AAC Encoder Queue", DISPATCH_QUEUE_SERIAL);
_callbackQueue = dispatch_queue_create("AAC Encoder Callback Queue", DISPATCH_QUEUE_SERIAL);
_audioConverter = NULL;
_pcmBufferSize = 0;
_pcmBuffer = NULL;
_aacBufferSize = 1024;
_aacBuffer = malloc(_aacBufferSize * sizeof(uint8_t));
memset(_aacBuffer, 0, _aacBufferSize);
}
return self;
}
- (id)initWithInputAudioStreamDesc:(AudioStreamBasicDescription)inputAudioStreamDesc
{
if (self = [super init])
{
_inputAudioStreamDesc = inputAudioStreamDesc;
_encoderQueue = dispatch_queue_create("AAC Encoder Queue", DISPATCH_QUEUE_SERIAL);
_callbackQueue = dispatch_queue_create("AAC Encoder Callback Queue", DISPATCH_QUEUE_SERIAL);
_audioConverter = NULL;
_pcmBufferSize = 0;
_pcmBuffer = NULL;
_aacBufferSize = 1024;
_aacBuffer = malloc(_aacBufferSize * sizeof(uint8_t));
memset(_aacBuffer, 0, _aacBufferSize);
}
return self;
}
- (void) dealloc
{
AudioConverterDispose(_audioConverter);
free(_aacBuffer);
}
#pragma mark - 编码方法
- (void)encodePCMData:(void *)pcmData len:(int)len completionBlock:(AACEncodeCompleteBlock)completionBlock
{
dispatch_async(_encoderQueue, ^{
if (!_audioConverter) {
[self setupEncoderWithInputAudioStreamDesc:self.inputAudioStreamDesc];
}
_pcmBuffer = pcmData;
_pcmBufferSize = len;
[self encoderWithCompletionBlock:completionBlock];
});
}
- (void)encodeSampleBuffer:(CMSampleBufferRef)sampleBuffer completionBlock:(AACEncodeCompleteBlock)completionBlock
{
CFRetain(sampleBuffer);
dispatch_async(_encoderQueue, ^{
if (!_audioConverter) {
[self setupEncoderFromSampleBuffer:sampleBuffer];
}
CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
CFRetain(blockBuffer);
OSStatus status = CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &_pcmBufferSize, &_pcmBuffer);
NSError *error = nil;
if (status != kCMBlockBufferNoErr) {
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
}
[self encoderWithCompletionBlock:completionBlock];
CFRelease(sampleBuffer);
CFRelease(blockBuffer);
});
}
- (void)encoderWithCompletionBlock:(AACEncodeCompleteBlock)completionBlock
{
OSStatus status = 0;
NSError *error = nil;
memset(_aacBuffer, 0, _aacBufferSize);
AudioBufferList outAudioBufferList = {0};
outAudioBufferList.mNumberBuffers = 1;
outAudioBufferList.mBuffers[0].mNumberChannels = 1;
outAudioBufferList.mBuffers[0].mDataByteSize = (int)_aacBufferSize;
outAudioBufferList.mBuffers[0].mData = _aacBuffer;
AudioStreamPacketDescription *outPacketDescription = NULL;
UInt32 ioOutputDataPacketSize = 1;
// Converts data supplied by an input callback function, supporting non-interleaved and packetized formats.
// Produces a buffer list of output data from an AudioConverter. The supplied input callback function is called whenever necessary.
status = AudioConverterFillComplexBuffer(_audioConverter, inInputDataProc, (__bridge void *)(self), &ioOutputDataPacketSize, &outAudioBufferList, outPacketDescription);
NSData *data = nil;
if (status == 0) {
NSData *rawAAC = [NSData dataWithBytes:outAudioBufferList.mBuffers[0].mData length:outAudioBufferList.mBuffers[0].mDataByteSize];
NSData *adtsHeader = [self adtsDataForPacketLength:rawAAC.length];
NSMutableData *fullData = [NSMutableData dataWithData:adtsHeader];
[fullData appendData:rawAAC];
data = fullData;
} else {
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
}
if (completionBlock) {
dispatch_async(_callbackQueue, ^{
completionBlock(data, error);
});
}
}
#pragma mark - 创建转换器
/**
* 设置编码参数
*
* @param sampleBuffer 音频
*/
- (void)setupEncoderFromSampleBuffer:(CMSampleBufferRef)sampleBuffer
{
AudioStreamBasicDescription inAudioStreamBasicDescription = *CMAudioFormatDescriptionGetStreamBasicDescription((CMAudioFormatDescriptionRef)CMSampleBufferGetFormatDescription(sampleBuffer));
[self setupEncoderWithInputAudioStreamDesc:inAudioStreamBasicDescription];
}
- (void)setupEncoderWithInputAudioStreamDesc:(AudioStreamBasicDescription)inputAudioStreamDesc
{
AudioStreamBasicDescription outAudioStreamBasicDescription = {0}; // 初始化输出流的结构体描述为0. 很重要。
outAudioStreamBasicDescription.mSampleRate = inputAudioStreamDesc.mSampleRate; // 音频流,在正常播放情况下的帧率。如果是压缩的格式,这个属性表示解压缩后的帧率。帧率不能为0。
outAudioStreamBasicDescription.mFormatID = kAudioFormatMPEG4AAC; // 设置编码格式
outAudioStreamBasicDescription.mFormatFlags = kMPEG4Object_AAC_LC; // 无损编码 ,0表示没有
outAudioStreamBasicDescription.mBytesPerPacket = 0; // 每一个packet的音频数据大小。如果的动态大小,设置为0。动态大小的格式,需要用AudioStreamPacketDescription 来确定每个packet的大小。
outAudioStreamBasicDescription.mFramesPerPacket = 1024; // 每个packet的帧数。如果是未压缩的音频数据,值是1。动态码率格式,这个值是一个较大的固定数字,比如说AAC的1024。如果是动态大小帧数(比如Ogg格式)设置为0。
outAudioStreamBasicDescription.mBytesPerFrame = 0; // 每帧的大小。每一帧的起始点到下一帧的起始点。如果是压缩格式,设置为0 。
outAudioStreamBasicDescription.mChannelsPerFrame = 1; // 声道数
outAudioStreamBasicDescription.mBitsPerChannel = 0; // 压缩格式设置为0
outAudioStreamBasicDescription.mReserved = 0; // 8字节对齐,填0.
AudioClassDescription *description = [self
getAudioClassDescriptionWithType:kAudioFormatMPEG4AAC
fromManufacturer:kAppleHardwareAudioCodecManufacturer]; //软编或者硬编
OSStatus status = AudioConverterNewSpecific(&inputAudioStreamDesc, &outAudioStreamBasicDescription, 1, description, &_audioConverter); // 创建转换器
if (status != 0) {
NSLog(@"setup converter: %d", (int)status);
}
}
/**
* 获取编解码器
*
* @param type 编码格式
* @param manufacturer 软/硬编
*
编解码器(codec)指的是一个能够对一个信号或者一个数据流进行变换的设备或者程序。这里指的变换既包括将 信号或者数据流进行编码(通常是为了传输、存储或者加密)或者提取得到一个编码流的操作,也包括为了观察或者处理从这个编码流中恢复适合观察或操作的形式的操作。编解码器经常用在视频会议和流媒体等应用中。
* @return 指定编码器
*/
- (AudioClassDescription *)getAudioClassDescriptionWithType:(UInt32)type
fromManufacturer:(UInt32)manufacturer
{
static AudioClassDescription desc;
UInt32 encoderSpecifier = type;
OSStatus st;
UInt32 size;
st = AudioFormatGetPropertyInfo(kAudioFormatProperty_Encoders,
sizeof(encoderSpecifier),
&encoderSpecifier,
&size);
if (st) {
NSLog(@"error getting audio format propery info: %d", (int)(st));
return nil;
}
unsigned int count = size / sizeof(AudioClassDescription);
AudioClassDescription descriptions[count];
st = AudioFormatGetProperty(kAudioFormatProperty_Encoders,
sizeof(encoderSpecifier),
&encoderSpecifier,
&size,
descriptions);
if (st) {
NSLog(@"error getting audio format propery: %d", (int)(st));
return nil;
}
for (unsigned int i = 0; i < count; i++) {
if ((type == descriptions[i].mSubType) &&
(manufacturer == descriptions[i].mManufacturer)) {
memcpy(&desc, &(descriptions[i]), sizeof(desc));
return &desc;
}
}
return nil;
}
#pragma mark - 拼接ADTS头
/**
* Add ADTS header at the beginning of each and every AAC packet.
* This is needed as MediaCodec encoder generates a packet of raw
* AAC data.
*
* Note the packetLen must count in the ADTS header itself.
* See: http://wiki.multimedia.cx/index.php?title=ADTS
* Also: http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Channel_Configurations
**/
- (NSData*)adtsDataForPacketLength:(NSUInteger)packetLength
{
int adtsLength = 7;
char *packet = malloc(sizeof(char) * adtsLength);
// Variables Recycled by addADTStoPacket
int profile = 2; //AAC LC
//39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
int freqIdx = 4; //44.1KHz
int chanCfg = 1; //MPEG-4 Audio Channel Configuration. 1 Channel front-center
NSUInteger fullLength = adtsLength + packetLength;
// fill in ADTS data
packet[0] = (char)0xFF; // 11111111 = syncword
packet[1] = (char)0xF9; // 1111 1 00 1 = syncword MPEG-2 Layer CRC
packet[2] = (char)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
packet[3] = (char)(((chanCfg&3)<<6) + (fullLength>>11));
packet[4] = (char)((fullLength&0x7FF) >> 3);
packet[5] = (char)(((fullLength&7)<<5) + 0x1F);
packet[6] = (char)0xFC;
NSData *data = [NSData dataWithBytesNoCopy:packet length:adtsLength freeWhenDone:YES];
return data;
}
#pragma mark - 为转换器提供输入数据的回调
/**
* A callback function that supplies audio data to convert. This callback is invoked repeatedly as the converter is ready for new input data.
*/
OSStatus inInputDataProc(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
{
XBAACEncoder_system *encoder = (__bridge XBAACEncoder_system *)(inUserData);
UInt32 requestedPackets = *ioNumberDataPackets;
size_t copiedSamples = [encoder copyPCMSamplesIntoBuffer:ioData];
if (copiedSamples < requestedPackets) {
//PCM 缓冲区还没满
*ioNumberDataPackets = 0;
return -1;
}
*ioNumberDataPackets = 1;
return noErr;
}
/**
* 填充PCM到缓冲区
*/
- (size_t)copyPCMSamplesIntoBuffer:(AudioBufferList*)ioData
{
size_t originalBufferSize = _pcmBufferSize;
if (!originalBufferSize) {
return 0;
}
ioData->mBuffers[0].mData = _pcmBuffer;
ioData->mBuffers[0].mDataByteSize = (int)_pcmBufferSize;
_pcmBuffer = NULL;
_pcmBufferSize = 0;
return originalBufferSize;
}
@end
//
// ExtAudioFileMixer.h
// XBVoiceTool
//
// Created by xxb on 2018/7/25.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface ExtAudioFileMixer : NSObject
+ (OSStatus)mixAudio:(NSString *)audioPath1
andAudio:(NSString *)audioPath2
toFile:(NSString *)outputPath
preferedSampleRate:(float)sampleRate;
@end
//
// ExtAudioFileMixer.m
// XBVoiceTool
//
// Created by xxb on 2018/7/25.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "ExtAudioFileMixer.h"
#import <AudioToolbox/AudioToolbox.h>
@implementation ExtAudioFileMixer
+ (OSStatus)mixAudio:(NSString *)audioPath1
andAudio:(NSString *)audioPath2
toFile:(NSString *)outputPath
preferedSampleRate:(float)sampleRate
{
OSStatus err = noErr;
AudioStreamBasicDescription inputFileFormat1;
AudioStreamBasicDescription inputFileFormat2;
AudioStreamBasicDescription converterFormat;
UInt32 thePropertySize = sizeof(inputFileFormat1);
ExtAudioFileRef inputAudioFileRef1 = NULL;
ExtAudioFileRef inputAudioFileRef2 = NULL;
ExtAudioFileRef outputAudioFileRef = NULL;
AudioStreamBasicDescription outputFileFormat;
NSURL *inURL1 = [NSURL fileURLWithPath:audioPath1];
NSURL *inURL2 = [NSURL fileURLWithPath:audioPath2];
NSURL *outURL = [NSURL fileURLWithPath:outputPath];
// Open input audio file
err = ExtAudioFileOpenURL((__bridge CFURLRef)inURL1, &inputAudioFileRef1);
if (err)
{
goto reterr;
}
assert(inputAudioFileRef1);
err = ExtAudioFileOpenURL((__bridge CFURLRef)inURL2, &inputAudioFileRef2);
if (err)
{
goto reterr;
}
assert(inputAudioFileRef2);
// Get input audio format
bzero(&inputFileFormat1, sizeof(inputFileFormat1));
err = ExtAudioFileGetProperty(inputAudioFileRef1, kExtAudioFileProperty_FileDataFormat,
&thePropertySize, &inputFileFormat1);
if (err)
{
goto reterr;
}
// only mono or stereo audio files are supported
if (inputFileFormat1.mChannelsPerFrame > 2)
{
err = kExtAudioFileError_InvalidDataFormat;
goto reterr;
}
bzero(&inputFileFormat2, sizeof(inputFileFormat2));
err = ExtAudioFileGetProperty(inputAudioFileRef2, kExtAudioFileProperty_FileDataFormat,
&thePropertySize, &inputFileFormat2);
if (err)
{
goto reterr;
}
// only mono or stereo audio files are supported
if (inputFileFormat2.mChannelsPerFrame > 2)
{
err = kExtAudioFileError_InvalidDataFormat;
goto reterr;
}
int numChannels = MAX(inputFileFormat1.mChannelsPerFrame, inputFileFormat2.mChannelsPerFrame);
// Enable an audio converter on the input audio data by setting
// the kExtAudioFileProperty_ClientDataFormat property. Each
// read from the input file returns data in linear pcm format.
AudioFileTypeID audioFileTypeID = kAudioFileCAFType;
Float64 mSampleRate = sampleRate? sampleRate : MAX(inputFileFormat1.mSampleRate, inputFileFormat2.mSampleRate);
[self _setDefaultAudioFormatFlags:&converterFormat sampleRate:mSampleRate numChannels:inputFileFormat1.mChannelsPerFrame];
err = ExtAudioFileSetProperty(inputAudioFileRef1, kExtAudioFileProperty_ClientDataFormat,
sizeof(converterFormat), &converterFormat);
if (err)
{
goto reterr;
}
[self _setDefaultAudioFormatFlags:&converterFormat sampleRate:mSampleRate numChannels:inputFileFormat2.mChannelsPerFrame];
err = ExtAudioFileSetProperty(inputAudioFileRef2, kExtAudioFileProperty_ClientDataFormat,
sizeof(converterFormat), &converterFormat);
if (err)
{
goto reterr;
}
// Handle the case of reading from a mono input file and writing to a stereo
// output file by setting up a channel map. The mono output is duplicated
// in the left and right channel.
if (inputFileFormat1.mChannelsPerFrame == 1 && numChannels == 2) {
SInt32 channelMap[2] = { 0, 0 };
// Get the underlying AudioConverterRef
AudioConverterRef convRef = NULL;
UInt32 size = sizeof(AudioConverterRef);
err = ExtAudioFileGetProperty(inputAudioFileRef1, kExtAudioFileProperty_AudioConverter, &size, &convRef);
if (err)
{
goto reterr;
}
assert(convRef);
err = AudioConverterSetProperty(convRef, kAudioConverterChannelMap, sizeof(channelMap), channelMap);
if (err)
{
goto reterr;
}
}
if (inputFileFormat2.mChannelsPerFrame == 1 && numChannels == 2) {
SInt32 channelMap[2] = { 0, 0 };
// Get the underlying AudioConverterRef
AudioConverterRef convRef = NULL;
UInt32 size = sizeof(AudioConverterRef);
err = ExtAudioFileGetProperty(inputAudioFileRef2, kExtAudioFileProperty_AudioConverter, &size, &convRef);
if (err)
{
goto reterr;
}
assert(convRef);
err = AudioConverterSetProperty(convRef, kAudioConverterChannelMap, sizeof(channelMap), channelMap);
if (err)
{
goto reterr;
}
}
// Output file is typically a caff file, but the user could emit some other
// common file types. If a file exists already, it is deleted before writing
// the new audio file.
[self _setDefaultAudioFormatFlags:&outputFileFormat sampleRate:mSampleRate numChannels:numChannels];
UInt32 flags = kAudioFileFlags_EraseFile;
err = ExtAudioFileCreateWithURL((__bridge CFURLRef)outURL, audioFileTypeID, &outputFileFormat,
NULL, flags, &outputAudioFileRef);
if (err)
{
// -48 means the file exists already
goto reterr;
}
assert(outputAudioFileRef);
// Enable converter when writing to the output file by setting the client
// data format to the pcm converter we created earlier.
err = ExtAudioFileSetProperty(outputAudioFileRef, kExtAudioFileProperty_ClientDataFormat,
sizeof(outputFileFormat), &outputFileFormat);
if (err)
{
goto reterr;
}
// Buffer to read from source file and write to dest file
UInt16 bufferSize = 8192;
AudioSampleType * buffer1 = malloc(bufferSize);
AudioSampleType * buffer2 = malloc(bufferSize);
AudioSampleType * outBuffer = malloc(bufferSize);
AudioBufferList conversionBuffer1;
conversionBuffer1.mNumberBuffers = 1;
conversionBuffer1.mBuffers[0].mNumberChannels = inputFileFormat1.mChannelsPerFrame;
conversionBuffer1.mBuffers[0].mDataByteSize = bufferSize;
conversionBuffer1.mBuffers[0].mData = buffer1;
AudioBufferList conversionBuffer2;
conversionBuffer2.mNumberBuffers = 1;
conversionBuffer2.mBuffers[0].mNumberChannels = inputFileFormat2.mChannelsPerFrame;
conversionBuffer2.mBuffers[0].mDataByteSize = bufferSize;
conversionBuffer2.mBuffers[0].mData = buffer2;
//
AudioBufferList outBufferList;
outBufferList.mNumberBuffers = 1;
outBufferList.mBuffers[0].mNumberChannels = outputFileFormat.mChannelsPerFrame;
outBufferList.mBuffers[0].mDataByteSize = bufferSize;
outBufferList.mBuffers[0].mData = outBuffer;
UInt32 numFramesToReadPerTime = INT_MAX;
UInt8 bitOffset = 8 * sizeof(AudioSampleType);
UInt64 bitMax = (UInt64) (pow(2, bitOffset));
UInt64 bitMid = bitMax/2;
while (TRUE) {
conversionBuffer1.mBuffers[0].mDataByteSize = bufferSize;
conversionBuffer2.mBuffers[0].mDataByteSize = bufferSize;
outBufferList.mBuffers[0].mDataByteSize = bufferSize;
UInt32 frameCount1 = numFramesToReadPerTime;
UInt32 frameCount2 = numFramesToReadPerTime;
if (inputFileFormat1.mBytesPerFrame)
{
frameCount1 = bufferSize/inputFileFormat1.mBytesPerFrame;
}
if (inputFileFormat2.mBytesPerFrame)
{
frameCount2 = bufferSize/inputFileFormat2.mBytesPerFrame;
}
// Read a chunk of input
err = ExtAudioFileRead(inputAudioFileRef1, &frameCount1, &conversionBuffer1);
if (err) {
goto reterr;
}
err = ExtAudioFileRead(inputAudioFileRef2, &frameCount2, &conversionBuffer2);
if (err) {
goto reterr;
}
// If no frames were returned, conversion is finished
if (frameCount1 == 0 && frameCount2 == 0)
break;
UInt32 frameCount = MAX(frameCount1, frameCount2);
UInt32 minFrames = MIN(frameCount1, frameCount2);
outBufferList.mBuffers[0].mDataByteSize = frameCount * outputFileFormat.mBytesPerFrame;
UInt32 length = frameCount * 2;
for (int j =0; j < length; j++)
{
if (j/2 < minFrames)
{
SInt32 sValue =0;
SInt16 value1 = (SInt16)*(buffer1+j); //-32768 ~ 32767
SInt16 value2 = (SInt16)*(buffer2+j); //-32768 ~ 32767
SInt8 sign1 = (value1 == 0)? 0 : abs(value1)/value1;
SInt8 sign2 = (value2 == 0)? 0 : abs(value2)/value2;
if (sign1 == sign2)
{
UInt32 tmp = ((value1 * value2) >> (bitOffset -1));
sValue = value1 + value2 - sign1 * tmp;
if (abs(sValue) >= bitMid)
{
sValue = sign1 * (bitMid - 1);
}
}
else
{
SInt32 tmpValue1 = value1 + bitMid;
SInt32 tmpValue2 = value2 + bitMid;
UInt32 tmp = ((tmpValue1 * tmpValue2) >> (bitOffset -1));
if (tmpValue1 < bitMid && tmpValue2 < bitMid)
{
sValue = tmp;
}
else
{
sValue = 2 * (tmpValue1 + tmpValue2 ) - tmp - bitMax;
}
sValue -= bitMid;
}
if (abs(sValue) >= bitMid)
{
SInt8 sign = abs(sValue)/sValue;
sValue = sign * (bitMid - 1);
}
*(outBuffer +j) = sValue;
}
else{
if (frameCount == frameCount1)
{
//将buffer1中的剩余数据添加到outbuffer
*(outBuffer +j) = *(buffer1 + j);
}
else
{
//将buffer1中的剩余数据添加到outbuffer
*(outBuffer +j) = *(buffer2 + j);
}
}
}
// Write pcm data to output file
NSLog(@"frame count (%ld, %ld, %ld)", frameCount, frameCount1, frameCount2);
err = ExtAudioFileWrite(outputAudioFileRef, frameCount, &outBufferList);
if (err) {
goto reterr;
}
}
reterr:
if (buffer1)
free(buffer1);
if (buffer2)
free(buffer2);
if (outBuffer)
free(outBuffer);
if (inputAudioFileRef1)
ExtAudioFileDispose(inputAudioFileRef1);
if (inputAudioFileRef2)
ExtAudioFileDispose(inputAudioFileRef2);
if (outputAudioFileRef)
ExtAudioFileDispose(outputAudioFileRef);
return err;
}
// Set flags for default audio format on iPhone OS
+ (void) _setDefaultAudioFormatFlags:(AudioStreamBasicDescription*)audioFormatPtr
sampleRate:(Float64)sampleRate
numChannels:(NSUInteger)numChannels
{
bzero(audioFormatPtr, sizeof(AudioStreamBasicDescription));
audioFormatPtr->mFormatID = kAudioFormatLinearPCM;
audioFormatPtr->mSampleRate = sampleRate;
audioFormatPtr->mChannelsPerFrame = numChannels;
audioFormatPtr->mBytesPerPacket = 2 * numChannels;
audioFormatPtr->mFramesPerPacket = 1;
audioFormatPtr->mBytesPerFrame = 2 * numChannels;
audioFormatPtr->mBitsPerChannel = 16;
audioFormatPtr->mFormatFlags = kAudioFormatFlagsNativeEndian |
kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
}
@end
//
// Header_audio.h
// XBVoiceTool
//
// Created by xxb on 2018/6/27.
// Copyright © 2018年 xxb. All rights reserved.
//
#ifndef Header_audio_h
#define Header_audio_h
#define kInputBus (1)
#define kOutputBus (0)
#define NO_MORE_DATA (-12306)
#define kSmapleRate (XBAudioRate_44k * 1.0)
#define kFramesPerPacket (1)
#define kChannelsPerFrame (2)
#define kBitsPerChannel (32) //s16
#import <AudioUnit/AudioUnit.h>
#import <AVFoundation/AVFoundation.h>
#import "Header_audio.h"
#import <assert.h>
#define kPreferredIOBufferDuration (0.05)
#define CONST_BUFFER_SIZE (0x10000)
typedef enum : NSUInteger {
XBAudioRate_8k = 8000,
XBAudioRate_16k = 16000,
XBAudioRate_20k = 20000,
XBAudioRate_44k = 44100,
XBAudioRate_96k = 96000
} XBAudioRate;
typedef enum : NSUInteger {
XBAudioBit_8 = 8,
XBAudioBit_16 = 16,
XBAudioBit_32 = 32,
XBAudioBit_128 = 128,
} XBAudioBit;
typedef enum : NSUInteger {
XBAudioChannel_1 = 1,
XBAudioChannel_2 = 2,
} XBAudioChannel;
typedef enum : NSUInteger {
XBEchoCancellationStatus_open,
XBEchoCancellationStatus_close
} XBEchoCancellationStatus;
struct XBAudioBuffer {
AudioStreamBasicDescription asbd;
Float32 *leftData;//左声道数据
Float32 *rightData;//右声道数据
UInt64 totalFrames;//总帧数
UInt64 startFrame;//从第几帧开始读
UInt32 channelCount; //声音是单声道还是立体声
};
typedef struct XBAudioBuffer XBAudioBuffer;
typedef enum : NSUInteger {
XBAudioFormatFlags_Float = kAudioFormatFlagIsFloat,
XBAudioFormatFlags_BigEndian = kAudioFormatFlagIsBigEndian,
XBAudioFormatFlags_SignedInteger = kAudioFormatFlagIsSignedInteger,
XBAudioFormatFlags_Packed = kAudioFormatFlagIsPacked,
XBAudioFormatFlags_AlignedHigh = kAudioFormatFlagIsAlignedHigh,
XBAudioFormatFlags_NonInterleaved = kAudioFormatFlagIsNonInterleaved,
XBAudioFormatFlags_NonMixable = kAudioFormatFlagIsNonMixable,
XBAudioFormatFlags_AreAllClear = kAudioFormatFlagsAreAllClear,
} XBAudioFormatFlags;
/*
CF_ENUM(AudioFormatFlags)
{
kAudioFormatFlagIsFloat = (1U << 0), // 0x1
kAudioFormatFlagIsBigEndian = (1U << 1), // 0x2
kAudioFormatFlagIsSignedInteger = (1U << 2), // 0x4
kAudioFormatFlagIsPacked = (1U << 3), // 0x8
kAudioFormatFlagIsAlignedHigh = (1U << 4), // 0x10
kAudioFormatFlagIsNonInterleaved = (1U << 5), // 0x20
kAudioFormatFlagIsNonMixable = (1U << 6), // 0x40
kAudioFormatFlagsAreAllClear = 0x80000000,
kLinearPCMFormatFlagIsFloat = kAudioFormatFlagIsFloat,
kLinearPCMFormatFlagIsBigEndian = kAudioFormatFlagIsBigEndian,
kLinearPCMFormatFlagIsSignedInteger = kAudioFormatFlagIsSignedInteger,
kLinearPCMFormatFlagIsPacked = kAudioFormatFlagIsPacked,
kLinearPCMFormatFlagIsAlignedHigh = kAudioFormatFlagIsAlignedHigh,
kLinearPCMFormatFlagIsNonInterleaved = kAudioFormatFlagIsNonInterleaved,
kLinearPCMFormatFlagIsNonMixable = kAudioFormatFlagIsNonMixable,
kLinearPCMFormatFlagsSampleFractionShift = 7,
kLinearPCMFormatFlagsSampleFractionMask = (0x3F << kLinearPCMFormatFlagsSampleFractionShift),
kLinearPCMFormatFlagsAreAllClear = kAudioFormatFlagsAreAllClear,
kAppleLosslessFormatFlag_16BitSourceData = 1,
kAppleLosslessFormatFlag_20BitSourceData = 2,
kAppleLosslessFormatFlag_24BitSourceData = 3,
kAppleLosslessFormatFlag_32BitSourceData = 4
};
*/
typedef enum : NSUInteger {
XBAudioFormatID_PCM = kAudioFormatLinearPCM,
} XBAudioFormatID;
/*
CF_ENUM(AudioFormatID)
{
kAudioFormatLinearPCM = 'lpcm',
kAudioFormatAC3 = 'ac-3',
kAudioFormat60958AC3 = 'cac3',
kAudioFormatAppleIMA4 = 'ima4',
kAudioFormatMPEG4AAC = 'aac ',
kAudioFormatMPEG4CELP = 'celp',
kAudioFormatMPEG4HVXC = 'hvxc',
kAudioFormatMPEG4TwinVQ = 'twvq',
kAudioFormatMACE3 = 'MAC3',
kAudioFormatMACE6 = 'MAC6',
kAudioFormatULaw = 'ulaw',
kAudioFormatALaw = 'alaw',
kAudioFormatQDesign = 'QDMC',
kAudioFormatQDesign2 = 'QDM2',
kAudioFormatQUALCOMM = 'Qclp',
kAudioFormatMPEGLayer1 = '.mp1',
kAudioFormatMPEGLayer2 = '.mp2',
kAudioFormatMPEGLayer3 = '.mp3',
kAudioFormatTimeCode = 'time',
kAudioFormatMIDIStream = 'midi',
kAudioFormatParameterValueStream = 'apvs',
kAudioFormatAppleLossless = 'alac',
kAudioFormatMPEG4AAC_HE = 'aach',
kAudioFormatMPEG4AAC_LD = 'aacl',
kAudioFormatMPEG4AAC_ELD = 'aace',
kAudioFormatMPEG4AAC_ELD_SBR = 'aacf',
kAudioFormatMPEG4AAC_ELD_V2 = 'aacg',
kAudioFormatMPEG4AAC_HE_V2 = 'aacp',
kAudioFormatMPEG4AAC_Spatial = 'aacs',
kAudioFormatAMR = 'samr',
kAudioFormatAMR_WB = 'sawb',
kAudioFormatAudible = 'AUDB',
kAudioFormatiLBC = 'ilbc',
kAudioFormatDVIIntelIMA = 0x6D730011,
kAudioFormatMicrosoftGSM = 0x6D730031,
kAudioFormatAES3 = 'aes3',
kAudioFormatEnhancedAC3 = 'ec-3',
kAudioFormatFLAC = 'flac',
kAudioFormatOpus = 'opus'
};
*/
typedef enum : NSUInteger {
XBAudioUnitPropertyID_callback_input = kAudioUnitProperty_SetRenderCallback, //设置输入回调(在回调里,是向ioData传递数据的)
XBAudioUnitPropertyID_callback_output = kAudioOutputUnitProperty_SetInputCallback, //设置输出回调(在回调里,是用AudioUnitRender获取数据)
XBAudioUnitPropertyID_StreamFormat = kAudioUnitProperty_StreamFormat, //设置数据流格式
XBAudioUnitPropertyID_ElementCount = kAudioUnitProperty_ElementCount, //设置节点个数
} XBAudioUnitPropertyID;
/*
CF_ENUM(AudioUnitPropertyID)
{
// range (0 -> 999)
kAudioUnitProperty_ClassInfo = 0,
kAudioUnitProperty_MakeConnection = 1,
kAudioUnitProperty_SampleRate = 2,
kAudioUnitProperty_ParameterList = 3,
kAudioUnitProperty_ParameterInfo = 4,
kAudioUnitProperty_CPULoad = 6,
kAudioUnitProperty_StreamFormat = 8,
kAudioUnitProperty_ElementCount = 11,
kAudioUnitProperty_Latency = 12,
kAudioUnitProperty_SupportedNumChannels = 13,
kAudioUnitProperty_MaximumFramesPerSlice = 14,
kAudioUnitProperty_ParameterValueStrings = 16,
kAudioUnitProperty_AudioChannelLayout = 19,
kAudioUnitProperty_TailTime = 20,
kAudioUnitProperty_BypassEffect = 21,
kAudioUnitProperty_LastRenderError = 22,
kAudioUnitProperty_SetRenderCallback = 23,
kAudioUnitProperty_FactoryPresets = 24,
kAudioUnitProperty_RenderQuality = 26,
kAudioUnitProperty_HostCallbacks = 27,
kAudioUnitProperty_InPlaceProcessing = 29,
kAudioUnitProperty_ElementName = 30,
kAudioUnitProperty_SupportedChannelLayoutTags = 32,
kAudioUnitProperty_PresentPreset = 36,
kAudioUnitProperty_DependentParameters = 45,
kAudioUnitProperty_InputSamplesInOutput = 49,
kAudioUnitProperty_ShouldAllocateBuffer = 51,
kAudioUnitProperty_FrequencyResponse = 52,
kAudioUnitProperty_ParameterHistoryInfo = 53,
kAudioUnitProperty_NickName = 54,
kAudioUnitProperty_OfflineRender = 37,
kAudioUnitProperty_ParameterIDName = 34,
kAudioUnitProperty_ParameterStringFromValue = 33,
kAudioUnitProperty_ParameterClumpName = 35,
kAudioUnitProperty_ParameterValueFromString = 38,
kAudioUnitProperty_ContextName = 25,
kAudioUnitProperty_PresentationLatency = 40,
kAudioUnitProperty_ClassInfoFromDocument = 50,
kAudioUnitProperty_RequestViewController = 56,
kAudioUnitProperty_ParametersForOverview = 57,
kAudioUnitProperty_SupportsMPE = 58,
#if !TARGET_OS_IPHONE
kAudioUnitProperty_FastDispatch = 5,
kAudioUnitProperty_SetExternalBuffer = 15,
kAudioUnitProperty_GetUIComponentList = 18,
kAudioUnitProperty_CocoaUI = 31,
kAudioUnitProperty_IconLocation = 39,
kAudioUnitProperty_AUHostIdentifier = 46,
#endif
kAudioUnitProperty_MIDIOutputCallbackInfo = 47,
kAudioUnitProperty_MIDIOutputCallback = 48,
};
CF_ENUM(AudioUnitPropertyID) {
kAudioOutputUnitProperty_CurrentDevice = 2000,
kAudioOutputUnitProperty_ChannelMap = 2002, // this will also work with AUConverter
kAudioOutputUnitProperty_EnableIO = 2003,
kAudioOutputUnitProperty_StartTime = 2004,
kAudioOutputUnitProperty_SetInputCallback = 2005,
kAudioOutputUnitProperty_HasIO = 2006,
kAudioOutputUnitProperty_StartTimestampsAtZero = 2007 // this will also work with AUConverter
};
*/
static void CheckError(OSStatus error, const char *operation)
{
if (error == noErr) return;
char errorString[20];
// See if it appears to be a 4-char-code
*(UInt32 *)(errorString + 1) = CFSwapInt32HostToBig(error);
if (isprint(errorString[1]) && isprint(errorString[2]) &&
isprint(errorString[3]) && isprint(errorString[4])) {
errorString[0] = errorString[5] = '\'';
errorString[6] = '\0';
} else
// No, format it as an integer
sprintf(errorString, "%d", (int)error);
fprintf(stderr, "Error: %s (%s)\n", operation, errorString);
exit(1);
}
#endif /* Header_audio_h */
//
// MP3Encoder.h
// XBVoiceTool
//
// Created by xxb on 2018/11/29.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#include "lame.h"
///编码后的数据再回调里提供给外部
typedef void (^MP3EncodeCompleteBlock)(unsigned char * encodedData, int len);
@interface MP3Encoder : NSObject
- (id)initWithSampleRate:(int)sampleRate channels:(int)channels bitRate:(int)bitRate;
- (void)encodePCMData:(void *)pcmData len:(int)len completeBlock:(MP3EncodeCompleteBlock)completeBlock;
@end
//
// MP3Encoder.m
// XBVoiceTool
//
// Created by xxb on 2018/11/29.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "MP3Encoder.h"
@interface MP3Encoder ()
{
lame_t lameClient;
}
@end
@implementation MP3Encoder
- (id)initWithSampleRate:(int)sampleRate channels:(int)channels bitRate:(int)bitRate
{
if (self = [super init])
{
lameClient = lame_init();
lame_set_in_samplerate(lameClient, sampleRate);
// lame_set_out_samplerate(lameClient, sampleRate);
// lame_set_mode(lameClient, 1);
lame_set_num_channels(lameClient, channels);
lame_set_brate(lameClient, bitRate);
// lame_set_quality(lameClient, 2);
lame_init_params(lameClient);
}
return self;
}
- (void)dealloc
{
if (lameClient)
{
lame_close(lameClient);
}
}
- (void)encodePCMData:(void *)pcmData len:(int)len completeBlock:(MP3EncodeCompleteBlock)completeBlock
{
int mp3DataSize = len;
unsigned char mp3Buffer[mp3DataSize];
/**
这里的len / 2,是因为我们录音数据是char *类型的,一个char占一个字节。而这里要传的数据是short *类型的,一个short占2个字节
lame_encode_buffer //录音数据单声道16位整形用这个方法
lame_encode_buffer_interleaved //录音数据双声道交错用这个方法
lame_encode_buffer_float //录音数据采样深度32位浮点型用这个方法
*/
int encodedBytes = lame_encode_buffer(lameClient, pcmData, pcmData, len / 2, mp3Buffer, mp3DataSize);
if (completeBlock)
{
completeBlock(mp3Buffer,encodedBytes);
}
}
@end
//
// XBAudioConverterPlayer.h
// XBVoiceTool
//
// Created by xxb on 2018/7/5.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface XBAudioConverterPlayer : NSObject
@property (nonatomic,assign) BOOL isPlaying;
- (instancetype)initWithFilePath:(NSString *)filePath;
- (void)play;
- (void)stop;
@end
//
// XBAudioConverterPlayer.m
// XBVoiceTool
//
// Created by xxb on 2018/7/5.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBAudioConverterPlayer.h"
#import "XBAudioUnitPlayer.h"
#import "XBAudioTool.h"
@interface XBAudioConverterPlayer ()
{
AudioFileID audioFileID;
AudioStreamBasicDescription audioFileFormat;
AudioStreamPacketDescription *audioPacketFormat;
UInt64 packetNums;
SInt64 readedPacket; // 已读的packet数量
AudioBufferList *buffList;
Byte *convertBuffer;
AudioConverterRef audioConverter;
}
@property (nonatomic,strong) XBAudioUnitPlayer *player;
@end
@implementation XBAudioConverterPlayer
- (instancetype)initWithFilePath:(NSString *)filePath
{
if (self = [super init])
{
[XBAudioTool getAudioPropertyWithFilepath:filePath completeBlock:^(AudioFileID audioFileIDT, AudioStreamBasicDescription audioFileFormatT, UInt64 packetNumsT, UInt64 maxFramesPerPacketT ,UInt64 fileLengthFrames) {
audioConverter = NULL;
audioFileID = audioFileIDT;
audioFileFormat = audioFileFormatT;
packetNums = packetNumsT;
readedPacket = 0;
audioPacketFormat = malloc(sizeof(AudioStreamPacketDescription) * (CONST_BUFFER_SIZE / maxFramesPerPacketT + 1));
buffList = [XBAudioTool allocAudioBufferListWithMDataByteSize:CONST_BUFFER_SIZE mNumberChannels:1 mNumberBuffers:1];
convertBuffer = malloc(CONST_BUFFER_SIZE);
int mFramesPerPacket = 1;
int mBitsPerChannel = 32;
int mChannelsPerFrame = 1;
//输出格式
AudioStreamBasicDescription outputFormat = [XBAudioTool allocAudioStreamBasicDescriptionWithMFormatID:kAudioFormatLinearPCM mFormatFlags:(kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsNonInterleaved) mSampleRate:44100 mFramesPerPacket:mFramesPerPacket mChannelsPerFrame:mChannelsPerFrame mBitsPerChannel:mBitsPerChannel];
[XBAudioTool printAudioStreamBasicDescription:audioFileFormat];
[XBAudioTool printAudioStreamBasicDescription:outputFormat];
CheckError(AudioConverterNew(&audioFileFormat, &outputFormat, &audioConverter), "AudioConverterNew eror");
self.player = [[XBAudioUnitPlayer alloc] initWithRate:outputFormat.mSampleRate bit:outputFormat.mBitsPerChannel channel:outputFormat.mChannelsPerFrame];
} errorBlock:^(NSError *error) {
NSLog(@"%@",error);
}];
}
return self;
}
- (void)dealloc
{
NSLog(@"XBPCMPlayer销毁");
[self stop];
free(convertBuffer);
}
- (void)play
{
if (self.player)
{
if (self.player.bl_input == nil)
{
typeof(self) __weak weakSelf = self;
typeof(weakSelf) __strong strongSelf = weakSelf;
self.player.bl_inputFull = ^(XBAudioUnitPlayer *player, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) {
strongSelf->buffList->mBuffers[0].mDataByteSize = CONST_BUFFER_SIZE;
OSStatus status = AudioConverterFillComplexBuffer(strongSelf->audioConverter, lyInInputDataProc, (__bridge void * _Nullable)(strongSelf), &inNumberFrames, strongSelf->buffList, NULL);
if (status) {
NSLog(@"转换格式失败 %d", status);
}
// NSLog(@"out size: %d", strongSelf->buffList->mBuffers[0].mDataByteSize);
memcpy(ioData->mBuffers[0].mData, strongSelf->buffList->mBuffers[0].mData, strongSelf->buffList->mBuffers[0].mDataByteSize);
ioData->mBuffers[0].mDataByteSize = strongSelf->buffList->mBuffers[0].mDataByteSize;
if (strongSelf->buffList->mBuffers[0].mDataByteSize <= 0) {
dispatch_async(dispatch_get_main_queue(), ^{
// [weakSelf stop];
});
}
};
}
[self.player start];
self.isPlaying = YES;
}
}
- (void)stop
{
self.player.bl_input = nil;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kPreferredIOBufferDuration*0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.player stop];
self.isPlaying = NO;
});
}
OSStatus lyInInputDataProc(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
{
XBAudioConverterPlayer *player = (__bridge XBAudioConverterPlayer *)(inUserData);
UInt32 byteSize = CONST_BUFFER_SIZE;
OSStatus status = AudioFileReadPacketData(player->audioFileID, NO, &byteSize, player->audioPacketFormat, player->readedPacket, ioNumberDataPackets, player->convertBuffer);
if (outDataPacketDescription) { // 这里要设置好packetFormat,否则会转码失败
*outDataPacketDescription = player->audioPacketFormat;
}
if(status) {
NSLog(@"读取文件失败");
}
if (!status && ioNumberDataPackets > 0) {
ioData->mBuffers[0].mDataByteSize = byteSize;
ioData->mBuffers[0].mData = player->convertBuffer;
player->readedPacket += *ioNumberDataPackets;
return noErr;
}
else {
return NO_MORE_DATA;
}
}
- (double)getCurrentProgress
{
Float64 timeInterval = (readedPacket * 1.0) / packetNums;
return timeInterval;
}
@end
//
// XBAudioDataBuffer.h
// smanos
//
// Created by xxb on 2018/9/7.
// Copyright © 2018年 sven. All rights reserved.
// 缓冲池,用于接受网络数据
#import <Foundation/Foundation.h>
@interface XBAudioDataBuffer : NSObject
/**
bufferSize : 缓冲池最大长度
*/
- (instancetype)initWithBufferSize:(int)bufferSize;
/** 写入数据
data : 要写入到缓冲池的数据
len : 要写入到数据的长度
*/
- (int)writeData:(void *)data len:(int)len;
/** 读取数据
len : 要读取多长的数据
data : 读取的数据提供给谁
*/
- (int)readLen:(int)len toData:(void *)data;
- (void)clearData;
/** 读取数据
获取当前已经缓冲好的长度
*/
- (int)availableLen;
@end
//
// XBAudioDataBuffer.m
// smanos
//
// Created by xxb on 2018/9/7.
// Copyright © 2018年 sven. All rights reserved.
//
#import "XBAudioDataBuffer.h"
@interface XBAudioDataBuffer ()
{
NSLock *_lock;//锁
char *_dataBuffer;//缓冲池
int _availableLen;//可用数据的长度
int _buf_size;//记录XBAudioDataBuffer的最大长度
int _w_pos;//写入数据的偏移地址
int _r_pos;//读取数据的偏移地址
}
@end
@implementation XBAudioDataBuffer
- (instancetype)init
{
if (self = [super init])
{
_lock = [NSLock new];
_buf_size = 1024 * 1024;
_dataBuffer = (char *)malloc(_buf_size * sizeof(char));
}
return self;
}
/**
bufferSize : 缓冲池最大长度
*/
- (instancetype)initWithBufferSize:(int)bufferSize
{
if (self = [super init])
{
_lock = [NSLock new];
_buf_size = bufferSize;
_dataBuffer = (char *)malloc(bufferSize * sizeof(char));
}
return self;
}
/** 读取数据
获取当前已经缓冲好的长度
*/
- (int)availableLen
{
return _availableLen;
}
/** 写入数据
data : 要写入到缓冲池的数据
len : 要写入到数据的长度
*/
- (int)writeData:(void *)data len:(int)len
{
if(len+_availableLen > _buf_size)
{
printf("Data len is more than buffer size!\n");
return 0;
}
[_lock lock];
if((_w_pos+len) <= _buf_size)
{
memcpy(_dataBuffer+_w_pos,data,len);
_w_pos += len;
}
else
{
int len1 = _buf_size - _w_pos;
memcpy(_dataBuffer+_w_pos,data,len1);
memcpy(_dataBuffer,data+len1,len-len1);
_w_pos = len-len1;
}
_availableLen += len;
[_lock unlock];
return len;
}
/** 读取数据
len : 要读取多长的数据
data : 读取的数据提供给谁
*/
- (int)readLen:(int)len toData:(void *)data
{
if(_availableLen < len)
{
return 0;
}
[_lock lock];
if((_r_pos+len) <= _buf_size)
{
memcpy(data,_dataBuffer+_r_pos,len);
_r_pos += len;
}
else
{
int len1 = _buf_size - _r_pos;
memcpy(data,_dataBuffer+_r_pos,len1);
memcpy(data+len1,_dataBuffer,len-len1);
_r_pos = len-len1;
}
_availableLen -= len;
[_lock unlock];
return len;
}
- (void)dealloc
{
[self free];
}
- (void)free
{
free(_dataBuffer);
}
- (void)clearData
{
_r_pos = 0;
_w_pos = 0;
_availableLen = 0;
}
@end
//
// XBAudioFileDataReader.h
// XBVoiceTool
//
// Created by xxb on 2018/7/23.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Header_audio.h"
typedef void (^XBAudioFileDataReaderLoadFileToMemoryCompleteBlock)(XBAudioBuffer *xbBufferList);
@interface XBAudioFileDataReader : NSObject
///yes:已经把歌曲载入内存了
@property (nonatomic,assign,readonly) BOOL endLoadFileToMemory;
/**
获取载入到内存以后的数据
返回nil,说明没有载入完
*/
- (XBAudioBuffer *)getBufferList;
///数组长度
- (NSInteger)getBufferListLength;
///载入歌曲到内存
- (void)loadFileToMemoryWithFilePathArr:(NSArray *)filePathArr;
///载入歌曲到内存
- (void)loadFileToMemoryWithFilePathArr:(NSArray *)filePathArr completeBlock:(XBAudioFileDataReaderLoadFileToMemoryCompleteBlock)completeBlock;
@end
//
// XBAudioFileDataReader.m
// XBVoiceTool
//
// Created by xxb on 2018/7/23.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBAudioFileDataReader.h"
#import "XBAudioTool.h"
@interface XBAudioFileDataReader ()
{
XBAudioBuffer *_bufferList;
NSInteger _bufferListLength;
}
@end
@implementation XBAudioFileDataReader
- (XBAudioBuffer *)getBufferList
{
if (_endLoadFileToMemory)
{
return _bufferList;
}
return nil;
}
- (NSInteger)getBufferListLength
{
return _bufferListLength;
}
///载入歌曲到内存
- (void)loadFileToMemoryWithFilePathArr:(NSArray *)filePathArr
{
[self loadFileToMemoryWithFilePathArr:filePathArr completeBlock:nil];
}
- (void)loadFileToMemoryWithFilePathArr:(NSArray *)filePathArr completeBlock:(XBAudioFileDataReaderLoadFileToMemoryCompleteBlock)completeBlock
{
_bufferListLength = filePathArr.count;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
XBAudioBuffer *bufferList = (XBAudioBuffer *)malloc(sizeof(XBAudioBuffer) * filePathArr.count);
// XBAudioBufferList bufferList = (XBAudioBufferList)malloc(sizeof(XBAudioBuffer) * filePathArr.count);
for (NSString *filePath in filePathArr)
{
NSInteger i = [filePathArr indexOfObject:filePath];
__block float rateRate = 1;
__block UInt32 channel = 1;
[XBAudioTool getAudioPropertyWithFilepath:filePath completeBlock:^(AudioFileID audioFileID, AudioStreamBasicDescription audioFileFormat, UInt64 packetNums, UInt64 maxFramesPerPacket, UInt64 fileLengthFrames) {
rateRate = kSmapleRate * 1.0 / audioFileFormat.mSampleRate;
if (audioFileFormat.mChannelsPerFrame == 2)
{
channel = 2;
}
} errorBlock:^(NSError *error) {
}];
//NO表示如果是双声道数据,两个声道的数据分别在不同的数组里
AVAudioFormat *clientFormat = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
sampleRate:kSmapleRate
channels:channel
interleaved:NO];
UInt32 size = sizeof(AudioStreamBasicDescription);
ExtAudioFileRef fp;
NSURL *url = [NSURL fileURLWithPath:filePath];
CheckError(ExtAudioFileOpenURL((__bridge CFURLRef _Nonnull)(url), &fp), "cant open the file");
//设置从文件中读出的音频格式
CheckError(ExtAudioFileSetProperty(fp, kExtAudioFileProperty_ClientDataFormat,
size, clientFormat.streamDescription),
"cant set the file output format");
//获取总帧数,乘以rateRate,是为了获取正确的帧数,因为这里设置输出的格式,rate为kSmapleRate
UInt64 numFrames = 0;
size = sizeof(numFrames);
CheckError(ExtAudioFileGetProperty(fp, kExtAudioFileProperty_FileLengthFrames,
&size, &numFrames),
"cant get the fileLengthFrames");
numFrames = numFrames * rateRate;
//设置bufferList
bufferList[i].totalFrames = numFrames;
bufferList[i].asbd = *(clientFormat.streamDescription);
bufferList[i].channelCount = channel;
bufferList[i].startFrame = 0;
bufferList[i].leftData = (Float32 *)malloc(numFrames * sizeof(Float32));
if (channel == 2)
{
bufferList[i].rightData = (Float32 *)malloc(numFrames * sizeof(Float32));
}
//把数据读取到bufferList的leftData和rightData中
AudioBufferList *bufList = (AudioBufferList *)malloc(sizeof(AudioBufferList) + (channel - 1) * sizeof(AudioBuffer));
AudioBuffer emptyBuffer = {0};
for (int j = 0; j < channel; j++) {
bufList->mBuffers[j] = emptyBuffer;
}
bufList->mNumberBuffers = channel;
bufList->mBuffers[0].mNumberChannels = 1;
bufList->mBuffers[0].mData = bufferList[i].leftData;
bufList->mBuffers[0].mDataByteSize = (UInt32)numFrames*sizeof(Float32);
if (2 == channel) {
bufList->mBuffers[1].mNumberChannels = 1;
bufList->mBuffers[1].mDataByteSize = (UInt32)numFrames*sizeof(Float32);
bufList->mBuffers[1].mData = bufferList[i].rightData;
}
UInt32 numberOfPacketsToRead = (UInt32) numFrames;
CheckError(ExtAudioFileRead(fp,
&numberOfPacketsToRead,
bufList),
"cant read the audio file");
free(bufList);
ExtAudioFileDispose(fp);
}
_bufferList = bufferList;
dispatch_async(dispatch_get_main_queue(), ^{
if (completeBlock)
{
completeBlock(bufferList);
}
_endLoadFileToMemory = YES;
});
});
}
@end
//
// XBAudioFormatConversion.h
// XBVoiceTool
//
// Created by xxb on 2018/6/27.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Header_audio.h"
@interface XBAudioFormatConversion : NSObject
/*
PCM转MP3
只能转双声道数据,单声道会变快
*/
+ (NSString *)audio_PCMToMP3:(NSString *)pcmFilePath rate:(XBAudioRate)rate;
///PCM转WAV
+ (NSString *)audio_PCMToWAV:(NSString *)pcmFilePath rate:(XBAudioRate)rate channels:(int)channels;
@end
//
// XBAudioFormatConversion.m
// XBVoiceTool
//
// Created by xxb on 2018/6/27.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBAudioFormatConversion.h"
#import "lame.h"
#import <UIKit/UIKit.h>
@implementation XBAudioFormatConversion
///PCM转MP3
+ (NSString *)audio_PCMToMP3:(NSString *)pcmFilePath rate:(XBAudioRate)rate
{
NSString *mp3FilePath = [NSString stringWithFormat:@"%@XB_PCMToMP3.mp3",NSTemporaryDirectory()];
NSString *_recordFilePath = pcmFilePath;
@try {
int read, write;
FILE *pcm = fopen([_recordFilePath cStringUsingEncoding:1], "rb"); //source 被转换的音频文件位置
fseek(pcm, 4*1024, SEEK_CUR); //skip file header
FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb+"); //output 输出生成的Mp3文件位置
const int PCM_SIZE = 8192;//8192
const int MP3_SIZE = 8192;//8192
short int pcm_buffer[PCM_SIZE*2];
unsigned char mp3_buffer[MP3_SIZE];
lame_t lame = lame_init();
// lame_set_in_samplerate(lame, 7500.0);//采样播音速度,值越大播报速度越快,反之。
lame_set_in_samplerate(lame, rate);//采样播音速度,值越大播报速度越快,反之。
lame_set_VBR(lame, vbr_default);
lame_init_params(lame);
do {
read = (int)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 {
//do some
NSLog(@"MP3生成成功: %@",mp3FilePath);
// UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"mp3转化成功!" message:nil delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
// [alert show];
}
return mp3FilePath;
}
///PCM转WAV
+ (NSString *)audio_PCMToWAV:(NSString *)pcmFilePath rate:(XBAudioRate)rate channels:(int)channels
{
NSString *wavPath = [NSString stringWithFormat:@"%@XB_PCMToWAV.wav",NSTemporaryDirectory()];
char *pcmPath_c = (char *)[pcmFilePath UTF8String];
char *wavPath_c = (char *)[wavPath UTF8String];
convertPcm2Wav(pcmPath_c, wavPath_c, channels, rate);
return wavPath;
}
// pcm 转wav
//wav头的结构如下所示:
typedef struct {
char fccID[4];
int32_t dwSize;
char fccType[4];
} HEADER;
typedef struct {
char fccID[4];
int32_t dwSize;
int16_t wFormatTag;
int16_t wChannels;
int32_t dwSamplesPerSec;
int32_t dwAvgBytesPerSec;
int16_t wBlockAlign;
int16_t uiBitsPerSample;
}FMT;
typedef struct {
char fccID[4];
int32_t dwSize;
}DATA;
/*
int convertPcm2Wav(char *src_file, char *dst_file, int channels, int sample_rate)
请问这个方法怎么用?参数都是什么意思啊
赞 回复
code书童: @不吃鸡爪 pcm文件路径,wav文件路径,channels为通道数,手机设备一般是单身道,传1即可,sample_rate为pcm文件的采样率,有44100,16000,8000,具体传什么看你录音时候设置的采样率。
*/
int convertPcm2Wav(char *src_file, char *dst_file, int channels, int sample_rate)
{
int bits = 16;
//以下是为了建立.wav头而准备的变量
HEADER pcmHEADER;
FMT pcmFMT;
DATA pcmDATA;
unsigned short m_pcmData;
FILE *fp,*fpCpy;
if((fp=fopen(src_file, "rb")) == NULL) //读取文件
{
printf("open pcm file %s error\n", src_file);
return -1;
}
if((fpCpy=fopen(dst_file, "wb+")) == NULL) //为转换建立一个新文件
{
printf("create wav file error\n");
return -1;
}
//以下是创建wav头的HEADER;但.dwsize未定,因为不知道Data的长度。
strncpy(pcmHEADER.fccID,"RIFF",4);
strncpy(pcmHEADER.fccType,"WAVE",4);
fseek(fpCpy,sizeof(HEADER),1); //跳过HEADER的长度,以便下面继续写入wav文件的数据;
//以上是创建wav头的HEADER;
if(ferror(fpCpy))
{
printf("error\n");
}
//以下是创建wav头的FMT;
pcmFMT.dwSamplesPerSec=sample_rate;
pcmFMT.dwAvgBytesPerSec=pcmFMT.dwSamplesPerSec*sizeof(m_pcmData);
pcmFMT.uiBitsPerSample=bits;
strncpy(pcmFMT.fccID,"fmt ", 4);
pcmFMT.dwSize=16;
pcmFMT.wBlockAlign=2;
pcmFMT.wChannels=channels;
pcmFMT.wFormatTag=1;
//以上是创建wav头的FMT;
fwrite(&pcmFMT,sizeof(FMT),1,fpCpy); //将FMT写入.wav文件;
//以下是创建wav头的DATA; 但由于DATA.dwsize未知所以不能写入.wav文件
strncpy(pcmDATA.fccID,"data", 4);
pcmDATA.dwSize=0; //给pcmDATA.dwsize 0以便于下面给它赋值
fseek(fpCpy,sizeof(DATA),1); //跳过DATA的长度,以便以后再写入wav头的DATA;
fread(&m_pcmData,sizeof(int16_t),1,fp); //从.pcm中读入数据
while(!feof(fp)) //在.pcm文件结束前将他的数据转化并赋给.wav;
{
pcmDATA.dwSize+=2; //计算数据的长度;每读入一个数据,长度就加一;
fwrite(&m_pcmData,sizeof(int16_t),1,fpCpy); //将数据写入.wav文件;
fread(&m_pcmData,sizeof(int16_t),1,fp); //从.pcm中读入数据
}
fclose(fp); //关闭文件
pcmHEADER.dwSize = 0; //根据pcmDATA.dwsize得出pcmHEADER.dwsize的值
rewind(fpCpy); //将fpCpy变为.wav的头,以便于写入HEADER和DATA;
fwrite(&pcmHEADER,sizeof(HEADER),1,fpCpy); //写入HEADER
fseek(fpCpy,sizeof(FMT),1); //跳过FMT,因为FMT已经写入
fwrite(&pcmDATA,sizeof(DATA),1,fpCpy); //写入DATA;
fclose(fpCpy); //关闭文件
return 0;
}
@end
//
// XBAudioPCMDataReader.h
// XBVoiceTool
//
// Created by xxb on 2018/7/2.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface XBAudioPCMDataReader : NSObject
///从已有的数据中读取数据
- (int)readDataFrom:(NSData *)dataStore len:(int)len forData:(Byte *)data;
@end
//
// XBAudioPCMDataReader.m
// XBVoiceTool
//
// Created by xxb on 2018/7/2.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBAudioPCMDataReader.h"
@interface XBAudioPCMDataReader ()
@property (nonatomic,assign) UInt32 readerLength;
@end
@implementation XBAudioPCMDataReader
- (int)readDataFrom:(NSData *)dataStore len:(int)len forData:(Byte *)data
{
UInt32 currentReadLength = 0;
if (_readerLength >= dataStore.length)
{
_readerLength = 0;
return currentReadLength;
}
NSRange range;
if (_readerLength+ len <= dataStore.length)
{
currentReadLength = len;
range = NSMakeRange(_readerLength, currentReadLength);
_readerLength = _readerLength + len;
}
else
{
currentReadLength = (UInt32)(dataStore.length - _readerLength);
range = NSMakeRange(_readerLength, currentReadLength);
_readerLength = (UInt32) dataStore.length;
}
NSData *subData = [dataStore subdataWithRange:range];
Byte *tempByte = (Byte *)[subData bytes];
memcpy(data,tempByte,currentReadLength);
return currentReadLength;
}
@end
//
// XBAudioPlayer.h
// XBVoiceTool
//
// Created by xxb on 2018/7/10.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Header_audio.h"
@interface XBAudioPlayer : NSObject
- (instancetype)initWithFilePath:(NSString *)filePath;
- (void)start;
- (void)stop;
- (float)getProgress;
@end
//
// XBAudioPlayer.m
// XBVoiceTool
//
// Created by xxb on 2018/7/10.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBAudioPlayer.h"
#import "XBAudioUnitPlayer.h"
#import "XBAudioTool.h"
@interface XBAudioPlayer ()
{
ExtAudioFileRef _audioFile;
XBAudioUnitPlayer *_player;
AudioBufferList *_bufferList;
AudioStreamBasicDescription _outputFormat;
UInt64 _totalFrame;
UInt64 _readedFrame;
}
@end
@implementation XBAudioPlayer
- (instancetype)initWithFilePath:(NSString *)filePath
{
if (self == [super init])
{
NSURL *url = [NSURL fileURLWithPath:filePath];
CheckError(ExtAudioFileOpenURL((__bridge CFURLRef)url, &_audioFile),"打开文件失败");
_bufferList = [XBAudioTool allocAudioBufferListWithMDataByteSize:CONST_BUFFER_SIZE mNumberChannels:1 mNumberBuffers:1];
_outputFormat = [XBAudioTool allocAudioStreamBasicDescriptionWithMFormatID:kAudioFormatLinearPCM mFormatFlags:kLinearPCMFormatFlagIsSignedInteger mSampleRate:XBAudioRate_44k mFramesPerPacket:1 mChannelsPerFrame:2 mBitsPerChannel:16];
uint size = sizeof(_outputFormat);
CheckError(ExtAudioFileSetProperty(_audioFile, kExtAudioFileProperty_ClientDataFormat, size, &_outputFormat), "setkExtAudioFileProperty_ClientDataFormat failure");
size = sizeof(_totalFrame);
CheckError(ExtAudioFileGetProperty(_audioFile,
kExtAudioFileProperty_FileLengthFrames,
&size,
&_totalFrame), "获取总帧数失败");
_readedFrame = 0;
}
return self;
}
- (void)start
{
if (_player == nil)
{
typeof(self) __weak weakSelf = self;
typeof(self) __strong strongSelf = weakSelf;
_player = [[XBAudioUnitPlayer alloc] initWithRate:_outputFormat.mSampleRate bit:_outputFormat.mBitsPerChannel channel:_outputFormat.mChannelsPerFrame];
_player.bl_inputFull = ^(XBAudioUnitPlayer *player, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) {
strongSelf->_bufferList->mBuffers[0].mDataByteSize = CONST_BUFFER_SIZE;
OSStatus status = ExtAudioFileRead(strongSelf->_audioFile, &inNumberFrames, strongSelf->_bufferList);
memcpy(ioData->mBuffers[0].mData, strongSelf->_bufferList->mBuffers[0].mData, strongSelf->_bufferList->mBuffers[0].mDataByteSize);
ioData->mBuffers[0].mDataByteSize = strongSelf->_bufferList->mBuffers[0].mDataByteSize;
if (ioData->mBuffers[0].mDataByteSize == 0)
{
[weakSelf stop];
}
strongSelf->_readedFrame += ioData->mBuffers[0].mDataByteSize / strongSelf->_outputFormat.mBytesPerFrame;
CheckError(status, "转换格式失败");
if (inNumberFrames == 0) NSLog(@"播放结束");
NSLog(@"%f",[strongSelf getProgress]);
};
}
[_player start];
}
- (void)stop
{
_player.bl_input = nil;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[_player stop];
_player = nil;
});
}
- (float)getProgress
{
return _readedFrame * 1.0 / _totalFrame;
}
@end
//
// XBAudioTool.h
// XBVoiceTool
//
// Created by xxb on 2018/7/5.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Header_audio.h"
@interface XBAudioTool : NSObject
/** 同步获取文件信息
filePath: 文件路径
audioFileFormat : 文件格式描述
packetNums : 总的packet数量
maxFramesPerPacket:单个packet的最大帧数
fileLengthFrames : 总帧数
*/
+ (void)getAudioPropertyWithFilepath:(NSString *)filePath completeBlock:(void (^)(AudioFileID audioFileID,AudioStreamBasicDescription audioFileFormat,UInt64 packetNums,UInt64 maxFramesPerPacket,UInt64 fileLengthFrames))completeBlock errorBlock:(void (^)(NSError *error))errorBlock;
/** 异步获取文件信息
filePath: 文件路径
audioFileFormat : 文件格式描述
packetNums : 总的packet数量
maxFramesPerPacket:单个packet的最大帧数
fileLengthFrames : 总帧数
*/
+ (void)getAudioPropertyAsyncWithFilepath:(NSString *)filePath completeBlock:(void (^)(AudioFileID audioFileID,AudioStreamBasicDescription audioFileFormat,UInt64 packetNums,UInt64 maxFramesPerPacket,UInt64 fileLengthFrames))completeBlock errorBlock:(void (^)(NSError *error))errorBlock;
/**
打印格式信息
*/
+ (void)printAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd;
/**
创建AudioBufferList
mDataByteSize :AudioBuffer.mData (是一个Byte *数组) 数组长度
mNumberChannels :声道数
mNumberBuffers :AudioBuffer(mBuffers[1]) 数组的元素个数
*/
+ (AudioBufferList *)allocAudioBufferListWithMDataByteSize:(UInt32)mDataByteSize mNumberChannels:(UInt32)mNumberChannels mNumberBuffers:(UInt32)mNumberBuffers;
/**
mSampleRate : 采样率
mFormatID :格式
mFormatFlags : 不知道是啥
mFramesPerPacket : 每packet多少frames
mChannelsPerFrame : 每frame多少channel
mBitsPerChannel : 采样精度
*/
+ (AudioStreamBasicDescription)allocAudioStreamBasicDescriptionWithMFormatID:(XBAudioFormatID)mFormatID mFormatFlags:(XBAudioFormatFlags)mFormatFlags mSampleRate:(XBAudioRate)mSampleRate mFramesPerPacket:(UInt32)mFramesPerPacket mChannelsPerFrame:(UInt32)mChannelsPerFrame mBitsPerChannel:(UInt32)mBitsPerChannel;
/**
componentType : kAudioUnitType_
componentSubType : kAudioUnitSubType_
componentFlags : 0
componentFlagsMask : 0
*/
+ (AudioComponentDescription)allocAudioComponentDescriptionWithComponentType:(OSType)componentType componentSubType:(OSType)componentSubType componentFlags:(UInt32)componentFlags componentFlagsMask:(UInt32)componentFlagsMask;
@end
/*
struct AudioBufferList
{
UInt32 mNumberBuffers;
AudioBuffer mBuffers[1]; // this is a variable length array of mNumberBuffers elements
#if defined(__cplusplus) && defined(CA_STRICT) && CA_STRICT
public:
AudioBufferList() {}
private:
// Copying and assigning a variable length struct is problematic; generate a compile error.
AudioBufferList(const AudioBufferList&);
AudioBufferList& operator=(const AudioBufferList&);
#endif
};
*/
/*
CF_ENUM(AudioFormatID)
{
kAudioFormatLinearPCM = 'lpcm',
kAudioFormatAC3 = 'ac-3',
kAudioFormat60958AC3 = 'cac3',
kAudioFormatAppleIMA4 = 'ima4',
kAudioFormatMPEG4AAC = 'aac ',
kAudioFormatMPEG4CELP = 'celp',
kAudioFormatMPEG4HVXC = 'hvxc',
kAudioFormatMPEG4TwinVQ = 'twvq',
kAudioFormatMACE3 = 'MAC3',
kAudioFormatMACE6 = 'MAC6',
kAudioFormatULaw = 'ulaw',
kAudioFormatALaw = 'alaw',
kAudioFormatQDesign = 'QDMC',
kAudioFormatQDesign2 = 'QDM2',
kAudioFormatQUALCOMM = 'Qclp',
kAudioFormatMPEGLayer1 = '.mp1',
kAudioFormatMPEGLayer2 = '.mp2',
kAudioFormatMPEGLayer3 = '.mp3',
kAudioFormatTimeCode = 'time',
kAudioFormatMIDIStream = 'midi',
kAudioFormatParameterValueStream = 'apvs',
kAudioFormatAppleLossless = 'alac',
kAudioFormatMPEG4AAC_HE = 'aach',
kAudioFormatMPEG4AAC_LD = 'aacl',
kAudioFormatMPEG4AAC_ELD = 'aace',
kAudioFormatMPEG4AAC_ELD_SBR = 'aacf',
kAudioFormatMPEG4AAC_ELD_V2 = 'aacg',
kAudioFormatMPEG4AAC_HE_V2 = 'aacp',
kAudioFormatMPEG4AAC_Spatial = 'aacs',
kAudioFormatAMR = 'samr',
kAudioFormatAMR_WB = 'sawb',
kAudioFormatAudible = 'AUDB',
kAudioFormatiLBC = 'ilbc',
kAudioFormatDVIIntelIMA = 0x6D730011,
kAudioFormatMicrosoftGSM = 0x6D730031,
kAudioFormatAES3 = 'aes3',
kAudioFormatEnhancedAC3 = 'ec-3',
kAudioFormatFLAC = 'flac',
kAudioFormatOpus = 'opus'
};
*/
/*
CF_ENUM(AudioFormatFlags)
{
kAudioFormatFlagIsFloat = (1U << 0), // 0x1
kAudioFormatFlagIsBigEndian = (1U << 1), // 0x2
kAudioFormatFlagIsSignedInteger = (1U << 2), // 0x4
kAudioFormatFlagIsPacked = (1U << 3), // 0x8
kAudioFormatFlagIsAlignedHigh = (1U << 4), // 0x10
kAudioFormatFlagIsNonInterleaved = (1U << 5), // 0x20
kAudioFormatFlagIsNonMixable = (1U << 6), // 0x40
kAudioFormatFlagsAreAllClear = 0x80000000,
kLinearPCMFormatFlagIsFloat = kAudioFormatFlagIsFloat,
kLinearPCMFormatFlagIsBigEndian = kAudioFormatFlagIsBigEndian,
kLinearPCMFormatFlagIsSignedInteger = kAudioFormatFlagIsSignedInteger,
kLinearPCMFormatFlagIsPacked = kAudioFormatFlagIsPacked,
kLinearPCMFormatFlagIsAlignedHigh = kAudioFormatFlagIsAlignedHigh,
kLinearPCMFormatFlagIsNonInterleaved = kAudioFormatFlagIsNonInterleaved,
kLinearPCMFormatFlagIsNonMixable = kAudioFormatFlagIsNonMixable,
kLinearPCMFormatFlagsSampleFractionShift = 7,
kLinearPCMFormatFlagsSampleFractionMask = (0x3F << kLinearPCMFormatFlagsSampleFractionShift),
kLinearPCMFormatFlagsAreAllClear = kAudioFormatFlagsAreAllClear,
kAppleLosslessFormatFlag_16BitSourceData = 1,
kAppleLosslessFormatFlag_20BitSourceData = 2,
kAppleLosslessFormatFlag_24BitSourceData = 3,
kAppleLosslessFormatFlag_32BitSourceData = 4
};
*/
/*
CF_ENUM(UInt32) {
kAudioUnitType_Output = 'auou',
kAudioUnitType_MusicDevice = 'aumu',
kAudioUnitType_MusicEffect = 'aumf',
kAudioUnitType_FormatConverter = 'aufc',
kAudioUnitType_Effect = 'aufx',
kAudioUnitType_Mixer = 'aumx',
kAudioUnitType_Panner = 'aupn',
kAudioUnitType_Generator = 'augn',
kAudioUnitType_OfflineEffect = 'auol',
kAudioUnitType_MIDIProcessor = 'aumi'
};
*/
//
// XBAudioTool.m
// XBVoiceTool
//
// Created by xxb on 2018/7/5.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBAudioTool.h"
@implementation XBAudioTool
/** 同步获取文件信息
filePath: 文件路径
audioFileFormat : 文件格式描述
packetNums : 总的packet数量
maxFramesPerPacket:单个packet的最大帧数
fileLengthFrames : 总帧数
*/
+ (void)getAudioPropertyWithFilepath:(NSString *)filePath completeBlock:(void (^)(AudioFileID audioFileID,AudioStreamBasicDescription audioFileFormat,UInt64 packetNums,UInt64 maxFramesPerPacket,UInt64 fileLengthFrames))completeBlock errorBlock:(void (^)(NSError *error))errorBlock
{
AudioFileID audioFileID;
AudioStreamBasicDescription audioFileFormat = {};
UInt64 packetNums = 0;
UInt64 maxFramesPerPacket = 0;
NSError *error;
//打开文件
NSURL *url = [NSURL fileURLWithPath:filePath];
OSStatus status = AudioFileOpenURL((__bridge CFURLRef)url, kAudioFileReadPermission, 0, &audioFileID);
if (status != noErr)
{
NSLog(@"打开文件失败 %@", url);
error = [NSError errorWithDomain:@"打开文件失败" code:1008601 userInfo:nil];
if (errorBlock)
{
errorBlock(error);
}
return;
}
//读取文件格式
uint32_t size = sizeof(AudioStreamBasicDescription);
status = AudioFileGetProperty(audioFileID, kAudioFilePropertyDataFormat, &size, &audioFileFormat);
if (status != noErr)
{
error = [NSError errorWithDomain:[NSString stringWithFormat:@"读取文件格式出错,error status %zd", status] code:1008602 userInfo:nil];
if (errorBlock)
{
errorBlock(error);
}
return;
}
//读取文件总的packet数量
size = sizeof(packetNums);
status = AudioFileGetProperty(audioFileID,
kAudioFilePropertyAudioDataPacketCount,
&size,
&packetNums);
if (error != noErr)
{
error = [NSError errorWithDomain:[NSString stringWithFormat:@"读取文件packets总数出错,error status %zd", status] code:1008603 userInfo:nil];
if (errorBlock)
{
errorBlock(error);
}
return;
}
// 读取单个packet的最大帧数
maxFramesPerPacket = audioFileFormat.mFramesPerPacket;
if (maxFramesPerPacket == 0) {
size = sizeof(maxFramesPerPacket);
status = AudioFileGetProperty(audioFileID, kAudioFilePropertyMaximumPacketSize, &size, &maxFramesPerPacket);
if (status != noErr)
{
error = [NSError errorWithDomain:[NSString stringWithFormat:@"读取单个packet的最大数量出错,error status %zd", status] code:1008604 userInfo:nil];
if (errorBlock)
{
errorBlock(error);
}
return;
}
if(status ==noErr && maxFramesPerPacket == 0)
{
error = [NSError errorWithDomain:@"AudioFileGetProperty error or sizePerPacket = 0" code:1008605 userInfo:nil];
if (errorBlock)
{
errorBlock(error);
}
return;
}
}
// 总帧数
UInt64 numFrames = maxFramesPerPacket * packetNums;
AudioFileClose(audioFileID);
if (completeBlock)
{
completeBlock(audioFileID,audioFileFormat,packetNums,maxFramesPerPacket,numFrames);
}
}
/** 异步获取文件信息
filePath: 文件路径
audioFileFormat : 文件格式描述
packetNums : 总的packet数量
maxFramesPerPacket:单个packet的最大帧数
fileLengthFrames : 总帧数
*/
+ (void)getAudioPropertyAsyncWithFilepath:(NSString *)filePath completeBlock:(void (^)(AudioFileID audioFileID,AudioStreamBasicDescription audioFileFormat,UInt64 packetNums,UInt64 maxFramesPerPacket,UInt64 fileLengthFrames))completeBlock errorBlock:(void (^)(NSError *error))errorBlock
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[XBAudioTool getAudioPropertyWithFilepath:filePath completeBlock:^(AudioFileID audioFileID, AudioStreamBasicDescription audioFileFormat, UInt64 packetNums, UInt64 maxFramesPerPacket, UInt64 fileLengthFrames) {
dispatch_async(dispatch_get_main_queue(), ^{
if (completeBlock)
{
completeBlock(audioFileID,audioFileFormat,packetNums,maxFramesPerPacket,fileLengthFrames);
}
});
} errorBlock:^(NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (errorBlock)
{
errorBlock(error);
}
});
}];
});
}
+ (void)printAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd
{
char formatID[5];
UInt32 mFormatID = CFSwapInt32HostToBig(asbd.mFormatID);
bcopy (&mFormatID, formatID, 4);
formatID[4] = '\0';
printf("Sample Rate: %10.0f\n", asbd.mSampleRate);
printf("Format ID: %10s\n", formatID);
printf("Format Flags: %10X\n", (unsigned int)asbd.mFormatFlags);
printf("Bytes per Packet: %10d\n", (unsigned int)asbd.mBytesPerPacket);
printf("Frames per Packet: %10d\n", (unsigned int)asbd.mFramesPerPacket);
printf("Bytes per Frame: %10d\n", (unsigned int)asbd.mBytesPerFrame);
printf("Channels per Frame: %10d\n", (unsigned int)asbd.mChannelsPerFrame);
printf("Bits per Channel: %10d\n", (unsigned int)asbd.mBitsPerChannel);
printf("\n");
}
/**
创建AudioBufferList
mDataByteSize :AudioBuffer.mData (是一个Byte *数组) 数组长度
mNumberChannels :声道数
mNumberBuffers :AudioBuffer(mBuffers[1]) 数组的元素个数
*/
+ (AudioBufferList *)allocAudioBufferListWithMDataByteSize:(UInt32)mDataByteSize mNumberChannels:(UInt32)mNumberChannels mNumberBuffers:(UInt32)mNumberBuffers
{
AudioBufferList *_bufferList;
_bufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList));
_bufferList->mNumberBuffers = 1;
_bufferList->mBuffers[0].mData = malloc(mDataByteSize);
_bufferList->mBuffers[0].mDataByteSize = mDataByteSize;
_bufferList->mBuffers[0].mNumberChannels = 1;
return _bufferList;
}
/**
mSampleRate : 采样率
mFormatID :格式
mFormatFlags : 不知道是啥
mFramesPerPacket : 每packet多少frames
mChannelsPerFrame : 每frame多少channel
mBitsPerChannel : 采样精度
*/
+ (AudioStreamBasicDescription)allocAudioStreamBasicDescriptionWithMFormatID:(XBAudioFormatID)mFormatID mFormatFlags:(XBAudioFormatFlags)mFormatFlags mSampleRate:(XBAudioRate)mSampleRate mFramesPerPacket:(UInt32)mFramesPerPacket mChannelsPerFrame:(UInt32)mChannelsPerFrame mBitsPerChannel:(UInt32)mBitsPerChannel
{
AudioStreamBasicDescription _outputFormat;
memset(&_outputFormat, 0, sizeof(_outputFormat));
_outputFormat.mSampleRate = mSampleRate;
_outputFormat.mFormatID = mFormatID;
_outputFormat.mFormatFlags = mFormatFlags;
_outputFormat.mFramesPerPacket = mFramesPerPacket;
_outputFormat.mChannelsPerFrame = mChannelsPerFrame;
_outputFormat.mBitsPerChannel = mBitsPerChannel;
_outputFormat.mBytesPerFrame = mBitsPerChannel * mChannelsPerFrame / 8;
_outputFormat.mBytesPerPacket = mBitsPerChannel * mChannelsPerFrame / 8 * mFramesPerPacket;
return _outputFormat;
}
/**
componentType : kAudioUnitType_
componentSubType : kAudioUnitSubType_
componentFlags : 0
componentFlagsMask : 0
*/
+ (AudioComponentDescription)allocAudioComponentDescriptionWithComponentType:(OSType)componentType componentSubType:(OSType)componentSubType componentFlags:(UInt32)componentFlags componentFlagsMask:(UInt32)componentFlagsMask
{
AudioComponentDescription outputDesc;
outputDesc.componentType = componentType;
outputDesc.componentSubType = componentSubType;
outputDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
outputDesc.componentFlags = componentFlags;
outputDesc.componentFlagsMask = componentFlagsMask;
return outputDesc;
}
@end
//
// XBAudioUnitMixer.h
// XBVoiceTool
//
// Created by xxb on 2018/7/18.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Header_audio.h"
@interface XBAudioUnitMixer : NSObject
@property (nonatomic,assign,readonly) BOOL isPlaying;
- (instancetype)initWithFilePathArr:(NSArray *)filePathArr;
- (void)start;
- (void)pause;
- (void)enableInput:(BOOL)enable forBus:(int)busIndex;
- (void)setInputVolumeValue:(CGFloat)value forBus:(int)busIndex;
- (void)setOutputVolumeValue:(AudioUnitParameterValue)value;
@end
//
// XBAudioUnitMixer.m
// XBVoiceTool
//
// Created by xxb on 2018/7/18.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBAudioUnitMixer.h"
#import "XBAudioTool.h"
#import "XBAudioFileDataReader.h"
#import "XBDataWriter.h"
#import "XBExtAudioFileRef.h"
#define subPathPCM @"/Documents/xbMixMusic.caf"
#define stroePath [NSHomeDirectory() stringByAppendingString:subPathPCM]
@interface XBAudioUnitMixer ()
{
AUGraph _auGraph;
AudioUnit _mixUnit;
AudioUnit _outputUnit;
XBAudioFileDataReader *_dataReader;
}
@property (nonatomic,strong) XBDataWriter *dataWriter;
@property (nonatomic,strong) XBExtAudioFileRef *storeAudioFile;
@end
@implementation XBAudioUnitMixer
#pragma mark - 生命周期
- (instancetype)initWithFilePathArr:(NSArray *)filePathArr
{
if (self = [super init])
{
_dataReader = [XBAudioFileDataReader new];
[_dataReader loadFileToMemoryWithFilePathArr:filePathArr];
[self createOutFile];
}
return self;
}
- (AudioStreamBasicDescription)getOutputFormat
{
AudioStreamBasicDescription outputFormat = [XBAudioTool allocAudioStreamBasicDescriptionWithMFormatID:XBAudioFormatID_PCM mFormatFlags:(kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsBigEndian) mSampleRate:XBAudioRate_44k mFramesPerPacket:1 mChannelsPerFrame:2 mBitsPerChannel:32];
return outputFormat;
}
- (AudioStreamBasicDescription)getMixFormat
{
AudioStreamBasicDescription mixStreamFmt = *([[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
sampleRate:44100
channels:2
interleaved:NO].streamDescription);
return mixStreamFmt;
}
- (void)createOutFile
{
AudioStreamBasicDescription outputFormat = [self getOutputFormat];
self.storeAudioFile = [[XBExtAudioFileRef alloc] initWithStorePath:stroePath inputFormat:&outputFormat];
}
- (void)initInputAudioUnitWithMixElementCount:(int)mixElementCount
{
//创建和打开AUGraph,用于管理AUNode(AUNode包含AudioUnit)
CheckError(NewAUGraph(&_auGraph), "newAuGraph failure");
CheckError(AUGraphOpen(_auGraph), "openAuGraph failure");
//创建outputUnit
AudioComponentDescription outputDesc = [XBAudioTool allocAudioComponentDescriptionWithComponentType:kAudioUnitType_Output componentSubType:kAudioUnitSubType_RemoteIO componentFlags:0 componentFlagsMask:0];
AUNode outputNode;
CheckError(AUGraphAddNode(_auGraph, &outputDesc, &outputNode), "add outputNode failure");
CheckError(AUGraphNodeInfo(_auGraph, outputNode, NULL, &_outputUnit), "NodeInfo for outputNode failure");
//创建mixUnit
AudioComponentDescription mixDesc = [XBAudioTool allocAudioComponentDescriptionWithComponentType:kAudioUnitType_Mixer componentSubType:kAudioUnitSubType_MultiChannelMixer componentFlags:0 componentFlagsMask:0];
AUNode mixNode;
CheckError(AUGraphAddNode(_auGraph, &mixDesc, &mixNode), "add mixNode failure");
CheckError(AUGraphNodeInfo(_auGraph, mixNode, NULL, &_mixUnit), "NodeInfo for mixNode failure");
//连接mixUnit的输出和outputUnit的输出
CheckError(AUGraphConnectNodeInput(_auGraph, mixNode, 0, outputNode, 0), "AUGraphConnectNodeInput failure");
//设置mixUnit的输入节点数量
int inputElementCount = mixElementCount;
CheckError(AudioUnitSetProperty(_mixUnit,
XBAudioUnitPropertyID_ElementCount,
kAudioUnitScope_Input,
kInputBus,
&inputElementCount,
sizeof(inputElementCount)), "SetProperty ElementCount failure");
for (int i = 0; i < mixElementCount; i++) {
// setup render callback struct
AURenderCallbackStruct callbackStr;
callbackStr.inputProc = &mixerInputFun;
callbackStr.inputProcRefCon = (__bridge void * _Nullable)(self);
CheckError(AUGraphSetNodeInputCallback(_auGraph, mixNode, i, &callbackStr),
"set mixerNode callback error");
AVAudioFormat *clientFormat = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
sampleRate:kSmapleRate
channels:_dataReader.getBufferList[i].channelCount
interleaved:NO];
CheckError(AudioUnitSetProperty(_mixUnit, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, i,
clientFormat.streamDescription, sizeof(AudioStreamBasicDescription)),
"cant set the input scope format on bus[i]");
}
AudioStreamBasicDescription mixStreamFmt = [self getMixFormat];
CheckError(AudioUnitSetProperty(_mixUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&mixStreamFmt,
sizeof(AudioStreamBasicDescription)),"set format failure");
//play
UInt32 size = sizeof(mixStreamFmt);
CheckError(AudioUnitSetProperty(_outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &mixStreamFmt, size),"set format failure");
// double sample = kSmapleRate;
// CheckError(AudioUnitSetProperty(_mixUnit, kAudioUnitProperty_SampleRate,
// kAudioUnitScope_Output, 0,&sample , sizeof(sample)),
// "cant the mixer unit output sample");
AudioStreamBasicDescription mixUnitOutputDesc;
size = sizeof(mixUnitOutputDesc);
CheckError(AudioUnitGetProperty(_mixUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&mixUnitOutputDesc,
&size),"get property failure");
//play data callback
CheckError(AudioUnitAddRenderNotify(_mixUnit, mixUnitOutputCallback, (__bridge void *)self),"AddRenderNotify mixUnitOutputCallback failure");
//初始化AUGraph
CheckError(AUGraphInitialize(_auGraph), "init AUGraph failure");
CheckError(AUGraphStart(_auGraph), "start AUGraph failure");
}
- (void)dealloc
{
[self pause];
NSLog(@"XBAudioUnitMixer销毁");
}
#pragma mark - 控制
- (void)start
{
if (_dataReader.endLoadFileToMemory)
{
[self initInputAudioUnitWithMixElementCount:(int)_dataReader.getBufferListLength];
_isPlaying = YES;
}
else
{
NSLog(@"歌曲载入中...");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self start];
});
}
}
- (void)pause
{
Boolean isRunning = false;
CheckError(AUGraphIsRunning(_auGraph, &isRunning), "get AUGraphIsRunning info failure");
if (isRunning)
{
CheckError(AUGraphStop(_auGraph), "stop graph failure");
CheckError(AUGraphUninitialize(_auGraph),
"AUGraphUninitialize failed");
// CheckError(AudioUnitRemoveRenderNotify(_mixUnit, mixUnitOutputCallback, (__bridge void *)self), "RemoveRenderNotify failure");
_isPlaying = NO;
}
}
- (void)enableInput:(BOOL)enable forBus:(int)busIndex
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
CheckError(AudioUnitSetParameter(_mixUnit, kMultiChannelMixerParam_Enable,
kAudioUnitScope_Input,
busIndex,
(AudioUnitParameterValue)enable,
0),
"cant set kMultiChannelMixerParam_Enable parameter") ;
});
}
- (void)setInputVolumeValue:(CGFloat)value forBus:(int)busIndex
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
CheckError(AudioUnitSetParameter(_mixUnit,
kMultiChannelMixerParam_Volume,
kAudioUnitScope_Input,
busIndex,
(AudioUnitParameterValue)value,
0),
"cant set kMultiChannelMixerParam_Volume parameter in kAudioUnitScope_Input") ;
});
}
- (void)setOutputVolumeValue:(AudioUnitParameterValue)value
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
CheckError(AudioUnitSetParameter(_mixUnit,
kMultiChannelMixerParam_Volume,
kAudioUnitScope_Output,
0,
value,
0),
"cant set kMultiChannelMixerParam_Volume parameter in kAudioUnitScope_Output");
});
}
#pragma mark - 混音输入回调函数
static OSStatus mixerInputFun(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
XBAudioUnitMixer *mixer = (__bridge XBAudioUnitMixer *)inRefCon;
XBAudioBuffer *buffer = &(mixer->_dataReader.getBufferList[inBusNumber]);
UInt64 sample = buffer->startFrame; // frame number to start from
UInt64 bufSamples = buffer->totalFrames; // total number of frames in the sound buffer
Float32 *leftData = buffer->leftData; // audio data buffer
Float32 *rightData = NULL;
Float32 *outL = (Float32 *)ioData->mBuffers[0].mData; // output audio buffer for L channel
Float32 *outR = NULL;
if (buffer->channelCount == 2) {
outR = (Float32 *)ioData->mBuffers[1].mData; //out audio buffer for R channel;
rightData = buffer->rightData;
}
for (UInt32 i = 0; i < inNumberFrames; ++i) {
outL[i] = leftData[sample];
if (buffer->channelCount == 2) {
outR[i] = rightData[sample];
}
sample++;
if (sample > bufSamples) {
// start over from the beginning of the data, our audio simply loops
printf("looping data for bus %d after %ld source frames rendered\n", (unsigned int)inBusNumber, (long)sample-1);
sample = 0;
}
}
buffer->startFrame = sample; // keep track of where we are in the source data buffer
return noErr;
}
static OSStatus mixUnitOutputCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
XBAudioUnitMixer *mixer = (__bridge XBAudioUnitMixer *)inRefCon;
//使用flag判断数据渲染前后,是渲染后状态则有数据可取
if ((*ioActionFlags) & kAudioUnitRenderAction_PostRender)
{
// [mixer.storeAudioFile writeIoData:ioData inNumberFrames:inNumberFrames];
}
return noErr;
}
@end
//
// XBAudioUnitMixerTest.h
// XBVoiceTool
//
// Created by xxb on 2018/7/2.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Header_audio.h"
@interface XBAudioUnitMixerTest : NSObject
- (instancetype)initWithPCMFilePath:(NSString *)filePath rate:(XBAudioRate)rate channels:(XBAudioChannel)channels bit:(XBAudioBit)bit;
- (void)start;
- (void)stop;
@end
//
// XBAudioUnitMixerTest.m
// XBVoiceTool
//
// Created by xxb on 2018/7/2.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBAudioUnitMixerTest.h"
#import "XBAudioUnitRecorder.h"
#import "XBAudioUnitPlayer.h"
#import "XBAudioPCMDataReader.h"
#import "XBDataWriter.h"
#define subPathPCM @"/Documents/xbMixData.caf"
#define stroePath [NSHomeDirectory() stringByAppendingString:subPathPCM]
//#define CONST_BUFFER_SIZE 2048*2*10
@interface XBAudioUnitMixerTest ()
{
Byte *recorderTempBuffer;
}
@property (nonatomic,strong) XBAudioUnitRecorder *recorder;
@property (nonatomic,strong) XBAudioUnitPlayer *player;
@property (nonatomic,strong) XBAudioPCMDataReader *dataReader;
@property (nonatomic,strong) XBDataWriter *dataWriter;
@property (nonatomic,strong) NSData *data;
@end
@implementation XBAudioUnitMixerTest
- (instancetype)initWithPCMFilePath:(NSString *)filePath rate:(XBAudioRate)rate channels:(XBAudioChannel)channels bit:(XBAudioBit)bit
{
if (self = [super init])
{
self.data = [NSData dataWithContentsOfFile:filePath];
self.player = [[XBAudioUnitPlayer alloc] initWithRate:rate bit:bit channel:channels];
self.recorder = [[XBAudioUnitRecorder alloc] initWithRate:rate bit:bit channel:channels];
self.dataReader = [XBAudioPCMDataReader new];
self.dataWriter = [XBDataWriter new];
[self initParams];
}
return self;
}
- (void)initParams
{
recorderTempBuffer = malloc(CONST_BUFFER_SIZE);
typeof(self) __weak weakSelf = self;
typeof(weakSelf) __strong strongSelf = weakSelf;
if (self.recorder.bl_output == nil)
{
self.recorder.bl_output = ^(AudioBufferList *bufferList) {
AudioBuffer buffer = bufferList->mBuffers[0];
int len = buffer.mDataByteSize;
memcpy(strongSelf->recorderTempBuffer, buffer.mData, len);
};
}
if (self.player.bl_input == nil)
{
self.player.bl_input = ^(AudioBufferList *bufferList) {
AudioBuffer buffer = bufferList->mBuffers[0];
int len = buffer.mDataByteSize;
int readLen = [weakSelf.dataReader readDataFrom:weakSelf.data len:len forData:buffer.mData];
buffer.mDataByteSize = readLen;
for (int i = 0; i < readLen; i++)
{
((Byte *)buffer.mData)[i] = ((Byte *)buffer.mData)[i] + strongSelf->recorderTempBuffer[i];
}
//写文件
[strongSelf.dataWriter writeBytes:buffer.mData len:readLen toPath:stroePath];
if (readLen == 0)
{
[weakSelf stop];
}
};
}
}
- (void)delete
{
NSString *pcmPath = stroePath;
if ([[NSFileManager defaultManager] fileExistsAtPath:pcmPath])
{
[[NSFileManager defaultManager] removeItemAtPath:pcmPath error:nil];
}
}
- (void)start
{
[self delete];
[self.recorder start];
[self.player start];
}
- (void)stop
{
self.recorder.bl_output = nil;
self.player.bl_input = nil;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.recorder stop];
[self.player stop];
});
}
@end
//
// XBAudioUnitPlayer.h
// XBVoiceTool
//
// Created by xxb on 2018/6/29.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Header_audio.h"
@class XBAudioUnitPlayer;
typedef void (^XBAudioUnitPlayerInputBlock)(AudioBufferList *bufferList);
typedef void (^XBAudioUnitPlayerInputBlockFull)(XBAudioUnitPlayer *player,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData);
@interface XBAudioUnitPlayer : NSObject
@property (nonatomic,copy) XBAudioUnitPlayerInputBlock bl_input;
@property (nonatomic,copy) XBAudioUnitPlayerInputBlockFull bl_inputFull;
- (instancetype)initWithRate:(XBAudioRate)rate bit:(XBAudioBit)bit channel:(XBAudioChannel)channel;
- (void)start;
- (void)stop;
- (void)destroy;
@end
//
// XBAudioUnitPlayer.m
// XBVoiceTool
//
// Created by xxb on 2018/6/29.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBAudioUnitPlayer.h"
#import "XBAudioTool.h"
@interface XBAudioUnitPlayer ()
{
AudioUnit audioUnit;
}
@property (nonatomic,assign) XBAudioBit bit;
@property (nonatomic,assign) XBAudioRate rate;
@property (nonatomic,assign) XBAudioChannel channel;
@end
@implementation XBAudioUnitPlayer
- (instancetype)initWithRate:(XBAudioRate)rate bit:(XBAudioBit)bit channel:(XBAudioChannel)channel
{
if (self = [super init])
{
self.rate = rate;
self.bit = bit;
self.channel = channel;
}
return self;
}
- (instancetype)init
{
if (self = [super init])
{
self.rate = XBAudioRate_44k;
self.bit = XBAudioBit_16;
self.channel = XBAudioChannel_1;
}
return self;
}
- (void)dealloc
{
NSLog(@"XBAudioUnitPlayer销毁");
[self destroy];
}
- (void)destroy
{
if (audioUnit)
{
OSStatus status;
status = AudioComponentInstanceDispose(audioUnit);
CheckError(status, "audioUnit释放失败");
}
}
- (void)initAudioUnitWithRate:(XBAudioRate)rate bit:(XBAudioBit)bit channel:(XBAudioChannel)channel
{
//设置session
NSError *error = nil;
AVAudioSession* session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&error];
[session setActive:YES error:nil];
//初始化audioUnit
AudioComponentDescription outputDesc = [XBAudioTool allocAudioComponentDescriptionWithComponentType:kAudioUnitType_Output
componentSubType:kAudioUnitSubType_VoiceProcessingIO
componentFlags:0
componentFlagsMask:0];
AudioComponent outputComponent = AudioComponentFindNext(NULL, &outputDesc);
AudioComponentInstanceNew(outputComponent, &audioUnit);
//设置输出格式
int mFramesPerPacket = 1;
AudioStreamBasicDescription streamDesc = [XBAudioTool allocAudioStreamBasicDescriptionWithMFormatID:kAudioFormatLinearPCM
mFormatFlags:(kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsNonInterleaved)
mSampleRate:rate
mFramesPerPacket:mFramesPerPacket
mChannelsPerFrame:channel
mBitsPerChannel:bit];
OSStatus status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kOutputBus,
&streamDesc,
sizeof(streamDesc));
CheckError(status, "SetProperty StreamFormat failure");
//设置回调
AURenderCallbackStruct outputCallBackStruct;
outputCallBackStruct.inputProc = outputCallBackFun;
outputCallBackStruct.inputProcRefCon = (__bridge void * _Nullable)(self);
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
kOutputBus,
&outputCallBackStruct,
sizeof(outputCallBackStruct));
CheckError(status, "SetProperty EnableIO failure");
}
- (void)start
{
if (audioUnit == nil)
{
[self initAudioUnitWithRate:self.rate bit:self.bit channel:self.channel];
}
AudioOutputUnitStart(audioUnit);
}
- (void)stop
{
if (audioUnit == nil)
{
return;
}
OSStatus status;
status = AudioOutputUnitStop(audioUnit);
CheckError(status, "audioUnit停止失败");
}
static OSStatus outputCallBackFun( void * inRefCon,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * __nullable ioData)
{
memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
// memset(ioData->mBuffers[1].mData, 0, ioData->mBuffers[1].mDataByteSize);
XBAudioUnitPlayer *player = (__bridge XBAudioUnitPlayer *)(inRefCon);
typeof(player) __weak weakPlayer = player;
if (player.bl_input)
{
player.bl_input(ioData);
}
if (player.bl_inputFull)
{
player.bl_inputFull(weakPlayer, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
}
return noErr;
}
@end
//
// XBAudioUnitRecorder.h
// XBVoiceTool
//
// Created by xxb on 2018/6/28.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Header_audio.h"
@class XBAudioUnitRecorder;
typedef void (^XBAudioUnitRecorderOnputBlock)(AudioBufferList *bufferList);
typedef void (^XBAudioUnitRecorderOnputBlockFull)(XBAudioUnitRecorder *player,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData);
@interface XBAudioUnitRecorder : NSObject
@property (nonatomic,readonly,assign) BOOL isRecording;
@property (nonatomic,copy) XBAudioUnitRecorderOnputBlock bl_output;
@property (nonatomic,copy) XBAudioUnitRecorderOnputBlockFull bl_outputFull;
- (instancetype)initWithRate:(XBAudioRate)rate bit:(XBAudioBit)bit channel:(XBAudioChannel)channel;
- (void)start;
- (void)stop;
- (AudioStreamBasicDescription)getOutputFormat;
@end
//
// XBAudioUnitRecorder.m
// XBVoiceTool
//
// Created by xxb on 2018/6/28.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBAudioUnitRecorder.h"
#import "XBAudioTool.h"
#define subPathPCM @"/Documents/xbMedia"
#define stroePath [NSHomeDirectory() stringByAppendingString:subPathPCM]
@interface XBAudioUnitRecorder ()
{
AudioUnit audioUnit;
}
@property (nonatomic,assign) XBAudioBit bit;
@property (nonatomic,assign) XBAudioRate rate;
@property (nonatomic,assign) XBAudioChannel channel;
@property (nonatomic,assign) AudioStreamBasicDescription inputStreamDesc;
@end
@implementation XBAudioUnitRecorder
- (instancetype)initWithRate:(XBAudioRate)rate bit:(XBAudioBit)bit channel:(XBAudioChannel)channel
{
if (self = [super init])
{
self.bit = bit;
self.rate = rate;
self.channel = channel;
[self initInputAudioUnitWithRate:self.rate bit:self.bit channel:self.channel];
}
return self;
}
- (instancetype)init
{
if (self = [super init])
{
self.bit = XBAudioBit_16;
self.rate = XBAudioRate_44k;
self.channel = XBAudioChannel_1;
[self initInputAudioUnitWithRate:self.rate bit:self.bit channel:self.channel];
}
return self;
}
- (void)dealloc
{
CheckError(AudioComponentInstanceDispose(audioUnit),
"AudioComponentInstanceDispose failed");
NSLog(@"XBAudioUnitRecorder销毁");
}
- (void)initInputAudioUnitWithRate:(XBAudioRate)rate bit:(XBAudioBit)bit channel:(XBAudioChannel)channel
{
//设置AVAudioSession
NSError *error = nil;
AVAudioSession* session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&error];
[session setActive:YES error:nil];
//初始化audioUnit
AudioComponentDescription inputDesc = [XBAudioTool allocAudioComponentDescriptionWithComponentType:kAudioUnitType_Output componentSubType:kAudioUnitSubType_RemoteIO componentFlags:0 componentFlagsMask:0];
AudioComponent inputComponent = AudioComponentFindNext(NULL, &inputDesc);
CheckError(AudioComponentInstanceNew(inputComponent, &audioUnit), "AudioComponentInstanceNew failure");
//设置输出流格式
int mFramesPerPacket = 1;
AudioStreamBasicDescription inputStreamDesc = [XBAudioTool allocAudioStreamBasicDescriptionWithMFormatID:kAudioFormatLinearPCM mFormatFlags:(kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagIsPacked) mSampleRate:rate mFramesPerPacket:mFramesPerPacket mChannelsPerFrame:channel mBitsPerChannel:bit];
self.inputStreamDesc = inputStreamDesc;
OSStatus status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
kInputBus,
&inputStreamDesc,
sizeof(inputStreamDesc));
CheckError(status, "setProperty inputStreamFormat error");
// status = AudioUnitSetProperty(audioUnit,
// kAudioUnitProperty_StreamFormat,
// kAudioUnitScope_Input,
// kOutputBus,
// &inputStreamDesc,
// sizeof(inputStreamDesc));
// CheckError(status, "setProperty outputStreamFormat error");
//麦克风输入设置为1(yes)
int inputEnable = 1;
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
kInputBus,
&inputEnable,
sizeof(inputEnable));
CheckError(status, "setProperty EnableIO error");
//设置回调
AURenderCallbackStruct inputCallBackStruce;
inputCallBackStruce.inputProc = inputCallBackFun;
inputCallBackStruce.inputProcRefCon = (__bridge void * _Nullable)(self);
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Output,
kInputBus,
&inputCallBackStruce,
sizeof(inputCallBackStruce));
CheckError(status, "setProperty InputCallback error");
AudioStreamBasicDescription outputDesc0;
UInt32 size = sizeof(outputDesc0);
CheckError(AudioUnitGetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&outputDesc0,
&size),"get property failure");
AudioStreamBasicDescription outputDesc1;
size = sizeof(outputDesc1);
CheckError(AudioUnitGetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&outputDesc1,
&size),"get property failure");
}
- (void)start
{
[self delete];
AudioOutputUnitStart(audioUnit);
_isRecording = YES;
}
- (void)stop
{
CheckError(AudioOutputUnitStop(audioUnit),
"AudioOutputUnitStop failed");
_isRecording = NO;
}
- (AudioStreamBasicDescription)getOutputFormat
{
return self.inputStreamDesc;
// AudioStreamBasicDescription outputDesc0;
// UInt32 size = sizeof(outputDesc0);
// CheckError(AudioUnitGetProperty(audioUnit,
// kAudioUnitProperty_StreamFormat,
// kAudioUnitScope_Output,
// 0,
// &outputDesc0,
// &size),"get property failure");
// return outputDesc0;
}
static OSStatus inputCallBackFun( void * inRefCon,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * __nullable ioData)
{
XBAudioUnitRecorder *recorder = (__bridge XBAudioUnitRecorder *)(inRefCon);
typeof(recorder) __weak weakRecorder = recorder;
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0].mData = NULL;
bufferList.mBuffers[0].mDataByteSize = 0;
AudioUnitRender(recorder->audioUnit,
ioActionFlags,
inTimeStamp,
kInputBus,
inNumberFrames,
&bufferList);
if (recorder.bl_output)
{
recorder.bl_output(&bufferList);
}
if (recorder.bl_outputFull)
{
recorder.bl_outputFull(weakRecorder, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList);
}
return noErr;
}
- (void)delete
{
NSString *pcmPath = stroePath;
if ([[NSFileManager defaultManager] fileExistsAtPath:pcmPath])
{
[[NSFileManager defaultManager] removeItemAtPath:pcmPath error:nil];
}
}
@end
//
// XBDataWriter.h
// XBVoiceTool
//
// Created by xxb on 2018/7/5.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Header_audio.h"
@interface XBDataWriter : NSObject
- (void)writeBytes:(void *)bytes len:(NSUInteger)len toPath:(NSString *)path;
- (void)writeData:(NSData *)data toPath:(NSString *)path;
@end
//
// XBDataWriter.m
// XBVoiceTool
//
// Created by xxb on 2018/7/5.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBDataWriter.h"
@interface XBDataWriter ()
@property (nonatomic,strong) NSLock *lock;
@end
@implementation XBDataWriter
- (void)writeBytes:(void *)bytes len:(NSUInteger)len toPath:(NSString *)path
{
NSData *data = [NSData dataWithBytes:bytes length:len];
[self writeData:data toPath:path];
// NSString *savePath = path;
// if ([[NSFileManager defaultManager] fileExistsAtPath:savePath] == false)
// {
// [[NSFileManager defaultManager] createFileAtPath:savePath contents:nil attributes:nil];
// }
// static FILE *fp=NULL;
//
// if(fp==NULL || access( [path UTF8String], F_OK )==-1){
//
// fp = fopen([path UTF8String], "ab+" );
//
// if(fp==NULL){
//
// printf("can't open file!");
//
// fp=NULL;
//
// return;
//
// }
//
// }
//
// if(fp!=NULL){
//
// fwrite(bytes , 1 , len , fp );
//
// printf("write to file %zd bytes",bytes);
//
// }
}
- (void)writeData:(NSData *)data toPath:(NSString *)path
{
[self.lock lock];
NSString *savePath = path;
if ([[NSFileManager defaultManager] fileExistsAtPath:savePath] == false)
{
[[NSFileManager defaultManager] createFileAtPath:savePath contents:nil attributes:nil];
}
NSFileHandle * handle = [NSFileHandle fileHandleForWritingAtPath:savePath];
[handle seekToEndOfFile];
[handle writeData:data];
[self.lock unlock];
}
- (NSLock *)lock
{
if (_lock == nil)
{
_lock = [NSLock new];
}
return _lock;
}
@end
//
// XBExtAudioFileRef.h
// XBVoiceTool
//
// Created by xxb on 2018/7/24.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Header_audio.h"
@interface XBExtAudioFileRef : NSObject
/**
storePath:存储路径
inputFormat : The format of the audio data to be written to the file.
*/
- (instancetype)initWithStorePath:(NSString *)storePath inputFormat:(AudioStreamBasicDescription *)inputFormat;
- (void)writeIoData:(AudioBufferList *)ioData inNumberFrames:(UInt32)inNumberFrames;
- (void)stopWrite;
@end
//
// XBExtAudioFileRef.m
// XBVoiceTool
//
// Created by xxb on 2018/7/24.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBExtAudioFileRef.h"
#import "XBAudioTool.h"
@interface XBExtAudioFileRef ()
{
ExtAudioFileRef _mAudioFileRef;
}
@end
@implementation XBExtAudioFileRef
- (instancetype)initWithStorePath:(NSString *)storePath inputFormat:(AudioStreamBasicDescription *)inputFormat
{
if (self = [super init])
{
[self createOutFileWithStorePath:storePath inputFormat:inputFormat];
}
return self;
}
- (void)createOutFileWithStorePath:(NSString *)storePath inputFormat:(AudioStreamBasicDescription *)inputFormat
{
AudioStreamBasicDescription outputDesc = *inputFormat;
NSString *destinationFilePath = storePath;
CFURLRef destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)destinationFilePath, kCFURLPOSIXPathStyle, false);
CheckError(ExtAudioFileCreateWithURL(destinationURL,
kAudioFileCAFType,
&outputDesc,
NULL,
kAudioFileFlags_EraseFile,
&_mAudioFileRef),"Couldn't create a file for writing");
CFRelease(destinationURL);
// AudioStreamBasicDescription tempDesc;
// uint32_t size = sizeof(AudioStreamBasicDescription);
// CheckError(ExtAudioFileGetProperty(_mAudioFileRef,
// kExtAudioFileProperty_ClientDataFormat,
// &size,
// &tempDesc),
// "cant get the DataFormat");
// UInt32 codecManf = kAppleHardwareAudioCodecManufacturer;
// CheckError(ExtAudioFileSetProperty(_mAudioFileRef, kExtAudioFileProperty_CodecManufacturer, sizeof(UInt32), &codecManf)," set CodecManufacturer failure");
// CheckError(ExtAudioFileSetProperty(_mAudioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(outputDesc), &outputDesc),"set ClientDataFormat failure");
}
- (void)writeIoData:(AudioBufferList *)ioData inNumberFrames:(UInt32)inNumberFrames
{
CheckError(ExtAudioFileWrite(_mAudioFileRef, inNumberFrames, ioData), "写入失败");
}
- (void)dealloc
{
[self stopWrite];
}
- (void)stopWrite
{
CheckError(ExtAudioFileDispose(_mAudioFileRef),"ExtAudioFileDispose failed");
}
@end
//
// XBPCMPlayer.h
// XBVoiceTool
//
// Created by xxb on 2018/7/2.
// Copyright © 2018年 xxb. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Header_audio.h"
@class XBPCMPlayer;
@protocol XBPCMPlayerDelegate <NSObject>
- (void)playToEnd:(XBPCMPlayer *)player;
@end
@interface XBPCMPlayer : NSObject
@property (nonatomic,copy) NSString *filePath;
@property (nonatomic,assign) BOOL isPlaying;
@property (nonatomic,weak) id<XBPCMPlayerDelegate>delegate;
- (instancetype)initWithPCMFilePath:(NSString *)filePath rate:(XBAudioRate)rate channels:(XBAudioChannel)channels bit:(XBAudioBit)bit;
- (void)play;
- (void)stop;
@end
//
// XBPCMPlayer.m
// XBVoiceTool
//
// Created by xxb on 2018/7/2.
// Copyright © 2018年 xxb. All rights reserved.
//
#import "XBPCMPlayer.h"
#import "XBAudioUnitPlayer.h"
#import "XBAudioPCMDataReader.h"
@interface XBPCMPlayer ()
@property (nonatomic,strong) NSData *dataStore;
@property (nonatomic,strong) XBAudioUnitPlayer *player;
@property (nonatomic,strong) XBAudioPCMDataReader *reader;
@end
@implementation XBPCMPlayer
- (instancetype)initWithPCMFilePath:(NSString *)filePath rate:(XBAudioRate)rate channels:(XBAudioChannel)channels bit:(XBAudioBit)bit
{
if (self = [super init])
{
self.filePath = filePath;
self.player = [[XBAudioUnitPlayer alloc] initWithRate:rate bit:bit channel:channels];
self.reader = [XBAudioPCMDataReader new];
}
return self;
}
- (void)dealloc
{
NSLog(@"XBPCMPlayer销毁");
[self.player stop];
self.player = nil;
}
- (void)play
{
if (self.player.bl_input == nil)
{
typeof(self) __weak weakSelf = self;
self.player.bl_input = ^(AudioBufferList *bufferList) {
// NSLog(@"xxxxxinputCallBackTime:%f",[[NSDate date] timeIntervalSince1970]);
AudioBuffer buffer = bufferList->mBuffers[0];
int len = buffer.mDataByteSize;
int readLen = [weakSelf.reader readDataFrom:weakSelf.dataStore len:len forData:buffer.mData];
buffer.mDataByteSize = readLen;
if (readLen == 0)
{
[weakSelf stop];
if ([weakSelf.delegate respondsToSelector:@selector(playToEnd:)])
{
[weakSelf.delegate playToEnd:weakSelf];
}
}
};
}
[self.player start];
self.isPlaying = YES;
}
- (void)stop
{
self.player.bl_input = nil;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kPreferredIOBufferDuration*0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.player stop];
self.isPlaying = NO;
});
}
#pragma mark - 方法重写
- (void)setFilePath:(NSString *)filePath
{
_filePath = filePath;
self.dataStore = [NSData dataWithContentsOfFile:filePath];
}
@end
/*
* Interface to MP3 LAME encoding engine
*
* Copyright (c) 1999 Mark Taylor
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* $Id: lame.h,v 1.189.2.1 2012/01/08 23:49:58 robert Exp $ */
#ifndef LAME_LAME_H
#define LAME_LAME_H
/* for size_t typedef */
#include <stddef.h>
/* for va_list typedef */
#include <stdarg.h>
/* for FILE typedef, TODO: remove when removing lame_mp3_tags_fid */
#include <stdio.h>
#if defined(__cplusplus)
extern "C" {
#endif
typedef void (*lame_report_function)(const char *format, va_list ap);
#if defined(WIN32) || defined(_WIN32)
#undef CDECL
#define CDECL __cdecl
#else
#define CDECL
#endif
#define DEPRECATED_OR_OBSOLETE_CODE_REMOVED 1
typedef enum vbr_mode_e {
vbr_off=0,
vbr_mt, /* obsolete, same as vbr_mtrh */
vbr_rh,
vbr_abr,
vbr_mtrh,
vbr_max_indicator, /* Don't use this! It's used for sanity checks. */
vbr_default=vbr_mtrh /* change this to change the default VBR mode of LAME */
} vbr_mode;
/* MPEG modes */
typedef enum MPEG_mode_e {
STEREO = 0,
JOINT_STEREO,
DUAL_CHANNEL, /* LAME doesn't supports this! */
MONO,
NOT_SET,
MAX_INDICATOR /* Don't use this! It's used for sanity checks. */
} MPEG_mode;
/* Padding types */
typedef enum Padding_type_e {
PAD_NO = 0,
PAD_ALL,
PAD_ADJUST,
PAD_MAX_INDICATOR /* Don't use this! It's used for sanity checks. */
} Padding_type;
/*presets*/
typedef enum preset_mode_e {
/*values from 8 to 320 should be reserved for abr bitrates*/
/*for abr I'd suggest to directly use the targeted bitrate as a value*/
ABR_8 = 8,
ABR_320 = 320,
V9 = 410, /*Vx to match Lame and VBR_xx to match FhG*/
VBR_10 = 410,
V8 = 420,
VBR_20 = 420,
V7 = 430,
VBR_30 = 430,
V6 = 440,
VBR_40 = 440,
V5 = 450,
VBR_50 = 450,
V4 = 460,
VBR_60 = 460,
V3 = 470,
VBR_70 = 470,
V2 = 480,
VBR_80 = 480,
V1 = 490,
VBR_90 = 490,
V0 = 500,
VBR_100 = 500,
/*still there for compatibility*/
R3MIX = 1000,
STANDARD = 1001,
EXTREME = 1002,
INSANE = 1003,
STANDARD_FAST = 1004,
EXTREME_FAST = 1005,
MEDIUM = 1006,
MEDIUM_FAST = 1007
} preset_mode;
/*asm optimizations*/
typedef enum asm_optimizations_e {
MMX = 1,
AMD_3DNOW = 2,
SSE = 3
} asm_optimizations;
/* psychoacoustic model */
typedef enum Psy_model_e {
PSY_GPSYCHO = 1,
PSY_NSPSYTUNE = 2
} Psy_model;
/* buffer considerations */
typedef enum buffer_constraint_e {
MDB_DEFAULT=0,
MDB_STRICT_ISO=1,
MDB_MAXIMUM=2
} buffer_constraint;
struct lame_global_struct;
typedef struct lame_global_struct lame_global_flags;
typedef lame_global_flags *lame_t;
/***********************************************************************
*
* The LAME API
* These functions should be called, in this order, for each
* MP3 file to be encoded. See the file "API" for more documentation
*
***********************************************************************/
/*
* REQUIRED:
* initialize the encoder. sets default for all encoder parameters,
* returns NULL if some malloc()'s failed
* otherwise returns pointer to structure needed for all future
* API calls.
*/
lame_global_flags * CDECL lame_init(void);
#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
#else
/* obsolete version */
int CDECL lame_init_old(lame_global_flags *);
#endif
/*
* OPTIONAL:
* set as needed to override defaults
*/
/********************************************************************
* input stream description
***********************************************************************/
/* number of samples. default = 2^32-1 */
int CDECL lame_set_num_samples(lame_global_flags *, unsigned long);
unsigned long CDECL lame_get_num_samples(const lame_global_flags *);
/* input sample rate in Hz. default = 44100hz */
int CDECL lame_set_in_samplerate(lame_global_flags *, int);
int CDECL lame_get_in_samplerate(const lame_global_flags *);
/* number of channels in input stream. default=2 */
int CDECL lame_set_num_channels(lame_global_flags *, int);
int CDECL lame_get_num_channels(const lame_global_flags *);
/*
scale the input by this amount before encoding. default=1
(not used by decoding routines)
*/
int CDECL lame_set_scale(lame_global_flags *, float);
float CDECL lame_get_scale(const lame_global_flags *);
/*
scale the channel 0 (left) input by this amount before encoding. default=1
(not used by decoding routines)
*/
int CDECL lame_set_scale_left(lame_global_flags *, float);
float CDECL lame_get_scale_left(const lame_global_flags *);
/*
scale the channel 1 (right) input by this amount before encoding. default=1
(not used by decoding routines)
*/
int CDECL lame_set_scale_right(lame_global_flags *, float);
float CDECL lame_get_scale_right(const lame_global_flags *);
/*
output sample rate in Hz. default = 0, which means LAME picks best value
based on the amount of compression. MPEG only allows:
MPEG1 32, 44.1, 48khz
MPEG2 16, 22.05, 24
MPEG2.5 8, 11.025, 12
(not used by decoding routines)
*/
int CDECL lame_set_out_samplerate(lame_global_flags *, int);
int CDECL lame_get_out_samplerate(const lame_global_flags *);
/********************************************************************
* general control parameters
***********************************************************************/
/* 1=cause LAME to collect data for an MP3 frame analyzer. default=0 */
int CDECL lame_set_analysis(lame_global_flags *, int);
int CDECL lame_get_analysis(const lame_global_flags *);
/*
1 = write a Xing VBR header frame.
default = 1
this variable must have been added by a Hungarian notation Windows programmer :-)
*/
int CDECL lame_set_bWriteVbrTag(lame_global_flags *, int);
int CDECL lame_get_bWriteVbrTag(const lame_global_flags *);
/* 1=decode only. use lame/mpglib to convert mp3/ogg to wav. default=0 */
int CDECL lame_set_decode_only(lame_global_flags *, int);
int CDECL lame_get_decode_only(const lame_global_flags *);
#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
#else
/* 1=encode a Vorbis .ogg file. default=0 */
/* DEPRECATED */
int CDECL lame_set_ogg(lame_global_flags *, int);
int CDECL lame_get_ogg(const lame_global_flags *);
#endif
/*
internal algorithm selection. True quality is determined by the bitrate
but this variable will effect quality by selecting expensive or cheap algorithms.
quality=0..9. 0=best (very slow). 9=worst.
recommended: 2 near-best quality, not too slow
5 good quality, fast
7 ok quality, really fast
*/
int CDECL lame_set_quality(lame_global_flags *, int);
int CDECL lame_get_quality(const lame_global_flags *);
/*
mode = 0,1,2,3 = stereo, jstereo, dual channel (not supported), mono
default: lame picks based on compression ration and input channels
*/
int CDECL lame_set_mode(lame_global_flags *, MPEG_mode);
MPEG_mode CDECL lame_get_mode(const lame_global_flags *);
#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
#else
/*
mode_automs. Use a M/S mode with a switching threshold based on
compression ratio
DEPRECATED
*/
int CDECL lame_set_mode_automs(lame_global_flags *, int);
int CDECL lame_get_mode_automs(const lame_global_flags *);
#endif
/*
force_ms. Force M/S for all frames. For testing only.
default = 0 (disabled)
*/
int CDECL lame_set_force_ms(lame_global_flags *, int);
int CDECL lame_get_force_ms(const lame_global_flags *);
/* use free_format? default = 0 (disabled) */
int CDECL lame_set_free_format(lame_global_flags *, int);
int CDECL lame_get_free_format(const lame_global_flags *);
/* perform ReplayGain analysis? default = 0 (disabled) */
int CDECL lame_set_findReplayGain(lame_global_flags *, int);
int CDECL lame_get_findReplayGain(const lame_global_flags *);
/* decode on the fly. Search for the peak sample. If the ReplayGain
* analysis is enabled then perform the analysis on the decoded data
* stream. default = 0 (disabled)
* NOTE: if this option is set the build-in decoder should not be used */
int CDECL lame_set_decode_on_the_fly(lame_global_flags *, int);
int CDECL lame_get_decode_on_the_fly(const lame_global_flags *);
#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
#else
/* DEPRECATED: now does the same as lame_set_findReplayGain()
default = 0 (disabled) */
int CDECL lame_set_ReplayGain_input(lame_global_flags *, int);
int CDECL lame_get_ReplayGain_input(const lame_global_flags *);
/* DEPRECATED: now does the same as
lame_set_decode_on_the_fly() && lame_set_findReplayGain()
default = 0 (disabled) */
int CDECL lame_set_ReplayGain_decode(lame_global_flags *, int);
int CDECL lame_get_ReplayGain_decode(const lame_global_flags *);
/* DEPRECATED: now does the same as lame_set_decode_on_the_fly()
default = 0 (disabled) */
int CDECL lame_set_findPeakSample(lame_global_flags *, int);
int CDECL lame_get_findPeakSample(const lame_global_flags *);
#endif
/* counters for gapless encoding */
int CDECL lame_set_nogap_total(lame_global_flags*, int);
int CDECL lame_get_nogap_total(const lame_global_flags*);
int CDECL lame_set_nogap_currentindex(lame_global_flags* , int);
int CDECL lame_get_nogap_currentindex(const lame_global_flags*);
/*
* OPTIONAL:
* Set printf like error/debug/message reporting functions.
* The second argument has to be a pointer to a function which looks like
* void my_debugf(const char *format, va_list ap)
* {
* (void) vfprintf(stdout, format, ap);
* }
* If you use NULL as the value of the pointer in the set function, the
* lame buildin function will be used (prints to stderr).
* To quiet any output you have to replace the body of the example function
* with just "return;" and use it in the set function.
*/
int CDECL lame_set_errorf(lame_global_flags *, lame_report_function);
int CDECL lame_set_debugf(lame_global_flags *, lame_report_function);
int CDECL lame_set_msgf (lame_global_flags *, lame_report_function);
/* set one of brate compression ratio. default is compression ratio of 11. */
int CDECL lame_set_brate(lame_global_flags *, int);
int CDECL lame_get_brate(const lame_global_flags *);
int CDECL lame_set_compression_ratio(lame_global_flags *, float);
float CDECL lame_get_compression_ratio(const lame_global_flags *);
int CDECL lame_set_preset( lame_global_flags* gfp, int );
int CDECL lame_set_asm_optimizations( lame_global_flags* gfp, int, int );
/********************************************************************
* frame params
***********************************************************************/
/* mark as copyright. default=0 */
int CDECL lame_set_copyright(lame_global_flags *, int);
int CDECL lame_get_copyright(const lame_global_flags *);
/* mark as original. default=1 */
int CDECL lame_set_original(lame_global_flags *, int);
int CDECL lame_get_original(const lame_global_flags *);
/* error_protection. Use 2 bytes from each frame for CRC checksum. default=0 */
int CDECL lame_set_error_protection(lame_global_flags *, int);
int CDECL lame_get_error_protection(const lame_global_flags *);
#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
#else
/* padding_type. 0=pad no frames 1=pad all frames 2=adjust padding(default) */
int CDECL lame_set_padding_type(lame_global_flags *, Padding_type);
Padding_type CDECL lame_get_padding_type(const lame_global_flags *);
#endif
/* MP3 'private extension' bit Meaningless. default=0 */
int CDECL lame_set_extension(lame_global_flags *, int);
int CDECL lame_get_extension(const lame_global_flags *);
/* enforce strict ISO compliance. default=0 */
int CDECL lame_set_strict_ISO(lame_global_flags *, int);
int CDECL lame_get_strict_ISO(const lame_global_flags *);
/********************************************************************
* quantization/noise shaping
***********************************************************************/
/* disable the bit reservoir. For testing only. default=0 */
int CDECL lame_set_disable_reservoir(lame_global_flags *, int);
int CDECL lame_get_disable_reservoir(const lame_global_flags *);
/* select a different "best quantization" function. default=0 */
int CDECL lame_set_quant_comp(lame_global_flags *, int);
int CDECL lame_get_quant_comp(const lame_global_flags *);
int CDECL lame_set_quant_comp_short(lame_global_flags *, int);
int CDECL lame_get_quant_comp_short(const lame_global_flags *);
int CDECL lame_set_experimentalX(lame_global_flags *, int); /* compatibility*/
int CDECL lame_get_experimentalX(const lame_global_flags *);
/* another experimental option. for testing only */
int CDECL lame_set_experimentalY(lame_global_flags *, int);
int CDECL lame_get_experimentalY(const lame_global_flags *);
/* another experimental option. for testing only */
int CDECL lame_set_experimentalZ(lame_global_flags *, int);
int CDECL lame_get_experimentalZ(const lame_global_flags *);
/* Naoki's psycho acoustic model. default=0 */
int CDECL lame_set_exp_nspsytune(lame_global_flags *, int);
int CDECL lame_get_exp_nspsytune(const lame_global_flags *);
void CDECL lame_set_msfix(lame_global_flags *, double);
float CDECL lame_get_msfix(const lame_global_flags *);
/********************************************************************
* VBR control
***********************************************************************/
/* Types of VBR. default = vbr_off = CBR */
int CDECL lame_set_VBR(lame_global_flags *, vbr_mode);
vbr_mode CDECL lame_get_VBR(const lame_global_flags *);
/* VBR quality level. 0=highest 9=lowest */
int CDECL lame_set_VBR_q(lame_global_flags *, int);
int CDECL lame_get_VBR_q(const lame_global_flags *);
/* VBR quality level. 0=highest 9=lowest, Range [0,...,10[ */
int CDECL lame_set_VBR_quality(lame_global_flags *, float);
float CDECL lame_get_VBR_quality(const lame_global_flags *);
/* Ignored except for VBR=vbr_abr (ABR mode) */
int CDECL lame_set_VBR_mean_bitrate_kbps(lame_global_flags *, int);
int CDECL lame_get_VBR_mean_bitrate_kbps(const lame_global_flags *);
int CDECL lame_set_VBR_min_bitrate_kbps(lame_global_flags *, int);
int CDECL lame_get_VBR_min_bitrate_kbps(const lame_global_flags *);
int CDECL lame_set_VBR_max_bitrate_kbps(lame_global_flags *, int);
int CDECL lame_get_VBR_max_bitrate_kbps(const lame_global_flags *);
/*
1=strictly enforce VBR_min_bitrate. Normally it will be violated for
analog silence
*/
int CDECL lame_set_VBR_hard_min(lame_global_flags *, int);
int CDECL lame_get_VBR_hard_min(const lame_global_flags *);
/* for preset */
#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
#else
int CDECL lame_set_preset_expopts(lame_global_flags *, int);
#endif
/********************************************************************
* Filtering control
***********************************************************************/
/* freq in Hz to apply lowpass. Default = 0 = lame chooses. -1 = disabled */
int CDECL lame_set_lowpassfreq(lame_global_flags *, int);
int CDECL lame_get_lowpassfreq(const lame_global_flags *);
/* width of transition band, in Hz. Default = one polyphase filter band */
int CDECL lame_set_lowpasswidth(lame_global_flags *, int);
int CDECL lame_get_lowpasswidth(const lame_global_flags *);
/* freq in Hz to apply highpass. Default = 0 = lame chooses. -1 = disabled */
int CDECL lame_set_highpassfreq(lame_global_flags *, int);
int CDECL lame_get_highpassfreq(const lame_global_flags *);
/* width of transition band, in Hz. Default = one polyphase filter band */
int CDECL lame_set_highpasswidth(lame_global_flags *, int);
int CDECL lame_get_highpasswidth(const lame_global_flags *);
/********************************************************************
* psycho acoustics and other arguments which you should not change
* unless you know what you are doing
***********************************************************************/
/* only use ATH for masking */
int CDECL lame_set_ATHonly(lame_global_flags *, int);
int CDECL lame_get_ATHonly(const lame_global_flags *);
/* only use ATH for short blocks */
int CDECL lame_set_ATHshort(lame_global_flags *, int);
int CDECL lame_get_ATHshort(const lame_global_flags *);
/* disable ATH */
int CDECL lame_set_noATH(lame_global_flags *, int);
int CDECL lame_get_noATH(const lame_global_flags *);
/* select ATH formula */
int CDECL lame_set_ATHtype(lame_global_flags *, int);
int CDECL lame_get_ATHtype(const lame_global_flags *);
/* lower ATH by this many db */
int CDECL lame_set_ATHlower(lame_global_flags *, float);
float CDECL lame_get_ATHlower(const lame_global_flags *);
/* select ATH adaptive adjustment type */
int CDECL lame_set_athaa_type( lame_global_flags *, int);
int CDECL lame_get_athaa_type( const lame_global_flags *);
#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
#else
/* select the loudness approximation used by the ATH adaptive auto-leveling */
int CDECL lame_set_athaa_loudapprox( lame_global_flags *, int);
int CDECL lame_get_athaa_loudapprox( const lame_global_flags *);
#endif
/* adjust (in dB) the point below which adaptive ATH level adjustment occurs */
int CDECL lame_set_athaa_sensitivity( lame_global_flags *, float);
float CDECL lame_get_athaa_sensitivity( const lame_global_flags* );
#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
#else
/* OBSOLETE: predictability limit (ISO tonality formula) */
int CDECL lame_set_cwlimit(lame_global_flags *, int);
int CDECL lame_get_cwlimit(const lame_global_flags *);
#endif
/*
allow blocktypes to differ between channels?
default: 0 for jstereo, 1 for stereo
*/
int CDECL lame_set_allow_diff_short(lame_global_flags *, int);
int CDECL lame_get_allow_diff_short(const lame_global_flags *);
/* use temporal masking effect (default = 1) */
int CDECL lame_set_useTemporal(lame_global_flags *, int);
int CDECL lame_get_useTemporal(const lame_global_flags *);
/* use temporal masking effect (default = 1) */
int CDECL lame_set_interChRatio(lame_global_flags *, float);
float CDECL lame_get_interChRatio(const lame_global_flags *);
/* disable short blocks */
int CDECL lame_set_no_short_blocks(lame_global_flags *, int);
int CDECL lame_get_no_short_blocks(const lame_global_flags *);
/* force short blocks */
int CDECL lame_set_force_short_blocks(lame_global_flags *, int);
int CDECL lame_get_force_short_blocks(const lame_global_flags *);
/* Input PCM is emphased PCM (for instance from one of the rarely
emphased CDs), it is STRONGLY not recommended to use this, because
psycho does not take it into account, and last but not least many decoders
ignore these bits */
int CDECL lame_set_emphasis(lame_global_flags *, int);
int CDECL lame_get_emphasis(const lame_global_flags *);
/************************************************************************/
/* internal variables, cannot be set... */
/* provided because they may be of use to calling application */
/************************************************************************/
/* version 0=MPEG-2 1=MPEG-1 (2=MPEG-2.5) */
int CDECL lame_get_version(const lame_global_flags *);
/* encoder delay */
int CDECL lame_get_encoder_delay(const lame_global_flags *);
/*
padding appended to the input to make sure decoder can fully decode
all input. Note that this value can only be calculated during the
call to lame_encoder_flush(). Before lame_encoder_flush() has
been called, the value of encoder_padding = 0.
*/
int CDECL lame_get_encoder_padding(const lame_global_flags *);
/* size of MPEG frame */
int CDECL lame_get_framesize(const lame_global_flags *);
/* number of PCM samples buffered, but not yet encoded to mp3 data. */
int CDECL lame_get_mf_samples_to_encode( const lame_global_flags* gfp );
/*
size (bytes) of mp3 data buffered, but not yet encoded.
this is the number of bytes which would be output by a call to
lame_encode_flush_nogap. NOTE: lame_encode_flush() will return
more bytes than this because it will encode the reamining buffered
PCM samples before flushing the mp3 buffers.
*/
int CDECL lame_get_size_mp3buffer( const lame_global_flags* gfp );
/* number of frames encoded so far */
int CDECL lame_get_frameNum(const lame_global_flags *);
/*
lame's estimate of the total number of frames to be encoded
only valid if calling program set num_samples
*/
int CDECL lame_get_totalframes(const lame_global_flags *);
/* RadioGain value. Multiplied by 10 and rounded to the nearest. */
int CDECL lame_get_RadioGain(const lame_global_flags *);
/* AudiophileGain value. Multipled by 10 and rounded to the nearest. */
int CDECL lame_get_AudiophileGain(const lame_global_flags *);
/* the peak sample */
float CDECL lame_get_PeakSample(const lame_global_flags *);
/* Gain change required for preventing clipping. The value is correct only if
peak sample searching was enabled. If negative then the waveform
already does not clip. The value is multiplied by 10 and rounded up. */
int CDECL lame_get_noclipGainChange(const lame_global_flags *);
/* user-specified scale factor required for preventing clipping. Value is
correct only if peak sample searching was enabled and no user-specified
scaling was performed. If negative then either the waveform already does
not clip or the value cannot be determined */
float CDECL lame_get_noclipScale(const lame_global_flags *);
/*
* REQUIRED:
* sets more internal configuration based on data provided above.
* returns -1 if something failed.
*/
int CDECL lame_init_params(lame_global_flags *);
/*
* OPTIONAL:
* get the version number, in a string. of the form:
* "3.63 (beta)" or just "3.63".
*/
const char* CDECL get_lame_version ( void );
const char* CDECL get_lame_short_version ( void );
const char* CDECL get_lame_very_short_version ( void );
const char* CDECL get_psy_version ( void );
const char* CDECL get_lame_url ( void );
const char* CDECL get_lame_os_bitness ( void );
/*
* OPTIONAL:
* get the version numbers in numerical form.
*/
typedef struct {
/* generic LAME version */
int major;
int minor;
int alpha; /* 0 if not an alpha version */
int beta; /* 0 if not a beta version */
/* version of the psy model */
int psy_major;
int psy_minor;
int psy_alpha; /* 0 if not an alpha version */
int psy_beta; /* 0 if not a beta version */
/* compile time features */
const char *features; /* Don't make assumptions about the contents! */
} lame_version_t;
void CDECL get_lame_version_numerical(lame_version_t *);
/*
* OPTIONAL:
* print internal lame configuration to message handler
*/
void CDECL lame_print_config(const lame_global_flags* gfp);
void CDECL lame_print_internals( const lame_global_flags *gfp);
/*
* input pcm data, output (maybe) mp3 frames.
* This routine handles all buffering, resampling and filtering for you.
*
* return code number of bytes output in mp3buf. Can be 0
* -1: mp3buf was too small
* -2: malloc() problem
* -3: lame_init_params() not called
* -4: psycho acoustic problems
*
* The required mp3buf_size can be computed from num_samples,
* samplerate and encoding rate, but here is a worst case estimate:
*
* mp3buf_size in bytes = 1.25*num_samples + 7200
*
* I think a tighter bound could be: (mt, March 2000)
* MPEG1:
* num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512
* MPEG2:
* num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256
*
* but test first if you use that!
*
* set mp3buf_size = 0 and LAME will not check if mp3buf_size is
* large enough.
*
* NOTE:
* if gfp->num_channels=2, but gfp->mode = 3 (mono), the L & R channels
* will be averaged into the L channel before encoding only the L channel
* This will overwrite the data in buffer_l[] and buffer_r[].
*
*/
int CDECL lame_encode_buffer (
lame_global_flags* gfp, /* global context handle */
const short int buffer_l [], /* PCM data for left channel */
const short int buffer_r [], /* PCM data for right channel */
const int nsamples, /* number of samples per channel */
unsigned char* mp3buf, /* pointer to encoded MP3 stream */
const int mp3buf_size ); /* number of valid octets in this
stream */
/*
* as above, but input has L & R channel data interleaved.
* NOTE:
* num_samples = number of samples in the L (or R)
* channel, not the total number of samples in pcm[]
*/
int CDECL lame_encode_buffer_interleaved(
lame_global_flags* gfp, /* global context handlei */
short int pcm[], /* PCM data for left and right
channel, interleaved */
int num_samples, /* number of samples per channel,
_not_ number of samples in
pcm[] */
unsigned char* mp3buf, /* pointer to encoded MP3 stream */
int mp3buf_size ); /* number of valid octets in this
stream */
/* as lame_encode_buffer, but for 'float's.
* !! NOTE: !! data must still be scaled to be in the same range as
* short int, +/- 32768
*/
int CDECL lame_encode_buffer_float(
lame_global_flags* gfp, /* global context handle */
const float pcm_l [], /* PCM data for left channel */
const float pcm_r [], /* PCM data for right channel */
const int nsamples, /* number of samples per channel */
unsigned char* mp3buf, /* pointer to encoded MP3 stream */
const int mp3buf_size ); /* number of valid octets in this
stream */
/* as lame_encode_buffer, but for 'float's.
* !! NOTE: !! data must be scaled to +/- 1 full scale
*/
int CDECL lame_encode_buffer_ieee_float(
lame_t gfp,
const float pcm_l [], /* PCM data for left channel */
const float pcm_r [], /* PCM data for right channel */
const int nsamples,
unsigned char * mp3buf,
const int mp3buf_size);
int CDECL lame_encode_buffer_interleaved_ieee_float(
lame_t gfp,
const float pcm[], /* PCM data for left and right
channel, interleaved */
const int nsamples,
unsigned char * mp3buf,
const int mp3buf_size);
/* as lame_encode_buffer, but for 'double's.
* !! NOTE: !! data must be scaled to +/- 1 full scale
*/
int CDECL lame_encode_buffer_ieee_double(
lame_t gfp,
const double pcm_l [], /* PCM data for left channel */
const double pcm_r [], /* PCM data for right channel */
const int nsamples,
unsigned char * mp3buf,
const int mp3buf_size);
int CDECL lame_encode_buffer_interleaved_ieee_double(
lame_t gfp,
const double pcm[], /* PCM data for left and right
channel, interleaved */
const int nsamples,
unsigned char * mp3buf,
const int mp3buf_size);
/* as lame_encode_buffer, but for long's
* !! NOTE: !! data must still be scaled to be in the same range as
* short int, +/- 32768
*
* This scaling was a mistake (doesn't allow one to exploit full
* precision of type 'long'. Use lame_encode_buffer_long2() instead.
*
*/
int CDECL lame_encode_buffer_long(
lame_global_flags* gfp, /* global context handle */
const long buffer_l [], /* PCM data for left channel */
const long buffer_r [], /* PCM data for right channel */
const int nsamples, /* number of samples per channel */
unsigned char* mp3buf, /* pointer to encoded MP3 stream */
const int mp3buf_size ); /* number of valid octets in this
stream */
/* Same as lame_encode_buffer_long(), but with correct scaling.
* !! NOTE: !! data must still be scaled to be in the same range as
* type 'long'. Data should be in the range: +/- 2^(8*size(long)-1)
*
*/
int CDECL lame_encode_buffer_long2(
lame_global_flags* gfp, /* global context handle */
const long buffer_l [], /* PCM data for left channel */
const long buffer_r [], /* PCM data for right channel */
const int nsamples, /* number of samples per channel */
unsigned char* mp3buf, /* pointer to encoded MP3 stream */
const int mp3buf_size ); /* number of valid octets in this
stream */
/* as lame_encode_buffer, but for int's
* !! NOTE: !! input should be scaled to the maximum range of 'int'
* If int is 4 bytes, then the values should range from
* +/- 2147483648.
*
* This routine does not (and cannot, without loosing precision) use
* the same scaling as the rest of the lame_encode_buffer() routines.
*
*/
int CDECL lame_encode_buffer_int(
lame_global_flags* gfp, /* global context handle */
const int buffer_l [], /* PCM data for left channel */
const int buffer_r [], /* PCM data for right channel */
const int nsamples, /* number of samples per channel */
unsigned char* mp3buf, /* pointer to encoded MP3 stream */
const int mp3buf_size ); /* number of valid octets in this
stream */
/*
* REQUIRED:
* lame_encode_flush will flush the intenal PCM buffers, padding with
* 0's to make sure the final frame is complete, and then flush
* the internal MP3 buffers, and thus may return a
* final few mp3 frames. 'mp3buf' should be at least 7200 bytes long
* to hold all possible emitted data.
*
* will also write id3v1 tags (if any) into the bitstream
*
* return code = number of bytes output to mp3buf. Can be 0
*/
int CDECL lame_encode_flush(
lame_global_flags * gfp, /* global context handle */
unsigned char* mp3buf, /* pointer to encoded MP3 stream */
int size); /* number of valid octets in this stream */
/*
* OPTIONAL:
* lame_encode_flush_nogap will flush the internal mp3 buffers and pad
* the last frame with ancillary data so it is a complete mp3 frame.
*
* 'mp3buf' should be at least 7200 bytes long
* to hold all possible emitted data.
*
* After a call to this routine, the outputed mp3 data is complete, but
* you may continue to encode new PCM samples and write future mp3 data
* to a different file. The two mp3 files will play back with no gaps
* if they are concatenated together.
*
* This routine will NOT write id3v1 tags into the bitstream.
*
* return code = number of bytes output to mp3buf. Can be 0
*/
int CDECL lame_encode_flush_nogap(
lame_global_flags * gfp, /* global context handle */
unsigned char* mp3buf, /* pointer to encoded MP3 stream */
int size); /* number of valid octets in this stream */
/*
* OPTIONAL:
* Normally, this is called by lame_init_params(). It writes id3v2 and
* Xing headers into the front of the bitstream, and sets frame counters
* and bitrate histogram data to 0. You can also call this after
* lame_encode_flush_nogap().
*/
int CDECL lame_init_bitstream(
lame_global_flags * gfp); /* global context handle */
/*
* OPTIONAL: some simple statistics
* a bitrate histogram to visualize the distribution of used frame sizes
* a stereo mode histogram to visualize the distribution of used stereo
* modes, useful in joint-stereo mode only
* 0: LR left-right encoded
* 1: LR-I left-right and intensity encoded (currently not supported)
* 2: MS mid-side encoded
* 3: MS-I mid-side and intensity encoded (currently not supported)
*
* attention: don't call them after lame_encode_finish
* suggested: lame_encode_flush -> lame_*_hist -> lame_close
*/
void CDECL lame_bitrate_hist(
const lame_global_flags * gfp,
int bitrate_count[14] );
void CDECL lame_bitrate_kbps(
const lame_global_flags * gfp,
int bitrate_kbps [14] );
void CDECL lame_stereo_mode_hist(
const lame_global_flags * gfp,
int stereo_mode_count[4] );
void CDECL lame_bitrate_stereo_mode_hist (
const lame_global_flags * gfp,
int bitrate_stmode_count[14][4] );
void CDECL lame_block_type_hist (
const lame_global_flags * gfp,
int btype_count[6] );
void CDECL lame_bitrate_block_type_hist (
const lame_global_flags * gfp,
int bitrate_btype_count[14][6] );
#if (DEPRECATED_OR_OBSOLETE_CODE_REMOVED && 0)
#else
/*
* OPTIONAL:
* lame_mp3_tags_fid will rewrite a Xing VBR tag to the mp3 file with file
* pointer fid. These calls perform forward and backwards seeks, so make
* sure fid is a real file. Make sure lame_encode_flush has been called,
* and all mp3 data has been written to the file before calling this
* function.
* NOTE:
* if VBR tags are turned off by the user, or turned off by LAME because
* the output is not a regular file, this call does nothing
* NOTE:
* LAME wants to read from the file to skip an optional ID3v2 tag, so
* make sure you opened the file for writing and reading.
* NOTE:
* You can call lame_get_lametag_frame instead, if you want to insert
* the lametag yourself.
*/
void CDECL lame_mp3_tags_fid(lame_global_flags *, FILE* fid);
#endif
/*
* OPTIONAL:
* lame_get_lametag_frame copies the final LAME-tag into 'buffer'.
* The function returns the number of bytes copied into buffer, or
* the required buffer size, if the provided buffer is too small.
* Function failed, if the return value is larger than 'size'!
* Make sure lame_encode flush has been called before calling this function.
* NOTE:
* if VBR tags are turned off by the user, or turned off by LAME,
* this call does nothing and returns 0.
* NOTE:
* LAME inserted an empty frame in the beginning of mp3 audio data,
* which you have to replace by the final LAME-tag frame after encoding.
* In case there is no ID3v2 tag, usually this frame will be the very first
* data in your mp3 file. If you put some other leading data into your
* file, you'll have to do some bookkeeping about where to write this buffer.
*/
size_t CDECL lame_get_lametag_frame(
const lame_global_flags *, unsigned char* buffer, size_t size);
/*
* REQUIRED:
* final call to free all remaining buffers
*/
int CDECL lame_close (lame_global_flags *);
#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
#else
/*
* OBSOLETE:
* lame_encode_finish combines lame_encode_flush() and lame_close() in
* one call. However, once this call is made, the statistics routines
* will no longer work because the data will have been cleared, and
* lame_mp3_tags_fid() cannot be called to add data to the VBR header
*/
int CDECL lame_encode_finish(
lame_global_flags* gfp,
unsigned char* mp3buf,
int size );
#endif
/*********************************************************************
*
* decoding
*
* a simple interface to mpglib, part of mpg123, is also included if
* libmp3lame is compiled with HAVE_MPGLIB
*
*********************************************************************/
struct hip_global_struct;
typedef struct hip_global_struct hip_global_flags;
typedef hip_global_flags *hip_t;
typedef struct {
int header_parsed; /* 1 if header was parsed and following data was
computed */
int stereo; /* number of channels */
int samplerate; /* sample rate */
int bitrate; /* bitrate */
int mode; /* mp3 frame type */
int mode_ext; /* mp3 frame type */
int framesize; /* number of samples per mp3 frame */
/* this data is only computed if mpglib detects a Xing VBR header */
unsigned long nsamp; /* number of samples in mp3 file. */
int totalframes; /* total number of frames in mp3 file */
/* this data is not currently computed by the mpglib routines */
int framenum; /* frames decoded counter */
} mp3data_struct;
/* required call to initialize decoder */
hip_t CDECL hip_decode_init(void);
/* cleanup call to exit decoder */
int CDECL hip_decode_exit(hip_t gfp);
/* HIP reporting functions */
void CDECL hip_set_errorf(hip_t gfp, lame_report_function f);
void CDECL hip_set_debugf(hip_t gfp, lame_report_function f);
void CDECL hip_set_msgf (hip_t gfp, lame_report_function f);
/*********************************************************************
* input 1 mp3 frame, output (maybe) pcm data.
*
* nout = hip_decode(hip, mp3buf,len,pcm_l,pcm_r);
*
* input:
* len : number of bytes of mp3 data in mp3buf
* mp3buf[len] : mp3 data to be decoded
*
* output:
* nout: -1 : decoding error
* 0 : need more data before we can complete the decode
* >0 : returned 'nout' samples worth of data in pcm_l,pcm_r
* pcm_l[nout] : left channel data
* pcm_r[nout] : right channel data
*
*********************************************************************/
int CDECL hip_decode( hip_t gfp
, unsigned char * mp3buf
, size_t len
, short pcm_l[]
, short pcm_r[]
);
/* same as hip_decode, and also returns mp3 header data */
int CDECL hip_decode_headers( hip_t gfp
, unsigned char* mp3buf
, size_t len
, short pcm_l[]
, short pcm_r[]
, mp3data_struct* mp3data
);
/* same as hip_decode, but returns at most one frame */
int CDECL hip_decode1( hip_t gfp
, unsigned char* mp3buf
, size_t len
, short pcm_l[]
, short pcm_r[]
);
/* same as hip_decode1, but returns at most one frame and mp3 header data */
int CDECL hip_decode1_headers( hip_t gfp
, unsigned char* mp3buf
, size_t len
, short pcm_l[]
, short pcm_r[]
, mp3data_struct* mp3data
);
/* same as hip_decode1_headers, but also returns enc_delay and enc_padding
from VBR Info tag, (-1 if no info tag was found) */
int CDECL hip_decode1_headersB( hip_t gfp
, unsigned char* mp3buf
, size_t len
, short pcm_l[]
, short pcm_r[]
, mp3data_struct* mp3data
, int *enc_delay
, int *enc_padding
);
/* OBSOLETE:
* lame_decode... functions are there to keep old code working
* but it is strongly recommended to replace calls by hip_decode...
* function calls, see above.
*/
#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
#else
int CDECL lame_decode_init(void);
int CDECL lame_decode(
unsigned char * mp3buf,
int len,
short pcm_l[],
short pcm_r[] );
int CDECL lame_decode_headers(
unsigned char* mp3buf,
int len,
short pcm_l[],
short pcm_r[],
mp3data_struct* mp3data );
int CDECL lame_decode1(
unsigned char* mp3buf,
int len,
short pcm_l[],
short pcm_r[] );
int CDECL lame_decode1_headers(
unsigned char* mp3buf,
int len,
short pcm_l[],
short pcm_r[],
mp3data_struct* mp3data );
int CDECL lame_decode1_headersB(
unsigned char* mp3buf,
int len,
short pcm_l[],
short pcm_r[],
mp3data_struct* mp3data,
int *enc_delay,
int *enc_padding );
int CDECL lame_decode_exit(void);
#endif /* obsolete lame_decode API calls */
/*********************************************************************
*
* id3tag stuff
*
*********************************************************************/
/*
* id3tag.h -- Interface to write ID3 version 1 and 2 tags.
*
* Copyright (C) 2000 Don Melton.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
/* utility to obtain alphabetically sorted list of genre names with numbers */
void CDECL id3tag_genre_list(
void (*handler)(int, const char *, void *),
void* cookie);
void CDECL id3tag_init (lame_t gfp);
/* force addition of version 2 tag */
void CDECL id3tag_add_v2 (lame_t gfp);
/* add only a version 1 tag */
void CDECL id3tag_v1_only (lame_t gfp);
/* add only a version 2 tag */
void CDECL id3tag_v2_only (lame_t gfp);
/* pad version 1 tag with spaces instead of nulls */
void CDECL id3tag_space_v1 (lame_t gfp);
/* pad version 2 tag with extra 128 bytes */
void CDECL id3tag_pad_v2 (lame_t gfp);
/* pad version 2 tag with extra n bytes */
void CDECL id3tag_set_pad (lame_t gfp, size_t n);
void CDECL id3tag_set_title(lame_t gfp, const char* title);
void CDECL id3tag_set_artist(lame_t gfp, const char* artist);
void CDECL id3tag_set_album(lame_t gfp, const char* album);
void CDECL id3tag_set_year(lame_t gfp, const char* year);
void CDECL id3tag_set_comment(lame_t gfp, const char* comment);
/* return -1 result if track number is out of ID3v1 range
and ignored for ID3v1 */
int CDECL id3tag_set_track(lame_t gfp, const char* track);
/* return non-zero result if genre name or number is invalid
result 0: OK
result -1: genre number out of range
result -2: no valid ID3v1 genre name, mapped to ID3v1 'Other'
but taken as-is for ID3v2 genre tag */
int CDECL id3tag_set_genre(lame_t gfp, const char* genre);
/* return non-zero result if field name is invalid */
int CDECL id3tag_set_fieldvalue(lame_t gfp, const char* fieldvalue);
/* return non-zero result if image type is invalid */
int CDECL id3tag_set_albumart(lame_t gfp, const char* image, size_t size);
/* lame_get_id3v1_tag copies ID3v1 tag into buffer.
* Function returns number of bytes copied into buffer, or number
* of bytes rquired if buffer 'size' is too small.
* Function fails, if returned value is larger than 'size'.
* NOTE:
* This functions does nothing, if user/LAME disabled ID3v1 tag.
*/
size_t CDECL lame_get_id3v1_tag(lame_t gfp, unsigned char* buffer, size_t size);
/* lame_get_id3v2_tag copies ID3v2 tag into buffer.
* Function returns number of bytes copied into buffer, or number
* of bytes rquired if buffer 'size' is too small.
* Function fails, if returned value is larger than 'size'.
* NOTE:
* This functions does nothing, if user/LAME disabled ID3v2 tag.
*/
size_t CDECL lame_get_id3v2_tag(lame_t gfp, unsigned char* buffer, size_t size);
/* normaly lame_init_param writes ID3v2 tags into the audio stream
* Call lame_set_write_id3tag_automatic(gfp, 0) before lame_init_param
* to turn off this behaviour and get ID3v2 tag with above function
* write it yourself into your file.
*/
void CDECL lame_set_write_id3tag_automatic(lame_global_flags * gfp, int);
int CDECL lame_get_write_id3tag_automatic(lame_global_flags const* gfp);
/* experimental */
int CDECL id3tag_set_textinfo_latin1(lame_t gfp, char const *id, char const *text);
/* experimental */
int CDECL id3tag_set_comment_latin1(lame_t gfp, char const *lang, char const *desc, char const *text);
#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
#else
/* experimental */
int CDECL id3tag_set_textinfo_ucs2(lame_t gfp, char const *id, unsigned short const *text);
/* experimental */
int CDECL id3tag_set_comment_ucs2(lame_t gfp, char const *lang,
unsigned short const *desc, unsigned short const *text);
/* experimental */
int CDECL id3tag_set_fieldvalue_ucs2(lame_t gfp, const unsigned short *fieldvalue);
#endif
/* experimental */
int CDECL id3tag_set_fieldvalue_utf16(lame_t gfp, const unsigned short *fieldvalue);
/* experimental */
int CDECL id3tag_set_textinfo_utf16(lame_t gfp, char const *id, unsigned short const *text);
/* experimental */
int CDECL id3tag_set_comment_utf16(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text);
/***********************************************************************
*
* list of valid bitrates [kbps] & sample frequencies [Hz].
* first index: 0: MPEG-2 values (sample frequencies 16...24 kHz)
* 1: MPEG-1 values (sample frequencies 32...48 kHz)
* 2: MPEG-2.5 values (sample frequencies 8...12 kHz)
***********************************************************************/
extern const int bitrate_table [3][16];
extern const int samplerate_table [3][ 4];
/* access functions for use in DLL, global vars are not exported */
int CDECL lame_get_bitrate(int mpeg_version, int table_index);
int CDECL lame_get_samplerate(int mpeg_version, int table_index);
/* maximum size of albumart image (128KB), which affects LAME_MAXMP3BUFFER
as well since lame_encode_buffer() also returns ID3v2 tag data */
#define LAME_MAXALBUMART (128 * 1024)
/* maximum size of mp3buffer needed if you encode at most 1152 samples for
each call to lame_encode_buffer. see lame_encode_buffer() below
(LAME_MAXMP3BUFFER is now obsolete) */
#define LAME_MAXMP3BUFFER (16384 + LAME_MAXALBUMART)
typedef enum {
LAME_OKAY = 0,
LAME_NOERROR = 0,
LAME_GENERICERROR = -1,
LAME_NOMEM = -10,
LAME_BADBITRATE = -11,
LAME_BADSAMPFREQ = -12,
LAME_INTERNALERROR = -13,
FRONTEND_READERROR = -80,
FRONTEND_WRITEERROR = -81,
FRONTEND_FILETOOLARGE = -82
} lame_errorcodes_t;
#if defined(__cplusplus)
}
#endif
#endif /* LAME_LAME_H */
//
// SHMp3RecordManager.h
// ShorthandMaster
//
// Created by 明津李 on 2020/9/1.
// Copyright © 2020 明津李. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface SHMp3RecordManager : NSObject
+ (instancetype)shared;
- (void)configMp3Path:(NSString *)mp3Path PCMPath:(NSString *)PCMPath;
- (void)start;
- (void)pause;
- (void)stop;
@end
NS_ASSUME_NONNULL_END
//
// SHMp3RecordManager.m
// ShorthandMaster
//
// Created by 明津李 on 2020/9/1.
// Copyright © 2020 明津李. All rights reserved.
//
#import "SHMp3RecordManager.h"
//#import "XBPCMPlayer.h"
//#import "XBAudioFormatConversion.h"
#import "XBAudioUnitRecorder.h"
//#import "XBAudioUnitMixerTest.h"
#import "XBAudioTool.h"
//#import "XBAudioConverterPlayer.h"
//#import "XBAudioPlayer.h"
//#import "XBAudioUnitMixer.h"
//#import "XBAudioPCMDataReader.h"
//#import "XBAudioFileDataReader.h"
#import "XBExtAudioFileRef.h"
//#import "ExtAudioFileMixer.h"
#import "XBDataWriter.h"
//#import "XBAACEncoder_system.h"
#import "MP3Encoder.h"
@interface SHMp3RecordManager ()
//@property (nonatomic,strong) XBPCMPlayer *palyer;
@property (nonatomic,strong) XBAudioUnitRecorder *recorder;
//@property (nonatomic,strong) XBAudioUnitMixerTest *mixer;
//@property (nonatomic,strong) XBAudioConverterPlayer *audioPlayer;
//@property (nonatomic,strong) XBAudioPlayer *audioPlayerNew;
//@property (nonatomic,strong) XBAudioUnitMixer *musicMixer;
//@property (nonatomic,strong) XBAudioPCMDataReader *dataReader;
@property (nonatomic,strong) XBExtAudioFileRef *xbFile;
@property (nonatomic,strong) XBDataWriter *dataWriter;
//@property (nonatomic,strong) XBAACEncoder_system *aacEncoder;
@property (nonatomic,strong) MP3Encoder *mp3Encoder;
@property (nonatomic, copy) NSString * mp3StroePath;
@property (nonatomic, copy) NSString * PCMStorePath;
@end
static SHMp3RecordManager * manager;
@implementation SHMp3RecordManager
+ (instancetype)shared{
static dispatch_once_t o;
dispatch_once(&o, ^{
manager = [[SHMp3RecordManager alloc] init];
});
return manager;
}
+ (instancetype)alloc{
if (manager) {
// 如果单例对象存在则抛出异常
NSException *exception = [NSException exceptionWithName:[NSString stringWithFormat:@"重复创建%@单例对象 异常", [self class]] reason:@"请使用单例" userInfo:nil];
[exception raise];
}
return [super alloc]; // 如果单例对象不存在 正常创建
}
- (id)copyWithZone:(struct _NSZone *)zone{
return manager;
}
+ (id)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [super allocWithZone:zone];
});
return manager;
}
- (instancetype)init{
if (self = [super init]) {
}
return self;
}
- (void)configMp3Path:(NSString *)mp3Path PCMPath:(NSString *)PCMPath{
self.mp3StroePath = mp3Path;
self.PCMStorePath = PCMPath;
[self mp3EncodeTest];
}
- (void)start{
[self.recorder start];
}
- (void)pause{
[self.recorder stop];
}
- (void)stop{
[self.recorder stop];
self.recorder = nil;
}
- (void)reset{
}
#pragma mark - mp3编码
- (void)mp3EncodeTest{
// NSLog(@"%@", mp3StroePath);
[self deleteFileAtPath:self.mp3StroePath];
self.recorder = [[XBAudioUnitRecorder alloc] initWithRate:XBAudioRate_44k bit:XBAudioBit_16 channel:XBAudioChannel_1];
_mp3Encoder = [[MP3Encoder alloc] initWithSampleRate:XBAudioRate_44k channels:1 bitRate:128];
self.dataWriter = [[XBDataWriter alloc] init];
typeof(self) __weak weakSelf = self;
self.recorder.bl_output = ^(AudioBufferList *bufferList) {
AudioBuffer buffer = bufferList->mBuffers[0];
[weakSelf.mp3Encoder encodePCMData:buffer.mData len:buffer.mDataByteSize completeBlock:^(unsigned char *encodedData, int len) {
[weakSelf.dataWriter writeBytes:encodedData len:len toPath:weakSelf.mp3StroePath];
}];
};
[self writeTest];
// [self.recorder start];
}
#pragma mark - 测试文件写入
- (void)writeTest{
[self deleteFileAtPath:self.PCMStorePath];
// self.recorder = [[XBAudioUnitRecorder alloc] initWithRate:XBAudioRate_44k bit:XBAudioBit_16 channel:XBAudioChannel_1];
AudioStreamBasicDescription desc = [XBAudioTool allocAudioStreamBasicDescriptionWithMFormatID:XBAudioFormatID_PCM mFormatFlags:(XBAudioFormatFlags)(kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) mSampleRate:XBAudioRate_44k mFramesPerPacket:1 mChannelsPerFrame:XBAudioChannel_2 mBitsPerChannel:XBAudioBit_16];
self.xbFile = [[XBExtAudioFileRef alloc] initWithStorePath:self.PCMStorePath inputFormat:&desc];
typeof(self) __weak weakSelf = self;
self.recorder.bl_outputFull = ^(XBAudioUnitRecorder *player, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) {
[weakSelf.xbFile writeIoData:ioData inNumberFrames:inNumberFrames];
};
// [self.recorder start];
}
- (void)deleteFileAtPath:(NSString *)path{
if ([[NSFileManager defaultManager] fileExistsAtPath:path]){
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
}
}
@end
......@@ -7,6 +7,7 @@
//
import UIKit
import CloudKit
class SHRecordListViewController: SHBaseViewController {
......
......@@ -14,24 +14,29 @@ class SHRecordModel: NSObject, NSCoding{
@objc var during: NSInteger = 0
@objc var address: String = ""
@objc var pathFile: String = ""
@objc var pcmPathFile: String = ""
@objc var txt: String = ""
@objc var delete: Bool = false
@objc var deleteDate: Date = Date()
@objc var fileData: Data = Data()
override func setValue(_ value: Any?, forUndefinedKey key: String) {
}
//构造方法
required init(time:Date=Date(), during:NSInteger=0, address:String="", pathFile:String="", txt:String="", delete:Bool=false, deleteDate:Date=Date()) {
required init(time:Date=Date(), during:NSInteger=0, address:String="", pathFile:String="", pcmPathFile:String="", txt:String="", delete:Bool=false, deleteDate:Date=Date(), fileData:Data=Data()) {
self.time = time
self.during = during
self.address = address
self.pathFile = pathFile
self.pcmPathFile = pcmPathFile
self.txt = txt
self.delete = delete
self.deleteDate = deleteDate
self.fileData = fileData
}
//从object解析回来
......@@ -40,9 +45,11 @@ class SHRecordModel: NSObject, NSCoding{
self.during = (decoder.decodeObject(forKey: "during") as? NSInteger)!
self.address = decoder.decodeObject(forKey: "address") as? String ?? ""
self.pathFile = decoder.decodeObject(forKey: "pathFile") as? String ?? ""
self.pcmPathFile = decoder.decodeObject(forKey: "pcmPathFile") as? String ?? ""
self.txt = decoder.decodeObject(forKey: "txt") as? String ?? ""
self.delete = decoder.decodeObject(forKey: "delete") as? Bool ?? false
self.deleteDate = (decoder.decodeObject(forKey: "deleteDate") as? Date)!
self.fileData = (decoder.decodeObject(forKey: "fileData") as? Data)!
}
//编码成object
......@@ -51,8 +58,10 @@ class SHRecordModel: NSObject, NSCoding{
coder.encode(during, forKey:"during")
coder.encode(address, forKey:"address")
coder.encode(pathFile, forKey:"pathFile")
coder.encode(pcmPathFile, forKey:"pcmPathFile")
coder.encode(txt, forKey:"txt")
coder.encode(delete, forKey:"delete")
coder.encode(deleteDate, forKey:"deleteDate")
coder.encode(fileData, forKey:"fileData")
}
}
......@@ -175,7 +175,9 @@ class SHRecordShowViewController: SHBaseViewController {
}
do {
let documentsFile = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first)!.appending(model!.pathFile)
// documentsFile = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first)!.appending(model!.pcmPathFile)
// player = try AVAudioPlayer.init(data: model!.fileData)
player = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: documentsFile))
print("歌曲长度:\(player!.duration)")
player!.play()
......
......@@ -11,7 +11,7 @@ import AVFoundation
import Speech
import PDFGenerator
class SHRecordViewController: SHBaseViewController {
class SHRecordViewController: SHBaseViewController{
private var waveView: SHRecordWaveView!
......@@ -42,8 +42,9 @@ class SHRecordViewController: SHBaseViewController {
// url : 录音文件的路径
var wav_file_path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first?.appending("/record.wav")
var txt_file_path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first?.appending("/text.txt")
var postfix_wav_file_path = ""
var mp3_file_path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first?.appending("/record.mp3")
var pcm_file_path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first?.appending("/xbMixData.caf")
var session:AVAudioSession {
let session:AVAudioSession = AVAudioSession.sharedInstance()
do {
......@@ -56,6 +57,7 @@ class SHRecordViewController: SHBaseViewController {
var player: AVAudioPlayer?
lazy var recorder: AVAudioRecorder? = self.getRecorder()
var recorder_mp3: SHMp3RecordManager = SHMp3RecordManager.shared()
// 创建语音识别器,指定语音识别的语言环境 locale ,将来会转化为什么语言,这里是使用的当前区域,那肯定就是简体汉语啦
// private let speechRecognizer = SFSpeechRecognizer(locale: Locale.autoupdatingCurrent)
......@@ -166,8 +168,6 @@ class SHRecordViewController: SHBaseViewController {
return
}
// play()
stopRecord()
save = true
......@@ -202,6 +202,7 @@ class SHRecordViewController: SHBaseViewController {
if sender.isSelected == true {
self.view.sendSubviewToBack(maskView)
recorder?.record()
recorder_mp3.start()
start = true
configSpeechTask()
......@@ -211,6 +212,7 @@ class SHRecordViewController: SHBaseViewController {
}else{
recorder?.pause()
recorder_mp3.pause()
start = false
......@@ -237,7 +239,7 @@ class SHRecordViewController: SHBaseViewController {
AVEncoderAudioQualityKey: NSNumber(value: Int32(AVAudioQuality.min.rawValue))
]
do {
let recorder = try AVAudioRecorder(url: URL(fileURLWithPath: wav_file_path!) as URL, settings: configDic)
let recorder = try AVAudioRecorder(url: URL(fileURLWithPath: wav_file_path!), settings: configDic)
recorder.isMeteringEnabled = true
// 准备录音(系统会给我们分配一些资源)
recorder.prepareToRecord()
......@@ -248,44 +250,42 @@ class SHRecordViewController: SHBaseViewController {
}
}
private func configPathFile(){
let fileManager = FileManager.default
let fileArr = SHRecordViewController.getAllFilePath((NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first)!)
let subFile = Date().format("yyyyMMdd")
var wavFilePath = ""
var txtFilePath = ""
var exist = false
let time = Date.init().timeStamp
for (_, documentsFile) in fileArr!.enumerated() {
// let documentsFile = fileArr![index] as String
if documentsFile == NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first?.appending("/\(subFile)") {
postfix_wav_file_path = "/"+subFile+time+"/record.wav"
let path = documentsFile.appending(time)
wavFilePath = path.appending("/record.wav")
txtFilePath = path.appending("/text.txt")
mp3_file_path = "/"+subFile+time+"/record.mp3"
pcm_file_path = "/"+subFile+time+"/xbMixData.caf"
try! fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
recorder_mp3.configMp3Path((NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first)!+mp3_file_path!, pcmPath: (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first)!+pcm_file_path!)
exist = true
break
}
}
if exist == false {
let path = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first?.appending("/\(subFile)").appending(time))!
postfix_wav_file_path = "/"+subFile+time+"/record.wav"
wavFilePath = path.appending("/record.wav")
txtFilePath = path.appending("/text.txt")
mp3_file_path = "/"+subFile+time+"/record.mp3"
pcm_file_path = "/"+subFile+time+"/xbMixData.caf"
try! fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
}
wav_file_path = wavFilePath
txt_file_path = txtFilePath
recorder_mp3.configMp3Path((NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first)!+mp3_file_path!, pcmPath: (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first)!+pcm_file_path!)
}
NSLog("wav_file_path==========\(String(describing: wav_file_path))")
NSLog("mp3_file_path=======\(String(describing: mp3_file_path)) pcm_file_path=======\(String(describing: pcm_file_path))")
}
private func configRecorder(){
......@@ -380,6 +380,7 @@ class SHRecordViewController: SHBaseViewController {
//结束录音
func stopRecord() {
recorder_mp3.stop()
if let recorder = self.recorder {
if recorder.isRecording {
print("正在录音,马上结束它,文件保存到了:\(wav_file_path!)")
......@@ -405,7 +406,8 @@ class SHRecordViewController: SHBaseViewController {
model.time = Date()
model.address = currentAddress ?? ""
model.txt = recognitionTaskText.first ?? (self.currentTxt ?? "")
model.pathFile = postfix_wav_file_path
model.pathFile = mp3_file_path!
model.pcmPathFile = pcm_file_path!
model.during = seconds
let dic = ["time":model.time, "address":model.address, "txt":model.txt, "pathFile":model.pathFile, "during":model.during, "delete":model.delete] as [String : Any]
......@@ -429,6 +431,39 @@ class SHRecordViewController: SHBaseViewController {
CRUserDefaults.recordList = list
currentModel = model
// do{
//// let documentsFile = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first)!.appending(model.pathFile)
//// let url = URL.init(fileURLWithPath: documentsFile)
////
//// let data = try Data.init(contentsOf: url)
//
// let dic = ["time":model.time, "address":model.address, "txt":model.txt, "pathFile":model.pathFile, "during":model.during, "delete":model.delete, "fileData":recorder_ss_data] as [String : Any]
//
// let keyValueStore = NSUbiquitousKeyValueStore.default
// var list = keyValueStore.object(forKey: "list") as? [Dictionary<String, Any>]
// if list == nil {
// list = [dic]
// }else{
// var contains = false
// for (index, subDic) in list!.enumerated(){
// if (subDic["pathFile"] as! String) == model.pathFile {
// contains = true
// list![index] = dic
// break
// }
// }
// if contains == false {
// list!.append(dic)
// }
// }
// keyValueStore.set(list, forKey: "list")
// keyValueStore.synchronize()
//
// } catch {
//
// }
}
func removeCurrentRecored(_ model:SHRecordModel){
......@@ -455,25 +490,6 @@ class SHRecordViewController: SHBaseViewController {
self.recognitionTask = nil
}
//播放
func play() {
//设置外放模式,不然录音会用听筒模式播放,就很小声
if session.category != AVAudioSession.Category.playback {
do{
try session.setCategory(AVAudioSession.Category.playback)
} catch{
print("外放模式设置失败")
}
}
do {
player = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: wav_file_path!))
print("歌曲长度:\(player!.duration)")
player!.play()
} catch let err {
print("播放失败:\(err.localizedDescription)")
}
}
deinit {
waveTimer?.invalidate()
secondTimer?.invalidate()
......@@ -492,10 +508,6 @@ extension SHRecordViewController{
let second = (seconds)%60;
let timeString = String(format: "%02lu:%02lu:%02lu", hours, minutes, second)
secondsLabel.text = timeString
if seconds%2 == 0{
saveContent()
}
}
@objc private func updateMeters() {
......
//
// SHCloudManager.swift
// ShorthandMaster
//
// Created by 明津李 on 2020/8/31.
// Copyright © 2020 明津李. All rights reserved.
//
import UIKit
import CloudKit
class SHCloudManager: NSObject {
@objc static let shared = SHCloudManager()
private override init() {}
let recordName = "Recording"
lazy var container = CKContainer.default()
lazy var resultRecords:[CKRecord] = []
typealias handler = ((Bool) -> ())
var statusHandler: handler?
// func cheakAccountStatus(_ handler:@escaping handler){
//
// statusHandler = handler
//
// let container = CKContainer.default()
// container.accountStatus { (status, statusError) in
//
// if let error = statusError {
// self.statusHandler?(false)
// print("\(error.localizedDescription)")
// }else{
// switch status {
// case .available:
// self.statusHandler?(true)
// break;
// default:
// self.statusHandler?(false)
// break;
// }
// }
// }
// }
func getAllRecordDataSource(){
let dateBase = container.publicCloudDatabase
let predicate = NSPredicate.init(value: true)
let query = CKQuery.init(recordType: recordName, predicate: predicate)
dateBase.perform(query, inZoneWith: nil) { (records, queryError) in
if let error = queryError {
print("\(error.localizedDescription)")
}else{
self.resultRecords = records!
}
}
}
func addNewRecord(_ model: SHRecordModel){
let documentsFile = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first)!.appending(model.pathFile)
let url = URL.init(fileURLWithPath: documentsFile)
let asset = CKAsset.init(fileURL: url)
let dateBase = container.publicCloudDatabase
let newRecording = CKRecord.init(recordType: recordName)
newRecording.setValue(model.address, forKey: "title")
newRecording.setValue(model.address, forKey: "address")
newRecording.setValue(model.txt, forKey: "content")
newRecording.setValue(model.time, forKey: "createTime")
newRecording.setValue(0, forKey: "delete")
newRecording.setValue(asset, forKey: "recordAsset")
dateBase.save(newRecording) { (subscription, saveError) in
if let error = saveError {
print("\(error.localizedDescription)")
}else{
}
}
}
func modifyRecord(){
let dateBase = container.publicCloudDatabase
let record = resultRecords.first
dateBase.fetch(withRecordID: record!.recordID) { (record, fetchError) in
if let error = fetchError {
print("\(error.localizedDescription)")
}else {
record![""] = ""
dateBase.save(record!) { (saveRecord, saveError) in
if let error = saveError {
print("\(error.localizedDescription)")
}else{
}
}
}
}
}
func deleteRecord(){
let dateBase = container.publicCloudDatabase
let record = resultRecords.first
dateBase.delete(withRecordID: record!.recordID) { (recordID, deleteError) in
if let error = deleteError {
print("\(error.localizedDescription)")
}else{
}
}
}
}
......@@ -3,8 +3,6 @@
//
#import "MBProgressHUD+MJ.h"
//#import "LameTool.h"
#import "AESCipher.h"
#import "UIView+CornerRadii.h"
//#import "MJRefresh.h"
#import "SHMp3RecordManager.h"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.ShorthandMaster.www</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudKit</string>
<string>CloudDocuments</string>
</array>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>iCloud.com.ShorthandMaster.www</string>
</array>
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
</dict>
</plist>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment