iOS Swift - 将.wav文件合并并转换为.mp3

时间:2023-01-24 12:10:18

I want to merge two or more .wav files to one and then convert it to .mp3 and this I would like to done in Swift (or at least to have option to include it to swift project).

我想将两个或多个.wav文件合并为一个,然后将其转换为.mp3,我想在Swift中完成(或者至少可以选择将它包含在swift项目中)。

Merge two .wav files in swift isn't problem. Here is my example Now I don't know how to add lame library to swift project and how to use it (how to change objective c lame code usage syntax to use it in swift).

在swift中合并两个.wav文件不是问题。这是我的示例现在我不知道如何将lame库添加到swift项目以及如何使用它(如何更改客观的蹩脚代码使用语法以在swift中使用它)。

I stuck in swift so I tried Lame library with Objective C. I found example code for converting .caf to .mp3 so I tried it. Here is what I've tried:

我坚持使用swift,所以我尝试了使用Objective C的Lame库。我找到了将.caf转换为.mp3的示例代码,所以我试了一下。这是我尝试过的:

- (void) toMp3
{
    NSString *cafFilePath = [[NSBundle mainBundle] pathForResource:@"sound" ofType:@"caf"];

    NSString *mp3FileName = @"Mp3File";
    mp3FileName = [mp3FileName stringByAppendingString:@".mp3"];
    NSString *mp3FilePath = [[NSHomeDirectory() stringByAppendingFormat:@"/Documents/"] stringByAppendingPathComponent:mp3FileName];

    NSLog(@"%@", mp3FilePath);

    @try {
        int read, write;

        FILE *pcm = fopen([cafFilePath cStringUsingEncoding:1], "rb");  //source
        fseek(pcm, 4*1024, SEEK_CUR);                                   //skip file header
        FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb");  //output

        const int PCM_SIZE = 8192;
        const int MP3_SIZE = 8192;
        short int pcm_buffer[PCM_SIZE*2];
        unsigned char mp3_buffer[MP3_SIZE];

        lame_t lame = lame_init();
        lame_set_in_samplerate(lame, 44100);
        lame_set_VBR(lame, vbr_default);
        lame_init_params(lame);

        do {
            read = fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm);
            if (read == 0)
                write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
            else
                write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);

            fwrite(mp3_buffer, write, 1, mp3);

        } while (read != 0);

        lame_close(lame);
        fclose(mp3);
        fclose(pcm);
    }
    @catch (NSException *exception) {
        NSLog(@"%@",[exception description]);
    }
    @finally {
        [self performSelectorOnMainThread:@selector(convertMp3Finish)
                               withObject:nil
                            waitUntilDone:YES];
    }
}

- (void) convertMp3Finish
{
}

But result of this is just .mp3 with noise.

但结果只是带有噪音的.mp3。

So I need fix my three problems:

所以我需要解决我的三个问题:

  • Create correct mp3 from caf in Objective C
  • 在Objective C中从caf创建正确的mp3
  • Change code to use it for wav file
  • 更改代码以将其用于wav文件
  • And change it to be able to use it in Swift
  • 并将其更改为能够在Swift中使用它

I know that there are many questions about encoding and converting mp3 in iOS but I can't find one with Swift example and I can't find example with working Objective C code (just code above). Thanks for help

我知道在iOS中有很多关于编码和转换mp3的问题,但是我找不到一个使用Swift的例子,我找不到使用Objective C代码的例子(只是上面的代码)。感谢帮助

2 个解决方案

#1


10  

I would like to post my working solution because I get so many thumbs up and answer from naresh doesn't help me much.

我想发布我的工作解决方案,因为我得到了很多赞许,来自naresh的回答对我没什么帮助。

  1. I have generated lame.framework library from this project https://github.com/wuqiong/mp3lame-for-iOS
  2. 我从这个项目生成了lame.framework库https://github.com/wuqiong/mp3lame-for-iOS
  3. I've added library to my Swift project (Build Phases -> Link Binary With Libraries)
  4. 我已经将库添加到我的Swift项目中(Build Phases - > Link Binary with Libraries)
  5. I've created wrapper for using c functions in Objective C and by bridging header I use it in Swift.
  6. 我已经创建了在Objective C中使用c函数的包装器,并通过桥接头我在Swift中使用它。
  7. For concatenate wav files I use AVAssetExportSession with Swift
  8. 对于连接wav文件,我使用AVAssetExportSession和Swift

And now source codes. So first wrapper. It's class for converting .wav files to .mp3. There could be many changes (maybe parameter for output file and other options) but I think everyone could change it. I guess this could be rewritten to Swift but I wasn't sure how to do it. So it's Objective C class:

现在源代码。首先包装。它是将.wav文件转换为.mp3的类。可能会有很多变化(可能是输出文件和其他选项的参数),但我认为每个人都可以改变它。我想这可以改写为Swift,但我不知道该怎么做。所以它是Objective C类:

#import "AudioWrapper.h"
#import "lame/lame.h"

@implementation AudioWrapper

+ (void)convertFromWavToMp3:(NSString *)filePath {


    NSString *mp3FileName = @"Mp3File";
    mp3FileName = [mp3FileName stringByAppendingString:@".mp3"];
    NSString *mp3FilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:mp3FileName];

    NSLog(@"%@", mp3FilePath);

    @try {
        int read, write;

        FILE *pcm = fopen([filePath cStringUsingEncoding:1], "rb");  //source
        fseek(pcm, 4*1024, SEEK_CUR);                                   //skip file header
        FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb");  //output

        const int PCM_SIZE = 8192;
        const int MP3_SIZE = 8192;
        short int pcm_buffer[PCM_SIZE*2];
        unsigned char mp3_buffer[MP3_SIZE];

        lame_t lame = lame_init();
        lame_set_in_samplerate(lame, 44100);
        lame_set_VBR(lame, vbr_default);
        lame_init_params(lame);

        do {
            read = fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm);
            if (read == 0)
                write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
            else
                write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);

            fwrite(mp3_buffer, write, 1, mp3);

        } while (read != 0);

        lame_close(lame);
        fclose(mp3);
        fclose(pcm);
    }
    @catch (NSException *exception) {
        NSLog(@"%@",[exception description]);
    }
    @finally {
        [self performSelectorOnMainThread:@selector(convertMp3Finish)
                               withObject:nil
                            waitUntilDone:YES];
    }
}

Swift AudioHelper class for concatening audio files and calling method for converting .wav file to .mp3:

用于连接音频文件的Swift AudioHelper类和用于将.wav文件转换为.mp3的调用方法:

import UIKit
import AVFoundation


protocol AudioHelperDelegate {
    func assetExportSessionDidFinishExport(session: AVAssetExportSession, outputUrl: NSURL)
}

class AudioHelper: NSObject {

    var delegate: AudioHelperDelegate?

    func concatenate(audioUrls: [NSURL]) {

        //Create AVMutableComposition Object.This object will hold our multiple AVMutableCompositionTrack.
        var composition = AVMutableComposition()
        var compositionAudioTrack:AVMutableCompositionTrack = composition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())

        //create new file to receive data
        var documentDirectoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first! as! NSURL
        var fileDestinationUrl = NSURL(fileURLWithPath: NSTemporaryDirectory().stringByAppendingPathComponent("resultmerge.wav"))
        println(fileDestinationUrl)

        StorageManager.sharedInstance.deleteFileAtPath(NSTemporaryDirectory().stringByAppendingPathComponent("resultmerge.wav"))

        var avAssets: [AVURLAsset] = []
        var assetTracks: [AVAssetTrack] = []
        var durations: [CMTime] = []
        var timeRanges: [CMTimeRange] = []

        var insertTime = kCMTimeZero

        for audioUrl in audioUrls {
            let avAsset = AVURLAsset(URL: audioUrl, options: nil)
            avAssets.append(avAsset)

            let assetTrack = avAsset.tracksWithMediaType(AVMediaTypeAudio)[0] as! AVAssetTrack
            assetTracks.append(assetTrack)

            let duration = assetTrack.timeRange.duration
            durations.append(duration)

            let timeRange = CMTimeRangeMake(kCMTimeZero, duration)
            timeRanges.append(timeRange)

            compositionAudioTrack.insertTimeRange(timeRange, ofTrack: assetTrack, atTime: insertTime, error: nil)
            insertTime = CMTimeAdd(insertTime, duration)
        }

        //AVAssetExportPresetPassthrough => concatenation
        var assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetPassthrough)
        assetExport.outputFileType = AVFileTypeWAVE
        assetExport.outputURL = fileDestinationUrl
        assetExport.exportAsynchronouslyWithCompletionHandler({
            self.delegate?.assetExportSessionDidFinishExport(assetExport, outputUrl: fileDestinationUrl!)
        })
    }

    func exportTempWavAsMp3() {

        let wavFilePath = NSTemporaryDirectory().stringByAppendingPathComponent("resultmerge.wav")
        AudioWrapper.convertFromWavToMp3(wavFilePath)
    }
}

Bridging header contains:

桥接头包含:

#import "lame/lame.h"
#import "AudioWrapper.h"

#2


6  

We have dedicated classes to read/write media from/to a file they are AVAssetReader and AVAssetWriter and with the help of AVAssetExportSession you can export it as mp3 file. or else you can use https://github.com/michaeltyson/TPAACAudioConverter

我们有专门的类来从/向AVAssetReader和AVAssetWriter文件读取/写入媒体,并且在AVAssetExportSession的帮助下,您可以将其导出为mp3文件。或者你可以使用https://github.com/michaeltyson/TPAACAudioConverter

#1


10  

I would like to post my working solution because I get so many thumbs up and answer from naresh doesn't help me much.

我想发布我的工作解决方案,因为我得到了很多赞许,来自naresh的回答对我没什么帮助。

  1. I have generated lame.framework library from this project https://github.com/wuqiong/mp3lame-for-iOS
  2. 我从这个项目生成了lame.framework库https://github.com/wuqiong/mp3lame-for-iOS
  3. I've added library to my Swift project (Build Phases -> Link Binary With Libraries)
  4. 我已经将库添加到我的Swift项目中(Build Phases - > Link Binary with Libraries)
  5. I've created wrapper for using c functions in Objective C and by bridging header I use it in Swift.
  6. 我已经创建了在Objective C中使用c函数的包装器,并通过桥接头我在Swift中使用它。
  7. For concatenate wav files I use AVAssetExportSession with Swift
  8. 对于连接wav文件,我使用AVAssetExportSession和Swift

And now source codes. So first wrapper. It's class for converting .wav files to .mp3. There could be many changes (maybe parameter for output file and other options) but I think everyone could change it. I guess this could be rewritten to Swift but I wasn't sure how to do it. So it's Objective C class:

现在源代码。首先包装。它是将.wav文件转换为.mp3的类。可能会有很多变化(可能是输出文件和其他选项的参数),但我认为每个人都可以改变它。我想这可以改写为Swift,但我不知道该怎么做。所以它是Objective C类:

#import "AudioWrapper.h"
#import "lame/lame.h"

@implementation AudioWrapper

+ (void)convertFromWavToMp3:(NSString *)filePath {


    NSString *mp3FileName = @"Mp3File";
    mp3FileName = [mp3FileName stringByAppendingString:@".mp3"];
    NSString *mp3FilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:mp3FileName];

    NSLog(@"%@", mp3FilePath);

    @try {
        int read, write;

        FILE *pcm = fopen([filePath cStringUsingEncoding:1], "rb");  //source
        fseek(pcm, 4*1024, SEEK_CUR);                                   //skip file header
        FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb");  //output

        const int PCM_SIZE = 8192;
        const int MP3_SIZE = 8192;
        short int pcm_buffer[PCM_SIZE*2];
        unsigned char mp3_buffer[MP3_SIZE];

        lame_t lame = lame_init();
        lame_set_in_samplerate(lame, 44100);
        lame_set_VBR(lame, vbr_default);
        lame_init_params(lame);

        do {
            read = fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm);
            if (read == 0)
                write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
            else
                write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);

            fwrite(mp3_buffer, write, 1, mp3);

        } while (read != 0);

        lame_close(lame);
        fclose(mp3);
        fclose(pcm);
    }
    @catch (NSException *exception) {
        NSLog(@"%@",[exception description]);
    }
    @finally {
        [self performSelectorOnMainThread:@selector(convertMp3Finish)
                               withObject:nil
                            waitUntilDone:YES];
    }
}

Swift AudioHelper class for concatening audio files and calling method for converting .wav file to .mp3:

用于连接音频文件的Swift AudioHelper类和用于将.wav文件转换为.mp3的调用方法:

import UIKit
import AVFoundation


protocol AudioHelperDelegate {
    func assetExportSessionDidFinishExport(session: AVAssetExportSession, outputUrl: NSURL)
}

class AudioHelper: NSObject {

    var delegate: AudioHelperDelegate?

    func concatenate(audioUrls: [NSURL]) {

        //Create AVMutableComposition Object.This object will hold our multiple AVMutableCompositionTrack.
        var composition = AVMutableComposition()
        var compositionAudioTrack:AVMutableCompositionTrack = composition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())

        //create new file to receive data
        var documentDirectoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first! as! NSURL
        var fileDestinationUrl = NSURL(fileURLWithPath: NSTemporaryDirectory().stringByAppendingPathComponent("resultmerge.wav"))
        println(fileDestinationUrl)

        StorageManager.sharedInstance.deleteFileAtPath(NSTemporaryDirectory().stringByAppendingPathComponent("resultmerge.wav"))

        var avAssets: [AVURLAsset] = []
        var assetTracks: [AVAssetTrack] = []
        var durations: [CMTime] = []
        var timeRanges: [CMTimeRange] = []

        var insertTime = kCMTimeZero

        for audioUrl in audioUrls {
            let avAsset = AVURLAsset(URL: audioUrl, options: nil)
            avAssets.append(avAsset)

            let assetTrack = avAsset.tracksWithMediaType(AVMediaTypeAudio)[0] as! AVAssetTrack
            assetTracks.append(assetTrack)

            let duration = assetTrack.timeRange.duration
            durations.append(duration)

            let timeRange = CMTimeRangeMake(kCMTimeZero, duration)
            timeRanges.append(timeRange)

            compositionAudioTrack.insertTimeRange(timeRange, ofTrack: assetTrack, atTime: insertTime, error: nil)
            insertTime = CMTimeAdd(insertTime, duration)
        }

        //AVAssetExportPresetPassthrough => concatenation
        var assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetPassthrough)
        assetExport.outputFileType = AVFileTypeWAVE
        assetExport.outputURL = fileDestinationUrl
        assetExport.exportAsynchronouslyWithCompletionHandler({
            self.delegate?.assetExportSessionDidFinishExport(assetExport, outputUrl: fileDestinationUrl!)
        })
    }

    func exportTempWavAsMp3() {

        let wavFilePath = NSTemporaryDirectory().stringByAppendingPathComponent("resultmerge.wav")
        AudioWrapper.convertFromWavToMp3(wavFilePath)
    }
}

Bridging header contains:

桥接头包含:

#import "lame/lame.h"
#import "AudioWrapper.h"

#2


6  

We have dedicated classes to read/write media from/to a file they are AVAssetReader and AVAssetWriter and with the help of AVAssetExportSession you can export it as mp3 file. or else you can use https://github.com/michaeltyson/TPAACAudioConverter

我们有专门的类来从/向AVAssetReader和AVAssetWriter文件读取/写入媒体,并且在AVAssetExportSession的帮助下,您可以将其导出为mp3文件。或者你可以使用https://github.com/michaeltyson/TPAACAudioConverter