Contenidos
Servidor en Kotlin
Importar librerías
1 2 3 4 |
io.ktor:ktor-server-netty:1.6.7 io.ktor:ktor-gson:1.6.7 org.slf4j:slf4j-simple:2.0.0-alpha1 org.slf4j:slf4j-simple:1.7.32 |
Código del servidor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
import io.ktor.application.* import io.ktor.http.* import io.ktor.http.content.* import io.ktor.request.* import io.ktor.response.* import io.ktor.routing.* import io.ktor.server.engine.* import io.ktor.server.netty.* import java.io.File fun main() { val server = embeddedServer(Netty, port = 6000) { routing { post("/send-audio") { val multipart = call.receiveMultipart() var fileName: String? = null // Recorre las partes del multipart recibido multipart.forEachPart { part -> when (part) { is PartData.FileItem -> { fileName = part.originalFileName ?: "audioFile" val fileBytes = part.streamProvider().readBytes() // Guarda el archivo en el servidor writeToFile(fileName!!, fileBytes) } else -> { } } part.dispose() } call.respond(HttpStatusCode.OK, "Archivo de audio guardado: $fileName") } } } server.start(wait = true) } // Función para escribir el archivo de audio fun writeToFile(fileName: String, fileBytes: ByteArray) { val file = File(fileName) file.writeBytes(fileBytes) } |
Cliente en Swift para el Apple Watch que graba un audio y lo envía a un servidor
Cambiar la propiedad Microphone Usage Description (Property List Key NSMicrophoneUsageDescription)
Código para el Apple Watch que graba un audio y lo envía a un servidor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
import AVFoundation import SwiftUI struct ContentView: View { var body: some View { VStack { Button(action: { startRecording() }) { Text("Iniciar grabación") .padding() .foregroundColor(.white) .background(Color.blue) .cornerRadius(8) } Button(action: { sendRecordedAudio() }) { Text("Enviar grabación") .padding() .foregroundColor(.white) .background(Color.green) .cornerRadius(8) } } } func startRecording() { let audioFilename = getDocumentsDirectory().appendingPathComponent("recording.wav") let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setCategory(.playAndRecord, mode: .default, options: []) try audioSession.setActive(true) let settings: [String: Any] = [ AVFormatIDKey: kAudioFormatLinearPCM, AVSampleRateKey: 44100.0, AVNumberOfChannelsKey: 1, AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue ] let audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings) audioRecorder.record() DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) { audioRecorder.stop() print("Grabación finalizada") print("Audio file is stored at: \(audioFilename)") } } catch { print("Error al iniciar la grabación: \(error.localizedDescription)") } } func sendRecordedAudio() { let audioFilename = getDocumentsDirectory().appendingPathComponent("recording.wav") guard let audioData = try? Data(contentsOf: audioFilename) else { print("Error al leer el archivo de audio") return } let url = URL(string: "http://192.168.1.55:6000/send-audio")! // Reemplaza con la URL de tu servidor var request = URLRequest(url: url) request.httpMethod = "POST" let boundary = "Boundary-\(UUID().uuidString)" request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") var body = Data() if let boundaryData = "--\(boundary)\r\n".data(using: .utf8), let dispositionData = "Content-Disposition: form-data; name=\"audioFile\"; filename=\"recording.wav\"\r\n".data(using: .utf8), let contentTypeData = "Content-Type: audio/wav\r\n\r\n".data(using: .utf8), let boundaryEndData = "\r\n--\(boundary)--\r\n".data(using: .utf8) { body.append(boundaryData) body.append(dispositionData) body.append(contentTypeData) body.append(audioData) body.append(boundaryEndData) } else { print("Error al convertir los datos de cadena a Data") return } request.httpBody = body if let bodySize = request.httpBody?.count { print("Tamaño aproximado del paquete a enviar: \(bodySize) bytes") } else { print("Error al calcular el tamaño del paquete") } URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { print("Error al enviar el archivo de audio: \(error.localizedDescription)") return } if let httpResponse = response as? HTTPURLResponse { print("Código de estado HTTP: \(httpResponse.statusCode)") } }.resume() } func getDocumentsDirectory() -> URL { FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] } } @main struct YourApp: App { var body: some Scene { WindowGroup { ContentView() } } } |