mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-07-25 17:00:50 +02:00
feat: add Ghostgram features
- Anti-Delete: save deleted messages locally - Ghost Mode: hide online status, read receipts - Voice Morpher: audio processing effects - Device Spoof: spoof device info - Custom GhostIcon app icon - User Notes: personal notes for contacts - Misc settings and controllers - GPLv2 License
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
import OpusBinding
|
||||
import TelegramCore
|
||||
|
||||
/// VoiceMorpherEngine - Swift wrapper for OGG voice morphing
|
||||
/// Uses VoiceMorpherProcessor (Obj-C) for OGG decode → effects → encode
|
||||
public final class VoiceMorpherEngine {
|
||||
|
||||
public static let shared = VoiceMorpherEngine()
|
||||
|
||||
private init() {}
|
||||
|
||||
// MARK: - Process OGG Data
|
||||
|
||||
/// Process OGG/Opus audio data with current voice morpher preset
|
||||
/// - Parameters:
|
||||
/// - inputData: Original OGG/Opus audio data
|
||||
/// - completion: Callback with processed OGG data or error
|
||||
public func processOggData(
|
||||
_ inputData: Data,
|
||||
completion: @escaping (Result<Data, Error>) -> Void
|
||||
) {
|
||||
let preset = VoiceMorpherManager.shared.effectivePreset
|
||||
|
||||
// If disabled, return original data
|
||||
guard preset != .disabled else {
|
||||
completion(.success(inputData))
|
||||
return
|
||||
}
|
||||
|
||||
// Convert Swift preset to ObjC preset
|
||||
let objcPreset: VoiceMorpherPreset
|
||||
switch preset {
|
||||
case .disabled:
|
||||
objcPreset = .disabled
|
||||
case .anonymous:
|
||||
objcPreset = .anonymous
|
||||
case .female:
|
||||
objcPreset = .female
|
||||
case .male:
|
||||
objcPreset = .male
|
||||
case .child:
|
||||
objcPreset = .child
|
||||
case .robot:
|
||||
objcPreset = .robot
|
||||
}
|
||||
|
||||
VoiceMorpherProcessor.processOggData(inputData, preset: objcPreset) { outputData, error in
|
||||
if let error = error {
|
||||
completion(.failure(error))
|
||||
} else if let outputData = outputData {
|
||||
completion(.success(outputData))
|
||||
} else {
|
||||
completion(.failure(VoiceMorpherError.processingFailed))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Synchronous version for use in existing pipelines
|
||||
public func processOggDataSync(_ inputData: Data) -> Data {
|
||||
let preset = VoiceMorpherManager.shared.effectivePreset
|
||||
|
||||
guard preset != .disabled else {
|
||||
return inputData
|
||||
}
|
||||
|
||||
// Use semaphore for sync call
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
var result = inputData
|
||||
|
||||
processOggData(inputData) { processingResult in
|
||||
switch processingResult {
|
||||
case .success(let data):
|
||||
result = data
|
||||
case .failure:
|
||||
// On error, return original
|
||||
result = inputData
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
|
||||
_ = semaphore.wait(timeout: .now() + 30)
|
||||
return result
|
||||
}
|
||||
|
||||
// MARK: - Errors
|
||||
|
||||
public enum VoiceMorpherError: Error, LocalizedError {
|
||||
case processingFailed
|
||||
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .processingFailed:
|
||||
return "Voice morphing processing failed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user