Code Monkey home page Code Monkey logo

Comments (13)

pro100andrey avatar pro100andrey commented on July 28, 2024 1

Certainly, @tryWabbit! Converting an M4A file to MP3 involves a few steps. I'll explain in detail:

  • Decode the M4A File: You need to decode the M4A audio data into raw PCM audio data. M4A is a compressed audio format, and you should decompress it before encoding it to MP3. You can use a library like FFmpeg or AVFoundation in Swift to do this. You'll end up with PCM audio data, which is uncompressed. Convert m4a to WAV

  • Encode to MP3: Once you have the raw PCM audio data, you can encode it to MP3. LAME is a popular library for encoding audio to MP3 format. Make sure you're passing the raw audio data (uncompressed) to LAME for encoding. AudioConverter.swift

  • Set Appropriate MP3 Parameters: When encoding to MP3, you can set various parameters like bitrate, sample rate, and quality. Adjust these parameters according to your needs. Higher bitrates typically result in better audio quality but larger file sizes. AudioConverter.swift

  • Save the MP3 File: Finally, save the encoded MP3 audio to a file. Make sure you provide a valid file path for the output.

Thanks!

from lame.

tryWabbit avatar tryWabbit commented on July 28, 2024 1

Thank you so much @pro100andrey the mistake I did was I put the lame_set_num_channels(lame, 1) after the lame_init_params(lame).

Really appreciate your help : )

from lame.

pro100andrey avatar pro100andrey commented on July 28, 2024

Hi @tryWabbit, You are not decoding the M4A audio into raw audio data before sending it into LAME.

"PCM audio" means no compression.

from lame.

tryWabbit avatar tryWabbit commented on July 28, 2024

Thanks for the quick reply @pro100andrey ! I have limited experience with audio conversions. Can you explain me in detail what updates I need to make to get an mp3 file out of a m4a file. Really appreaciate your help!

from lame.

pro100andrey avatar pro100andrey commented on July 28, 2024

@tryWabbit

If you're recording audio directly from a microphone and want to save it in MP3 format, you can use the LAME library to convert PCM audio to MP3 on the fly. LAME provides the capability to encode audio into MP3 format while recording or streaming.

For example AURenderCallback

from lame.

tryWabbit avatar tryWabbit commented on July 28, 2024

Thank you so much for the detailed explanation @pro100andrey Really appreciate your help! I will try and update you

from lame.

tryWabbit avatar tryWabbit commented on July 28, 2024

Hey @pro100andrey I tried your solution and it worked! The only problem I have right now is the converted audio is half of the size and playing like it's in fast forward. I'm sure there is a difference between the configuration I'm converting it in and your configs for conversion. Can you verify?

Method I'm using to convert m4a to wav. The output is fine and match the audio.

    func convertAudio(_ url: URL, outputURL: URL) {
        var error : OSStatus = noErr
        var destinationFile : ExtAudioFileRef? = nil
        var sourceFile : ExtAudioFileRef? = nil

        var srcFormat : AudioStreamBasicDescription = AudioStreamBasicDescription()
        var dstFormat : AudioStreamBasicDescription = AudioStreamBasicDescription()

        ExtAudioFileOpenURL(url as CFURL, &sourceFile)

        var thePropertySize: UInt32 = UInt32(MemoryLayout.stride(ofValue: srcFormat))

        ExtAudioFileGetProperty(sourceFile!,
            kExtAudioFileProperty_FileDataFormat,
            &thePropertySize, &srcFormat)
        
        dstFormat.mSampleRate = 44100  //Set sample rate
        dstFormat.mFormatID = kAudioFormatLinearPCM
        dstFormat.mChannelsPerFrame = 1
        dstFormat.mBitsPerChannel = 16
        dstFormat.mBytesPerPacket = 2 * dstFormat.mChannelsPerFrame
        dstFormat.mBytesPerFrame = 2 * dstFormat.mChannelsPerFrame
        dstFormat.mFramesPerPacket = 1
        dstFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked |
        kAudioFormatFlagIsSignedInteger


        // Create destination file
        error = ExtAudioFileCreateWithURL(
            outputURL as CFURL,
            kAudioFileWAVEType,
            &dstFormat,
            nil,
            AudioFileFlags.eraseFile.rawValue,
            &destinationFile)
        reportError(error: error)

        error = ExtAudioFileSetProperty(sourceFile!,
                kExtAudioFileProperty_ClientDataFormat,
                thePropertySize,
                &dstFormat)
        reportError(error: error)

        error = ExtAudioFileSetProperty(destinationFile!,
                                         kExtAudioFileProperty_ClientDataFormat,
                                        thePropertySize,
                                        &dstFormat)
        reportError(error: error)

        let bufferByteSize : UInt32 = 32768
        var srcBuffer = [UInt8](repeating: 0, count: 32768)
        var sourceFrameOffset : ULONG = 0

        while(true){
            var fillBufList = AudioBufferList(
                mNumberBuffers: 1,
                mBuffers: AudioBuffer(
                    mNumberChannels: 2,
                    mDataByteSize: UInt32(srcBuffer.count),
                    mData: &srcBuffer
                )
            )
            var numFrames : UInt32 = 0

            if(dstFormat.mBytesPerFrame > 0){
                numFrames = bufferByteSize / dstFormat.mBytesPerFrame
            }

            error = ExtAudioFileRead(sourceFile!, &numFrames, &fillBufList)
            reportError(error: error)

            if(numFrames == 0){
                error = noErr;
                break;
            }
            
            sourceFrameOffset += numFrames
            error = ExtAudioFileWrite(destinationFile!, numFrames, &fillBufList)
            reportError(error: error)
        }
        
        error = ExtAudioFileDispose(destinationFile!)
        reportError(error: error)
        error = ExtAudioFileDispose(sourceFile!)
        reportError(error: error)
    }

Your method of conversion from wav to mp3

class func encodeToMp3(
        inPcmPath: String,
        outMp3Path: String,
        onProgress: @escaping (Float) -> (Void),
        onComplete: @escaping () -> (Void)
    ) {

        encoderQueue.async {

            let lame = lame_init()
            lame_set_in_samplerate(lame, 44100)
            lame_set_out_samplerate(lame, 0)
            lame_set_brate(lame, 0)
            lame_set_quality(lame, 4)
            lame_set_VBR(lame, vbr_off)
            lame_init_params(lame)
            

            let pcmFile: UnsafeMutablePointer<FILE> = fopen(inPcmPath, "rb")
            fseek(pcmFile, 0 , SEEK_END)
            
            let fileSize = ftell(pcmFile)
            // Skip file header.
            let pcmHeaderSize = 48 * 8
            fseek(pcmFile, pcmHeaderSize, SEEK_SET)

            let mp3File: UnsafeMutablePointer<FILE> = fopen(outMp3Path, "wb")

            let pcmSize = 1024 * 8
            let pcmbuffer = UnsafeMutablePointer<Int16>.allocate(capacity: Int(pcmSize * 2))

            let mp3Size: Int32 = 1024 * 8
            let mp3buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(mp3Size))

            var write: Int32 = 0
            var read = 0

            repeat {

                let size = MemoryLayout<Int16>.size * 2
                read = fread(pcmbuffer, size, pcmSize, pcmFile)
                // Progress
                if read != 0 {
                    let progress = Float(ftell(pcmFile)) / Float(fileSize)
                    DispatchQueue.main.sync { onProgress(progress) }
                }

                if read == 0 {
                    write = lame_encode_flush_nogap(lame, mp3buffer, mp3Size)
                } else {
                    write = lame_encode_buffer_interleaved(lame, pcmbuffer, Int32(read), mp3buffer, mp3Size)
                }

                fwrite(mp3buffer, Int(write), 1, mp3File)

            } while read != 0

            // Clean up
            lame_close(lame)
            fclose(mp3File)
            fclose(pcmFile)

            pcmbuffer.deallocate()
            mp3buffer.deallocate()

            DispatchQueue.main.sync { onComplete() }
        }
    }

Can you identify the difference in conversion which is causing the audio to be fast the half of the actual duration ?

Thanks

from lame.

pro100andrey avatar pro100andrey commented on July 28, 2024

Hi, @tryWabbit, if your file contains 1 channel, try set lame_set_num_channels to 1

/* number of channels in input stream. default=2 */
int CDECL lame_set_num_channels(lame_global_flags *, int);

from lame.

pro100andrey avatar pro100andrey commented on July 28, 2024

@tryWabbit
I noticed that you can improve your code. You can combine the two functions func convertAudio(_ url:, outputURL:) and func encodeToMp3(inPcmPath:, outMp3Path:, onProgress:, onComplete:) into one. After ExtAudioFileRead(sourceFile!, &numFrames, &fillBufList), you can directly write fillBufList to MP3 without intermediate saving to a WAV file.

Think about it :).

from lame.

tryWabbit avatar tryWabbit commented on July 28, 2024

Hi, @tryWabbit, if your file contains 1 channel, try set lame_set_num_channels to 1

/* number of channels in input stream. default=2 */ int CDECL lame_set_num_channels(lame_global_flags *, int);

I set the number of channels to 1 by lame_set_num_channels(lame, 1) but it is still generating audio which is having half of the duration and fast playing

I'm sorry if I don't make sense I don't have any experience with lame and c apis and have limited experience with audio apis for ios.

from lame.

tryWabbit avatar tryWabbit commented on July 28, 2024

@tryWabbit I noticed that you can improve your code. You can combine the two functions func convertAudio(_ url:, outputURL:) and func encodeToMp3(inPcmPath:, outMp3Path:, onProgress:, onComplete:) into one. After ExtAudioFileRead(sourceFile!, &numFrames, &fillBufList), you can directly write fillBufList to MP3 without intermediate saving to a WAV file.

Think about it :).

Thank you so much for suggesting that. I will definitely focus on this once I get it working.

from lame.

tryWabbit avatar tryWabbit commented on July 28, 2024

I uploaded a testing project on which I'm doing the experiment here in case you want to see the issue - https://github.com/tryWabbit/Audio-Conversion

from lame.

pro100andrey avatar pro100andrey commented on July 28, 2024

@tryWabbit result without issue (replaced lame_encode_buffer_interleaved with lame_encode_buffer with empty right channel.)

//
//  AudioConverter.swift
//  Example
//
//  Created by Andrey on 20.11.2020.
//

import Foundation
import lame

class AudioConverter {

    private static let encoderQueue = DispatchQueue(label: "com.audio.encoder.queue")

    class func encodeToMp3(
        inPcmPath: String,
        outMp3Path: String,
        
        onProgress: @escaping (Float) -> (Void),
        onComplete: @escaping () -> (Void)
    ) {

        encoderQueue.async {
            
            let numOfChannels: Int32 = 1

            let lame = lame_init()
            lame_set_in_samplerate(lame, 44100)
            lame_set_out_samplerate(lame, 0)
            lame_set_brate(lame, 0)
            lame_set_quality(lame, 4)
            lame_set_VBR(lame, vbr_off)
            lame_set_num_channels(lame, numOfChannels)
            lame_init_params(lame)
            
            let pcmFile: UnsafeMutablePointer<FILE> = fopen(inPcmPath, "rb")
            fseek(pcmFile, 0 , SEEK_END)
            
            let fileSize = ftell(pcmFile)
            // Skip file header.
            let pcmHeaderSize = 48 * 8
            fseek(pcmFile, pcmHeaderSize, SEEK_SET)

            let mp3File: UnsafeMutablePointer<FILE> = fopen(outMp3Path, "wb")

            let pcmSize = 1024 * 8
            let pcmbuffer = UnsafeMutablePointer<Int16>.allocate(capacity: Int(pcmSize * 2))

            let mp3Size: Int32 = 1024 * 8
            let mp3buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(mp3Size))

            var write: Int32 = 0
            var read = 0

            repeat {

                let size = MemoryLayout<Int16>.size * Int(numOfChannels)
                read = fread(pcmbuffer, size, pcmSize, pcmFile)
                // Progress
                if read != 0 {
                    let progress = Float(ftell(pcmFile)) / Float(fileSize)
                    DispatchQueue.main.sync { onProgress(progress) }
                }

                if read == 0 {
                    write = lame_encode_flush_nogap(lame, mp3buffer, mp3Size)
                } else {
                    write = lame_encode_buffer(lame, pcmbuffer, [] ,Int32(read), mp3buffer, mp3Size)
                }

                fwrite(mp3buffer, Int(write), 1, mp3File)

            } while read != 0

            // Clean up
            lame_close(lame)
            fclose(mp3File)
            fclose(pcmFile)

            pcmbuffer.deallocate()
            mp3buffer.deallocate()

            DispatchQueue.main.sync { onComplete() }
        }
    }
}

from lame.

Related Issues (12)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.