Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In iOS, How to create audio file(.wav, .mp3) file from data?

I am working on BLE project where hardware records the audio data & sending to the iOS application. I writting a logic to convert mp3/wav file from data.

Here, I written mp3 file conversion logic from Data like below:

func storeMusicFile(data: Data) {
     let fileName = "Record-1"
     guard mediaDirectoryURL != nil else {
           print("Error: Failed to fetch mediaDirectoryURL")
           return
     }

     let filePath = mediaDirectoryURL!.appendingPathComponent("/\(fileName).mp3")
     do {
        try data.write(to: filePath, options: .atomic)
     } catch {
        print("Failed while storing files.")
     }
}

But while playing an audio file in AVAudioPlayer, I am getting "The operation couldn’t be completed. (OSStatus error 1954115647.)" error.

So, Confused whether audio file conversion logic is wrong or data from hardware is still needs to decode?

like image 413
Sagar Thummar Avatar asked Oct 16 '22 21:10

Sagar Thummar


2 Answers

The previous answer from @sagar-thummar saved me a ton of time. Unfortunately I am not allowed to vote or comment on it. A few corrections I need to do was:

  • change mediaDirectoryURL to documentDirectoryURL
  • create ARError exception
  • adjust the sample rate AND bits per sample to my settings
like image 101
tobyr Avatar answered Oct 21 '22 08:10

tobyr


To create a audio file(.mp3/.wav), you have to dynamically calculate header file data & need to append that header with actual transfer audio data from the hardware.

Reference: WAVE PCM soundfile format

Here, below I added Swift 4 code snippet for reference

//MARK: Logic for Creating Audio file

class ARFileManager {

      static let shared = ARFileManager()
      let fileManager = FileManager.default

      var documentDirectoryURL: URL? {
          return fileManager.urls(for: .documentDirectory, in: .userDomainMask).first
      }

      func createWavFile(using rawData: Data) throws -> URL {
           //Prepare Wav file header
           let waveHeaderFormate = createWaveHeader(data: rawData) as Data

           //Prepare Final Wav File Data
           let waveFileData = waveHeaderFormate + rawData

           //Store Wav file in document directory.
           return try storeMusicFile(data: waveFileData)
       }

       private func createWaveHeader(data: Data) -> NSData {

            let sampleRate:Int32 = 2000
            let chunkSize:Int32 = 36 + Int32(data.count)
            let subChunkSize:Int32 = 16
            let format:Int16 = 1
            let channels:Int16 = 1
            let bitsPerSample:Int16 = 8
            let byteRate:Int32 = sampleRate * Int32(channels * bitsPerSample / 8)
            let blockAlign: Int16 = channels * bitsPerSample / 8
            let dataSize:Int32 = Int32(data.count)

            let header = NSMutableData()

            header.append([UInt8]("RIFF".utf8), length: 4)
            header.append(intToByteArray(chunkSize), length: 4)

            //WAVE
            header.append([UInt8]("WAVE".utf8), length: 4)

            //FMT
            header.append([UInt8]("fmt ".utf8), length: 4)

            header.append(intToByteArray(subChunkSize), length: 4)
            header.append(shortToByteArray(format), length: 2)
            header.append(shortToByteArray(channels), length: 2)
            header.append(intToByteArray(sampleRate), length: 4)
            header.append(intToByteArray(byteRate), length: 4)
            header.append(shortToByteArray(blockAlign), length: 2)
            header.append(shortToByteArray(bitsPerSample), length: 2)

            header.append([UInt8]("data".utf8), length: 4)
            header.append(intToByteArray(dataSize), length: 4)

            return header
       }

      private func intToByteArray(_ i: Int32) -> [UInt8] {
            return [
              //little endian
              UInt8(truncatingIfNeeded: (i      ) & 0xff),
              UInt8(truncatingIfNeeded: (i >>  8) & 0xff),
              UInt8(truncatingIfNeeded: (i >> 16) & 0xff),
              UInt8(truncatingIfNeeded: (i >> 24) & 0xff)
             ]
       }

       private func shortToByteArray(_ i: Int16) -> [UInt8] {
              return [
                  //little endian
                  UInt8(truncatingIfNeeded: (i      ) & 0xff),
                  UInt8(truncatingIfNeeded: (i >>  8) & 0xff)
              ]
        }

       func storeMusicFile(data: Data) throws -> URL {

             let fileName = "Record \(Date().dateFileName)"

             guard mediaDirectoryURL != nil else {
                debugPrint("Error: Failed to fetch mediaDirectoryURL")
                throw ARError(localizedDescription:             AlertMessage.medioDirectoryPathNotAvaiable)
              }

             let filePath = mediaDirectoryURL!.appendingPathComponent("\(fileName).wav")
              debugPrint("File Path: \(filePath.path)")
              try data.write(to: filePath)

             return filePath //Save file's path respected to document directory.
        }
 }
like image 42
Sagar Thummar Avatar answered Oct 21 '22 08:10

Sagar Thummar