capacitor-voice-recorder-wav-stereo 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CapacitorVoiceRecorderWavStereo.podspec +17 -0
  2. package/README.md +213 -0
  3. package/android/build.gradle +58 -0
  4. package/android/src/main/AndroidManifest.xml +3 -0
  5. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/CurrentRecordingStatus.java +7 -0
  6. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/CustomMediaRecorder.java +187 -0
  7. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/Messages.java +15 -0
  8. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/NotSupportedOsVersion.java +4 -0
  9. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/RecordData.java +51 -0
  10. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/ResponseGenerator.java +37 -0
  11. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/VoiceRecorder.java +217 -0
  12. package/android/src/main/res/.gitkeep +0 -0
  13. package/dist/docs.json +174 -0
  14. package/dist/esm/VoiceRecorderImpl.d.ts +19 -0
  15. package/dist/esm/VoiceRecorderImpl.js +172 -0
  16. package/dist/esm/VoiceRecorderImpl.js.map +1 -0
  17. package/dist/esm/definitions.d.ts +24 -0
  18. package/dist/esm/definitions.js +2 -0
  19. package/dist/esm/definitions.js.map +1 -0
  20. package/dist/esm/index.d.ts +4 -0
  21. package/dist/esm/index.js +7 -0
  22. package/dist/esm/index.js.map +1 -0
  23. package/dist/esm/predefined-web-responses.d.ts +12 -0
  24. package/dist/esm/predefined-web-responses.js +12 -0
  25. package/dist/esm/predefined-web-responses.js.map +1 -0
  26. package/dist/esm/web.d.ts +13 -0
  27. package/dist/esm/web.js +33 -0
  28. package/dist/esm/web.js.map +1 -0
  29. package/dist/plugin.cjs.js +228 -0
  30. package/dist/plugin.cjs.js.map +1 -0
  31. package/dist/plugin.js +230 -0
  32. package/dist/plugin.js.map +1 -0
  33. package/ios/Plugin/CurrentRecordingStatus.swift +9 -0
  34. package/ios/Plugin/CustomMediaRecorder.swift +82 -0
  35. package/ios/Plugin/Info.plist +24 -0
  36. package/ios/Plugin/Messages.swift +15 -0
  37. package/ios/Plugin/RecordData.swift +17 -0
  38. package/ios/Plugin/ResponseGenerator.swift +28 -0
  39. package/ios/Plugin/VoiceRecorder.swift +130 -0
  40. package/ios/Plugin/VoiceRecorderPlugin.h +10 -0
  41. package/ios/Plugin/VoiceRecorderPlugin.m +15 -0
  42. package/package.json +92 -0
@@ -0,0 +1,82 @@
1
+ import Foundation
2
+ import AVFoundation
3
+
4
+ class CustomMediaRecorder {
5
+
6
+ private var recordingSession: AVAudioSession!
7
+ private var audioRecorder: AVAudioRecorder!
8
+ private var audioFilePath: URL!
9
+ private var originalRecordingSessionCategory: AVAudioSession.Category!
10
+ private var status = CurrentRecordingStatus.NONE
11
+
12
+ private let settings = [
13
+ AVFormatIDKey: Int(kAudioFormatLinearPCM), // Changed to Linear PCM for WAV
14
+ AVSampleRateKey: 44100,
15
+ AVNumberOfChannelsKey: 2, // Stereo
16
+ AVLinearPCMBitDepthKey: 16, // Typical bit depth for WAV
17
+ AVLinearPCMIsBigEndianKey: false,
18
+ AVLinearPCMIsFloatKey: false,
19
+ AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
20
+ ] as [String : Any]
21
+
22
+ private func getDirectoryToSaveAudioFile() -> URL {
23
+ return URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
24
+ }
25
+
26
+ public func startRecording() -> Bool {
27
+ do {
28
+ recordingSession = AVAudioSession.sharedInstance()
29
+ originalRecordingSessionCategory = recordingSession.category
30
+ try recordingSession.setCategory(AVAudioSession.Category.playAndRecord, options: [.allowBluetoothA2DP, .defaultToSpeaker])
31
+ try recordingSession.setActive(true)
32
+ audioFilePath = getDirectoryToSaveAudioFile().appendingPathComponent("\(UUID().uuidString).wav")
33
+ audioRecorder = try AVAudioRecorder(url: audioFilePath, settings: settings)
34
+ audioRecorder.record()
35
+ status = CurrentRecordingStatus.RECORDING
36
+ return true
37
+ } catch {
38
+ return false
39
+ }
40
+ }
41
+
42
+ public func stopRecording() {
43
+ do {
44
+ audioRecorder.stop()
45
+ try recordingSession.setActive(false)
46
+ try recordingSession.setCategory(originalRecordingSessionCategory)
47
+ originalRecordingSessionCategory = nil
48
+ audioRecorder = nil
49
+ recordingSession = nil
50
+ status = CurrentRecordingStatus.NONE
51
+ } catch {}
52
+ }
53
+
54
+ public func getOutputFile() -> URL {
55
+ return audioFilePath
56
+ }
57
+
58
+ public func pauseRecording() -> Bool {
59
+ if(status == CurrentRecordingStatus.RECORDING) {
60
+ audioRecorder.pause()
61
+ status = CurrentRecordingStatus.PAUSED
62
+ return true
63
+ } else {
64
+ return false
65
+ }
66
+ }
67
+
68
+ public func resumeRecording() -> Bool {
69
+ if(status == CurrentRecordingStatus.PAUSED) {
70
+ audioRecorder.record()
71
+ status = CurrentRecordingStatus.RECORDING
72
+ return true
73
+ } else {
74
+ return false
75
+ }
76
+ }
77
+
78
+ public func getCurrentStatus() -> CurrentRecordingStatus {
79
+ return status
80
+ }
81
+
82
+ }
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDevelopmentRegion</key>
6
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
7
+ <key>CFBundleExecutable</key>
8
+ <string>$(EXECUTABLE_NAME)</string>
9
+ <key>CFBundleIdentifier</key>
10
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11
+ <key>CFBundleInfoDictionaryVersion</key>
12
+ <string>6.0</string>
13
+ <key>CFBundleName</key>
14
+ <string>$(PRODUCT_NAME)</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>FMWK</string>
17
+ <key>CFBundleShortVersionString</key>
18
+ <string>1.0</string>
19
+ <key>CFBundleVersion</key>
20
+ <string>$(CURRENT_PROJECT_VERSION)</string>
21
+ <key>NSPrincipalClass</key>
22
+ <string></string>
23
+ </dict>
24
+ </plist>
@@ -0,0 +1,15 @@
1
+ import Foundation
2
+
3
+
4
+ struct Messages {
5
+
6
+ static let MISSING_PERMISSION = "MISSING_PERMISSION"
7
+ static let CANNOT_RECORD_ON_THIS_PHONE = "CANNOT_RECORD_ON_THIS_PHONE"
8
+ static let FAILED_TO_RECORD = "FAILED_TO_RECORD"
9
+ static let RECORDING_HAS_NOT_STARTED = "RECORDING_HAS_NOT_STARTED"
10
+ static let FAILED_TO_FETCH_RECORDING = "FAILED_TO_FETCH_RECORDING"
11
+ static let EMPTY_RECORDING = "EMPTY_RECORDING"
12
+ static let ALREADY_RECORDING = "ALREADY_RECORDING"
13
+ static let MICROPHONE_BEING_USED = "MICROPHONE_BEING_USED"
14
+
15
+ }
@@ -0,0 +1,17 @@
1
+ import Foundation
2
+
3
+ struct RecordData {
4
+
5
+ public let recordDataBase64: String?
6
+ public let mimeType: String
7
+ public let msDuration: Int
8
+
9
+ public func toDictionary() -> Dictionary<String, Any> {
10
+ return [
11
+ "recordDataBase64": recordDataBase64 ?? "",
12
+ "msDuration": msDuration,
13
+ "mimeType": mimeType
14
+ ]
15
+ }
16
+
17
+ }
@@ -0,0 +1,28 @@
1
+ import Foundation
2
+
3
+ struct ResponseGenerator {
4
+
5
+ private static let VALUE_RESPONSE_KEY = "value"
6
+ private static let STATUS_RESPONSE_KEY = "status"
7
+
8
+ public static func fromBoolean(_ value: Bool) -> Dictionary<String, Bool> {
9
+ return value ? successResponse() : failResponse()
10
+ }
11
+
12
+ public static func successResponse() -> Dictionary<String, Bool> {
13
+ return [VALUE_RESPONSE_KEY: true]
14
+ }
15
+
16
+ public static func failResponse() -> Dictionary<String, Bool> {
17
+ return [VALUE_RESPONSE_KEY: false]
18
+ }
19
+
20
+ public static func dataResponse(_ data: Any) -> Dictionary<String, Any> {
21
+ return [VALUE_RESPONSE_KEY: data]
22
+ }
23
+
24
+ public static func statusResponse(_ data: CurrentRecordingStatus) -> Dictionary<String, String> {
25
+ return [STATUS_RESPONSE_KEY: data.rawValue]
26
+ }
27
+
28
+ }
@@ -0,0 +1,130 @@
1
+ import Foundation
2
+ import AVFoundation
3
+ import Capacitor
4
+
5
+ @objc(VoiceRecorder)
6
+ public class VoiceRecorder: CAPPlugin {
7
+
8
+ private var customMediaRecorder: CustomMediaRecorder? = nil
9
+
10
+ @objc func canDeviceVoiceRecord(_ call: CAPPluginCall) {
11
+ call.resolve(ResponseGenerator.successResponse())
12
+ }
13
+
14
+ @objc func requestAudioRecordingPermission(_ call: CAPPluginCall) {
15
+ AVAudioSession.sharedInstance().requestRecordPermission { granted in
16
+ if granted {
17
+ call.resolve(ResponseGenerator.successResponse())
18
+ } else {
19
+ call.resolve(ResponseGenerator.failResponse())
20
+ }
21
+ }
22
+ }
23
+
24
+ @objc func hasAudioRecordingPermission(_ call: CAPPluginCall) {
25
+ call.resolve(ResponseGenerator.fromBoolean(doesUserGaveAudioRecordingPermission()))
26
+ }
27
+
28
+
29
+ @objc func startRecording(_ call: CAPPluginCall) {
30
+ if(!doesUserGaveAudioRecordingPermission()) {
31
+ call.reject(Messages.MISSING_PERMISSION)
32
+ return
33
+ }
34
+
35
+ if(customMediaRecorder != nil) {
36
+ call.reject(Messages.ALREADY_RECORDING)
37
+ return
38
+ }
39
+
40
+ customMediaRecorder = CustomMediaRecorder()
41
+ if(customMediaRecorder == nil) {
42
+ call.reject(Messages.CANNOT_RECORD_ON_THIS_PHONE)
43
+ return
44
+ }
45
+
46
+ let successfullyStartedRecording = customMediaRecorder!.startRecording()
47
+ if successfullyStartedRecording == false {
48
+ call.reject(Messages.CANNOT_RECORD_ON_THIS_PHONE)
49
+ } else {
50
+ call.resolve(ResponseGenerator.successResponse())
51
+ }
52
+ }
53
+
54
+ @objc func stopRecording(_ call: CAPPluginCall) {
55
+ if(customMediaRecorder == nil) {
56
+ call.reject(Messages.RECORDING_HAS_NOT_STARTED)
57
+ return
58
+ }
59
+
60
+ customMediaRecorder?.stopRecording()
61
+
62
+ let audioFileUrl = customMediaRecorder?.getOutputFile()
63
+ if(audioFileUrl == nil) {
64
+ customMediaRecorder = nil
65
+ call.reject(Messages.FAILED_TO_FETCH_RECORDING)
66
+ return
67
+ }
68
+ let recordData = RecordData(
69
+ recordDataBase64: readFileAsBase64(audioFileUrl),
70
+ mimeType: "audio/aac",
71
+ msDuration: getMsDurationOfAudioFile(audioFileUrl)
72
+ )
73
+ customMediaRecorder = nil
74
+ if recordData.recordDataBase64 == nil || recordData.msDuration < 0 {
75
+ call.reject(Messages.EMPTY_RECORDING)
76
+ } else {
77
+ call.resolve(ResponseGenerator.dataResponse(recordData.toDictionary()))
78
+ }
79
+ }
80
+
81
+ @objc func pauseRecording(_ call: CAPPluginCall) {
82
+ if(customMediaRecorder == nil) {
83
+ call.reject(Messages.RECORDING_HAS_NOT_STARTED)
84
+ } else {
85
+ call.resolve(ResponseGenerator.fromBoolean(customMediaRecorder?.pauseRecording() ?? false))
86
+ }
87
+ }
88
+
89
+ @objc func resumeRecording(_ call: CAPPluginCall) {
90
+ if(customMediaRecorder == nil) {
91
+ call.reject(Messages.RECORDING_HAS_NOT_STARTED)
92
+ } else {
93
+ call.resolve(ResponseGenerator.fromBoolean(customMediaRecorder?.resumeRecording() ?? false))
94
+ }
95
+ }
96
+
97
+ @objc func getCurrentStatus(_ call: CAPPluginCall) {
98
+ if(customMediaRecorder == nil) {
99
+ call.resolve(ResponseGenerator.statusResponse(CurrentRecordingStatus.NONE))
100
+ } else {
101
+ call.resolve(ResponseGenerator.statusResponse(customMediaRecorder?.getCurrentStatus() ?? CurrentRecordingStatus.NONE))
102
+ }
103
+ }
104
+
105
+ func doesUserGaveAudioRecordingPermission() -> Bool {
106
+ return AVAudioSession.sharedInstance().recordPermission == AVAudioSession.RecordPermission.granted
107
+ }
108
+
109
+ func readFileAsBase64(_ filePath: URL?) -> String? {
110
+ if(filePath == nil) {
111
+ return nil
112
+ }
113
+
114
+ do {
115
+ let fileData = try Data.init(contentsOf: filePath!)
116
+ let fileStream = fileData.base64EncodedString(options: NSData.Base64EncodingOptions.init(rawValue: 0))
117
+ return fileStream
118
+ } catch {}
119
+
120
+ return nil
121
+ }
122
+
123
+ func getMsDurationOfAudioFile(_ filePath: URL?) -> Int {
124
+ if filePath == nil {
125
+ return -1
126
+ }
127
+ return Int(CMTimeGetSeconds(AVURLAsset(url: filePath!).duration) * 1000)
128
+ }
129
+
130
+ }
@@ -0,0 +1,10 @@
1
+ #import <UIKit/UIKit.h>
2
+
3
+ //! Project version number for Plugin.
4
+ FOUNDATION_EXPORT double PluginVersionNumber;
5
+
6
+ //! Project version string for Plugin.
7
+ FOUNDATION_EXPORT const unsigned char PluginVersionString[];
8
+
9
+ // In this header, you should import all the public headers of your framework using statements like #import <Plugin/PublicHeader.h>
10
+
@@ -0,0 +1,15 @@
1
+ #import <Foundation/Foundation.h>
2
+ #import <Capacitor/Capacitor.h>
3
+
4
+ // Define the plugin using the CAP_PLUGIN Macro, and
5
+ // each method the plugin supports using the CAP_PLUGIN_METHOD macro.
6
+ CAP_PLUGIN(VoiceRecorder, "VoiceRecorder",
7
+ CAP_PLUGIN_METHOD(canDeviceVoiceRecord, CAPPluginReturnPromise);
8
+ CAP_PLUGIN_METHOD(requestAudioRecordingPermission, CAPPluginReturnPromise);
9
+ CAP_PLUGIN_METHOD(hasAudioRecordingPermission, CAPPluginReturnPromise);
10
+ CAP_PLUGIN_METHOD(startRecording, CAPPluginReturnPromise);
11
+ CAP_PLUGIN_METHOD(stopRecording, CAPPluginReturnPromise);
12
+ CAP_PLUGIN_METHOD(pauseRecording, CAPPluginReturnPromise);
13
+ CAP_PLUGIN_METHOD(resumeRecording, CAPPluginReturnPromise);
14
+ CAP_PLUGIN_METHOD(getCurrentStatus, CAPPluginReturnPromise);
15
+ )
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "capacitor-voice-recorder-wav-stereo",
3
+ "version": "7.0.0",
4
+ "description": "Capacitor plugin for voice recording in wav format",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Plugin/",
14
+ "CapacitorVoiceRecorderWavStereo.podspec"
15
+ ],
16
+ "author": {
17
+ "name": "Jun Murakami",
18
+ "email": "jun.murakami.dev@gmail.com"
19
+ },
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/Jun-Murakami/capacitor-voice-recorder-wav-stereo.git"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/Jun-Murakami/capacitor-voice-recorder-wav-stereo/issues"
27
+ },
28
+ "keywords": [
29
+ "capacitor",
30
+ "plugin",
31
+ "native",
32
+ "voice",
33
+ "audio",
34
+ "record",
35
+ "recorder",
36
+ "ios",
37
+ "android"
38
+ ],
39
+ "scripts": {
40
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
41
+ "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..",
42
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
43
+ "verify:web": "npm run build",
44
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
45
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
46
+ "eslint": "eslint . --ext ts",
47
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
48
+ "swiftlint": "node-swiftlint",
49
+ "docgen": "docgen --api VoiceRecorderPlugin --output-readme README.md --output-json dist/docs.json",
50
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
51
+ "clean": "rimraf ./dist",
52
+ "watch": "tsc --watch",
53
+ "prepublishOnly": "npm run build"
54
+ },
55
+ "dependencies": {
56
+ "get-blob-duration": "^1.2.0"
57
+ },
58
+ "devDependencies": {
59
+ "@capacitor/android": "^7.0.1",
60
+ "@capacitor/cli": "^7.0.1",
61
+ "@capacitor/core": "^7.0.1",
62
+ "@capacitor/docgen": "^0.3.0",
63
+ "@capacitor/ios": "^7.0.1",
64
+ "@ionic/eslint-config": "^0.4.0",
65
+ "@ionic/prettier-config": "^4.0.0",
66
+ "@ionic/swiftlint-config": "^2.0.0",
67
+ "@typescript-eslint/eslint-plugin": "^8.22.0",
68
+ "eslint": "^9.19.0",
69
+ "prettier": "~3.4.2",
70
+ "prettier-plugin-java": "~2.6.7",
71
+ "rimraf": "^6.0.1",
72
+ "rollup": "^4.32.1",
73
+ "swiftlint": "^2.0.0",
74
+ "typescript": "5.7.3"
75
+ },
76
+ "peerDependencies": {
77
+ "@capacitor/core": "^6.0.0"
78
+ },
79
+ "prettier": "@ionic/prettier-config",
80
+ "swiftlint": "@ionic/swiftlint-config",
81
+ "eslintConfig": {
82
+ "extends": "@ionic/eslint-config/recommended"
83
+ },
84
+ "capacitor": {
85
+ "ios": {
86
+ "src": "ios"
87
+ },
88
+ "android": {
89
+ "src": "android"
90
+ }
91
+ }
92
+ }