core audio how to generate a square wave with two channel ( stereo) - objective-c

It's the code from Learning Core Audio http://www.amazon.com/Learning-Core-Audio-Hands-On-Programming/dp/0321636848
Just like the book says ,generating stereo should set asbd.mBitsPerChannel to 8 and asbd.mChannelsPerFrame to 2 . But the audio I get only has one channel . I don't know what's wrong with the code ,please help me . Thanks
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#define SAMPLE_RATE 44100
#define DURATION 5.0
#define FILENAME_FORMAT #"%0.3f-square.aif"
int main(int argc, const char * argv[])
{
#autoreleasepool {
if (argc < 2) {
printf("Usage: CAToneFileGenerator n\n(where n is tone in Hz)");
return -1;
}
double hz = atof(argv[1]);
assert(hz > 0);
NSLog(#"generating %f hz tone", hz);
NSString *fileName = [NSString stringWithFormat:FILENAME_FORMAT, hz];
NSString *filePath = [[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:fileName];
NSLog(#"%#", filePath);
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mSampleRate = SAMPLE_RATE;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFormatFlags = kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
asbd.mBitsPerChannel = 8;
asbd.mChannelsPerFrame = 2;
asbd.mFramesPerPacket = 1;
asbd.mBytesPerFrame = 2;
asbd.mBytesPerPacket = 2;
AudioFileID audioFile;
OSStatus audioErr = noErr;
audioErr = AudioFileCreateWithURL((CFURLRef) fileURL, kAudioFileAIFFType, &asbd, kAudioFileFlags_EraseFile, &audioFile);
assert(audioErr == noErr);
long maxSampleCount = SAMPLE_RATE * DURATION;
long sampleCount = 0;
UInt32 bytesToWrite = 2;
double wavelengthInSamples = SAMPLE_RATE / hz;
while (sampleCount < maxSampleCount) {
for (int i = 0; i < wavelengthInSamples; i++) {
SInt16 sample;
if (i < wavelengthInSamples /2) {
sample = CFSwapInt16HostToBig(SHRT_MAX);
}else{
sample = CFSwapInt16HostToBig(SHRT_MIN);
}
audioErr = AudioFileWriteBytes(audioFile, false, sampleCount*2, &bytesToWrite, &sample);
assert(audioErr == noErr);
sampleCount ++;
}
}
audioErr = AudioFileClose(audioFile);
assert(audioErr == noErr);
NSLog(#"wrote %ld samples", sampleCount);
}
return 0;
}

Just changing the ASBD from the book code doesn't magically fix everything. You haven't accounted for how you're writing the samples to the file. Also, 8-bit is going to sound like ass.
Go back to mBitsPerChannel = 16, and then account for the fact you're writing two channels per frame, meaning that mBytesPerFrame and mBytesPerPacket will now be 4 (they were 2 in the book). Think about why this is.
Then you should just be able to add a second call to AudioFileWriteBytes() -- or do a loop where you count over mChannelsPerFrame -- right after the first one. But you'll have to account for the different offsets in the file, since you're writing 4 bytes each pass instead of 2. I think this is right:
audioErr = AudioFileWriteBytes(audioFile, false, sampleCount*4, &bytesToWrite, &sample); // left
audioErr = AudioFileWriteBytes(audioFile, false, (sampleCount*4)+2, &bytesToWrite, &sample); // right
You need to figure out some of this stuff on your own in order for it to sink in.

Related

Dividing file into 10MB chunks

I have a script that divides file into 10MB chunks. Haven't had a problem with this script until I tried to do it on a 6GB file. Getting negative values on ranges even if they are uint64_t. Any suggestions on where is the error?
NSData *conData = [NSURLConnection sendSynchronousRequest:fileSizeRequest returningResponse:&response error:&error];
if (conData)
{
NSDictionary *headers = [response allHeaderFields];
NSString *fileSizeString = [headers objectForKey:#"Content-Length"];
uint64_t fileSize = strtoull([fileSizeString UTF8String], NULL, 0);
self.size += fileSize;
uint64_t amountOfRanges = fileSize / 10485760;
for (int i = 0; i <= amountOfRanges; i++)
{
uint64_t rangeMin = 0;
uint64_t rangeMax = 0;
if (i != amountOfRanges)
{
rangeMin = i * 10485760;
rangeMax = (i + 1) * 10485760 - 1;
}
else
{
if (i == 0)
{
rangeMin = 0;
rangeMax = fileSize - 1;
}
else
{
rangeMin = i * 10485760;
rangeMax = i * 10485760 - 1 + (fileSize - rangeMin);
}
}
}
}
You have a problem with expressions such as this:
rangeMin = i * 10485760;
Note that i is an int and 10485760 is an int literal, so the resulting int expression can easily overflow. You should ideally make i a uint64_t and/or use unsigned long long literals, e.g.
rangeMin = i * 10485760ULL;

Accelerate Framework FFT to create audio fingerprint in iOS

First of all, sorry for my bad English.
I just started with the Objective-C a couple of week ago. I'm doing a project that require me to compared two audio signals recorded from two iOS devices. So far, I managed to record two .aif files from an iPhone 4s and iPhone 4. Then I try to apply the following algorithm A Highly Robust Audio Fingerprinting System. by: Jaap Haitsma" to get two fingerprints (in binary bit patterns 101011010) and then compare them with each other bits by bits. But so far, the result I got is somewhere between 45% and 55% which is pretty much the random probability between 0s and 1s. So can somebody give me any advice. Here's the code so far:
CalculateFingerprint *myCalculateFingerprint = [CalculateFingerprint alloc];
SInt16 *inputBuffer;
path4 = [documentsDirectory stringByAppendingPathComponent:fileName4];
/////////Calculate for the 4 file
fileURL = [NSURL fileURLWithPath:path4];
status = AudioFileOpenURL((__bridge CFURLRef)fileURL, kAudioFileReadPermission,kAudioFileAIFFType, &myAudioFile);
status = AudioFileGetPropertyInfo(myAudioFile,
kAudioFilePropertyAudioDataPacketCount,
&propertySizeDataPacketCount,
&writabilityDataPacketCount);
status = AudioFileGetProperty(myAudioFile,
kAudioFilePropertyAudioDataPacketCount,
&propertySizeDataPacketCount,
&numberOfPackets);
status = AudioFileGetPropertyInfo (myAudioFile,
kAudioFilePropertyMaximumPacketSize,
&propertySizeMaxPacketSize,
&writabilityMaxPacketSize);
status = AudioFileGetProperty(myAudioFile,
kAudioFilePropertyMaximumPacketSize,
&propertySizeMaxPacketSize,
&maxPacketSize);
inputBuffer = (SInt16 *)malloc(numberOfPackets * maxPacketSize);
currentPacket = 0;
status = AudioFileReadPackets(myAudioFile,
false, &numberOfBytesRead,
NULL,
currentPacket,
&numberOfPackets,
inputBuffer);
[myCalculateFingerprint calculateFingerprint:inputBuffer sampleCount:numberOfPackets index:indexFile];
status = AudioFileClose(myAudioFile);
Here's the calculation of fingerprint code:
-(void)calculateFingerprint :(SInt16*)samples
sampleCount:(int)sampleCount
index:(int)indexFile{
//Divide the audio signal into 32 frames
frames myFrames [32];
int stepFrames = sampleCount / 62;
int number = 0;
int index ;
for (int i = 0; i < 32; ++i){
index = 0;
myFrames[i].start = number;
myFrames[i].end = number + (32*stepFrames);
myFrames[i].dataFrames = (SInt16*)malloc((myFrames[i].end -number+1)*sizeof(SInt16));
for (int j = number;j<=myFrames[i].end; ++j){
myFrames[i].dataFrames[index] = samples[j];
++index;
}
number = number + stepFrames;
}
//Calculate FFT for each of the audio signal frames.
CalculateFFT *myCalculateFFT = [[CalculateFFT alloc] init];
theFFT myFFTData [32];
for (int i = 0; i <32; ++i){
myFFTData[i].FFTdata = [myCalculateFFT calculateFFTForData:myFrames[i].dataFrames];
}
//each index represent the frequency as followed:
// index i is frequency i * 44100/1024
//We only need 33 bands from 300 Hz to 2000Hz, so we will get the FFTdata from the index 7 to 40
float energy [33][33];
for (int i =0; i < 33; ++i){
energy[0][i] = 0;
}
int stepBand;
for (int i = 1; i < 33; ++i){
for (int j = 0; j < 33; ++j){
energy[i][j] = myFFTData[i].FFTdata[j+7];
}
}
//next we calculate the bits for the audio fingerprint
Float32 check = 0;
int fingerPrint [32][32];
NSMutableString *result = [[NSMutableString alloc]init];
for (int i = 0; i < 32; ++i){
for (int j = 0; j <32; ++j){
check = energy[i+1][j] -energy[i+1][j+1] -energy[i][j] +energy[i][j+1];
if (check > 0){
fingerPrint[i][j] = 1;//[tempBitFingerPrint addObject:[NSNumber numberWithInt:1]];
}else {
fingerPrint[i][j] = 0;//[tempBitFingerPrint addObject:[NSNumber numberWithInt:0]];
}
[result appendString:[NSString stringWithFormat:#"%d",fingerPrint[i][j]]];
}
}
And finally the FFT calculation code:
-(void)FFTSetup{
UInt32 maxFrames = 1024;
originalReal = (float*) malloc(maxFrames*sizeof(float));
originalRealTransfer = (float*)malloc(maxFrames*sizeof(float));
obtainedReal = (float*) malloc(maxFrames *sizeof(float));
freqArray = (Float32*) malloc((maxFrames/2) *sizeof(Float32));
fftLog2n = log2f(maxFrames);
fftN = 1 << fftLog2n;
fftNOver2 = maxFrames/2;
fftBufferCapacity = maxFrames;
fftIndex = 0;
fftA.realp = (float*)malloc(fftNOver2*sizeof(float));
fftA.imagp = (float*)malloc(fftNOver2*sizeof(float));
fftSetup = vDSP_create_fftsetup(fftLog2n,FFT_RADIX2);
}
-(Float32*) calculateFFTForData:(SInt16*)sampleData
{
[self FFTSetup];
int stride = 1;
for (int i = 0; i < fftN; ++i){
originalReal[i] = (float) sampleData[i];
}
UInt32 maxFrames = 1024;
//Apply Hann window on the data
int windowSize = maxFrames;
float * window = (float*)malloc(sizeof(float)*windowSize);
memset(window, 0, sizeof(float)*windowSize);
vDSP_hann_window(window, windowSize, vDSP_HANN_NORM);
vDSP_vmul(originalReal,1,window,1,originalRealTransfer,1,windowSize);
vDSP_ctoz((COMPLEX*) originalRealTransfer,2,&fftA,1,fftNOver2);
vDSP_fft_zrip(fftSetup,&fftA, stride,fftLog2n,FFT_FORWARD);
float scale = (float) 1.0 /(2*fftN);
vDSP_vsmul(fftA.realp,1,&scale,fftA.realp,1,fftNOver2);
vDSP_vsmul(fftA.imagp,1,&scale,fftA.imagp,1,fftNOver2);
vDSP_ztoc(&fftA,1,(COMPLEX*)obtainedReal,2,fftNOver2);
int index = 0;
NSMutableString *testResult = [[NSMutableString alloc]init];
for (int i = 0; i < fftN; i=i+2){
freqArray[index] = (obtainedReal[i]*obtainedReal[i])+(obtainedReal[i+1]*obtainedReal[i+1]);
[testResult appendString:[NSString stringWithFormat:#"%f ",freqArray[index]]];
++index;
}
return freqArray;
}

audio unit playing m4a files

I've been working on this task for 12 days and i cant find any solution pleaaaaase help
i'm supposed to load about 80 m4a files and play some of them with augraph which contains mixer and remoteIO units thats how i load the files
OSStatus result;
for (int i = 0; i < [filePaths count]; i++) {
NSMutableArray *linearr=[[NSMutableArray alloc] init];
for (int j = 0; j < [[filePaths objectAtIndex:i] count]; j++) {
NSString *str=[[filePaths objectAtIndex:i] objectAtIndex:j];
CFURLRef audioFileURL = CFURLCreateFromFileSystemRepresentation (NULL, (const UInt8 *)[str cStringUsingEncoding:[NSString defaultCStringEncoding]] , strlen([str cStringUsingEncoding:[NSString defaultCStringEncoding]]), false);
ExtAudioFileRef audiofile;
ExtAudioFileOpenURL(audioFileURL, &audiofile);
assert(audiofile);
OSStatus err;
AudioStreamBasicDescription fileFormat;
UInt32 size = sizeof(fileFormat);
err = ExtAudioFileGetProperty(audiofile, kExtAudioFileProperty_FileDataFormat, &size, &fileFormat);
AudioFileID aFile;
//size = sizeof(aFile);
PropertySize =sizeof(PacketsToRead);
err = ExtAudioFileGetProperty(audiofile, kExtAudioFileProperty_AudioFile, &PropertySize, &aFile);
AudioFileTypeID fileType;
PropertySize = sizeof(fileType);
err = AudioFileGetProperty(aFile, kAudioFilePropertyFileFormat, &PropertySize, &fileType);
AudioStreamBasicDescription clientFormat;
bzero(&clientFormat, sizeof(clientFormat));
clientFormat.mChannelsPerFrame = 2;
clientFormat.mBytesPerFrame = 4;
clientFormat.mBytesPerPacket = clientFormat.mBytesPerFrame;
clientFormat.mFramesPerPacket = 1;
clientFormat.mBitsPerChannel = 32;
clientFormat.mFormatID = kAudioFormatLinearPCM;
clientFormat.mSampleRate = 44100.00;
clientFormat.mFormatFlags =kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsNonInterleaved; //kLinearPCMFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved;
err = ExtAudioFileSetProperty(audiofile, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);
SInt64 numFrames = 0;
PropertySize = sizeof(numFrames);
err = ExtAudioFileGetProperty(audiofile, kExtAudioFileProperty_FileLengthFrames, &PropertySize, &numFrames);
NSNumber *pc = [NSNumber numberWithLongLong:numFrames];
[[packetCount objectAtIndex:i] replaceObjectAtIndex:j withObject:pc];
// create the buffers for reading in data
bufferList = malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer) * (clientFormat.mChannelsPerFrame - 1));
bufferList->mNumberBuffers = clientFormat.mChannelsPerFrame;
for (int ii=0; ii < bufferList->mNumberBuffers; ++ii) {
bufferList->mBuffers[ii].mDataByteSize = sizeof(float) * numFrames;
bufferList->mBuffers[ii].mNumberChannels = 2;
bufferList->mBuffers[ii].mData = malloc(bufferList->mBuffers[ii].mDataByteSize);
}
UInt32 rFrames = 0;
rFrames =(UInt32)numFrames;
err = ExtAudioFileRead(audiofile, &rFrames, bufferList);
[linearr addObject:[NSData dataWithBytes:bufferList->mBuffers[1].mData length:numFrames]];
err = ExtAudioFileDispose(audiofile);
}
[audioData addObject:linearr];
}
and that's how i play it:
UInt32 *buslist;
buslist=( UInt32*)[[[audioData objectAtIndex:0] objectAtIndex:4]bytes ];
in rendercallback func:
for (int i = 0 ; i < ioData->mNumberBuffers; i++){
UInt32 *au3=au->mBuffers[0].mData;
AudioBuffer buffer = ioData->mBuffers[i];
UInt32 *frameBuffer = buffer.mData;
for (int j = 0; j < inNumberFrames; j++)
{
frameBuffer[j] = buflist[counter];
if(counter>=529200)
counter=0;
else
counter++;
}}}
Now when i play the sound i get the first part played with double speed then the second part only distortion.
I was having the exact same problem I think;
The incoming sound was at a lower sample rate, so the array I allocated wasn't big enough for the higher sample rate of the auGraph, and was too fast and short.
make sure you allocate an array to read the file something like this:
sarray->frames = totalFramesInFile * graphSampleRate / fileAudioFormat.mSampleRate;

ExtAudioFileWrite puzzler (kExtAudioFileError_InvalidOperationOrder error)

The following code skips the first second of audio in a pcm caf file and removes the last 5 seconds, writing to a temp file (which will be 6 seconds shorter than the input). The loop, everytime, produces a kExtAudioFileError_InvalidOperationOrder on the ExtAudioFileWrite. What am I doing wrong?
NSString *destURLString = [self.track.location absoluteString];
destURLString = [destURLString substringToIndex:([destURLString length] - 4)]; //remove .caf
destURLString = [NSString stringWithFormat:#"%#TMP.caf",destURLString]; //add tmp.caf
NSURL *destinationURL = [NSURL URLWithString:destURLString];
ExtAudioFileRef inputFile = NULL;
ExtAudioFileRef outputFile = NULL;
AudioStreamBasicDescription destFormat;
destFormat.mFormatID = kAudioFormatLinearPCM;
destFormat.mFormatFlags = kAudioFormatFlagsCanonical;
destFormat.mSampleRate = 22000;
destFormat.mFormatFlags = 0;
destFormat.mBytesPerPacket = 2;
destFormat.mFramesPerPacket = 1;
destFormat.mBytesPerFrame = 2;
destFormat.mChannelsPerFrame = 1;
destFormat.mBitsPerChannel = 16;
destFormat.mReserved = 0;
ExtAudioFileCreateWithURL((CFURLRef)destinationURL, kAudioFileCAFType, &destFormat, NULL, kAudioFileFlags_EraseFile, &outputFile);
OSStatus fileStatus = ExtAudioFileOpenURL((CFURLRef)track.location, &inputFile);
//AudioFileID fileID;
//OSStatus fileStatus = AudioFileOpenURL((CFURLRef)track.location, kAudioFileReadPermission, 0, &fileID);
//ExtAudioFileWrapAudioFileID (fileID, true, &inputFile);
OSStatus fileStatus2 = ExtAudioFileOpenURL((CFURLRef)destinationURL, &outputFile);
//NSLog(#"open status: %i", fileStatus2);
//find out how many frames long this file is
SInt64 length = 0;
UInt32 dataSize2 = (UInt32)sizeof(length);
OSStatus propStatus2 = ExtAudioFileGetProperty(inputFile, kExtAudioFileProperty_FileLengthFrames, &dataSize2, &length);
AudioStreamBasicDescription clientFormat;
clientFormat.mFormatID = kAudioFormatLinearPCM;
clientFormat.mSampleRate = 22000;
clientFormat.mFormatFlags = kAudioFormatFlagsCanonical;
clientFormat.mBitsPerChannel = 16;
clientFormat.mChannelsPerFrame = 1;
clientFormat.mFramesPerPacket = 1;
clientFormat.mBytesPerPacket = 2;
clientFormat.mBytesPerFrame = 2;
destFormat.mReserved = 0;
UInt32 size = sizeof(clientFormat);
//set the intermediate format to canonical on the source file for conversion (?)
OSStatus setpropstatus = ExtAudioFileSetProperty(inputFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);
OSStatus setpropstatusout = ExtAudioFileSetProperty(outputFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);
//UInt32 size = sizeof(destFormat);
//OSStatus setpropstatus = ExtAudioFileSetProperty(inputFile, kAudioFilePropertyDataFormat, size, &destFormat);
//NSLog(#"set prop status in %i", setpropstatus);
//NSLog(#"set prop status out %i", setpropstatusout);
OSStatus seekStatus = ExtAudioFileSeek(inputFile, (SInt64)22000); // skip one second of audio
NSLog(#"seekstatus %i", seekStatus);
SInt64 newLength = length - (5*22000); //shorten by 5 seconds worth of frames
NSLog(#"length: %i frames", length);
UInt8 *buffer = malloc(65536); //64K
UInt32 totalFramecount = 0;
while(true) {
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0].mNumberChannels = 1;
bufferList.mBuffers[0].mData = buffer; //pointer to buffer of audio data
bufferList.mBuffers[0].mDataByteSize = 65536; //number of bytes in the buffer
UInt32 frameCount = 65536 / 2; //2 bytes per frame
// Read a chunk of input
OSStatus status = ExtAudioFileRead(inputFile, &frameCount, &bufferList);
totalFramecount += frameCount;
NSLog(#"read status %i", status);
//NSLog(#"loaded %f KB of data in %i frames", frameCount*2 / 1024.0, frameCount);
NSLog(#"loaded %i frames and stopping at %i", totalFramecount, newLength);
if (!frameCount || totalFramecount >= newLength) {
//termination condition
break;
}
OSStatus writeStatus = ExtAudioFileWrite(outputFile, frameCount, &bufferList);
NSLog(#"ws: %i", writeStatus);
}
free(buffer);
ExtAudioFileDispose(inputFile);
ExtAudioFileDispose(outputFile);
Turns out ExtAudioFileCreateWithURL returns a file already open, so the call to ExtAudioFileOpenURL was not needed, even though it returns successfully. I removed that and all works correctly.

How to Base64 encoding on the iPhone

How do I do Base64 encoding on the iPhone?
I have found a few examples that looked promising, but could never get any of them to work on the phone.
You can see an example here.
This is for iOS7+.
I copy the code here, just in case:
// Create NSData object
NSData *nsdata = [#"iOS Developer Tips encoded in Base64"
dataUsingEncoding:NSUTF8StringEncoding];
// Get NSString from NSData object in Base64
NSString *base64Encoded = [nsdata base64EncodedStringWithOptions:0];
// Print the Base64 encoded string
NSLog(#"Encoded: %#", base64Encoded);
// Let's go the other way...
// NSData from the Base64 encoded str
NSData *nsdataFromBase64String = [[NSData alloc]
initWithBase64EncodedString:base64Encoded options:0];
// Decoded NSString from the NSData
NSString *base64Decoded = [[NSString alloc]
initWithData:nsdataFromBase64String encoding:NSUTF8StringEncoding];
NSLog(#"Decoded: %#", base64Decoded);
Use this library to encode Base64.
It also supports ARC
I also had trouble finding working code for the iPhone that I could understand.
I finally wrote this.
-(NSString *)Base64Encode:(NSData *)data;
-(NSString *)Base64Encode:(NSData *)data{
//Point to start of the data and set buffer sizes
int inLength = [data length];
int outLength = ((((inLength * 4)/3)/4)*4) + (((inLength * 4)/3)%4 ? 4 : 0);
const char *inputBuffer = [data bytes];
char *outputBuffer = malloc(outLength+1);
outputBuffer[outLength] = 0;
//64 digit code
static char Encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//Start the count
int cycle = 0;
int inpos = 0;
int outpos = 0;
char temp;
//Pad the last to bytes, the outbuffer must always be a multiple of 4.
outputBuffer[outLength-1] = '=';
outputBuffer[outLength-2] = '=';
/* http://en.wikipedia.org/wiki/Base64
Text content M a n
ASCII 77 97 110
8 Bit pattern 01001101 01100001 01101110
6 Bit pattern 010011 010110 000101 101110
Index 19 22 5 46
Base64-encoded T W F u
*/
while (inpos < inLength){
switch (cycle) {
case 0:
outputBuffer[outpos++] = Encode[(inputBuffer[inpos] & 0xFC) >> 2];
cycle = 1;
break;
case 1:
temp = (inputBuffer[inpos++] & 0x03) << 4;
outputBuffer[outpos] = Encode[temp];
cycle = 2;
break;
case 2:
outputBuffer[outpos++] = Encode[temp|(inputBuffer[inpos]&0xF0) >> 4];
temp = (inputBuffer[inpos++] & 0x0F) << 2;
outputBuffer[outpos] = Encode[temp];
cycle = 3;
break;
case 3:
outputBuffer[outpos++] = Encode[temp|(inputBuffer[inpos]&0xC0) >> 6];
cycle = 4;
break;
case 4:
outputBuffer[outpos++] = Encode[inputBuffer[inpos++] & 0x3f];
cycle = 0;
break;
default:
cycle = 0;
break;
}
}
NSString *pictemp = [NSString stringWithUTF8String:outputBuffer];
free(outputBuffer);
return pictemp;
}
Download following two files from GitHub
Base64.h
Base64.m
Add these files to your project
Import header file in desired file
#import "Base64.h"
And use as to encode
NSString *plainText = #"Your String";
NSString *base64String = [plainText base64EncodedStringWithWrapWidth:0];
Also you can decode it as
NSString *plainText = [base64String base64DecodedString];
Try this out...this worked perfectly for me.create a category Base64.h and Base 64.m,Import to any class you want to use and call it using single line for base 64 encoding to happen.
//
// Base64.h
// CryptTest
// Created by SURAJ K THOMAS on 02/05/2013.
#import <Foundation/Foundation.h>
#interface Base64 : NSObject {
}
+ (void) initialize;
+ (NSString*) encode:(const uint8_t*) input length:(NSInteger) length;
+ (NSString*) encode:(NSData*) rawBytes;
+ (NSData*) decode:(const char*) string length:(NSInteger) inputLength;
+ (NSData*) decode:(NSString*) string;
#end
#import "Base64.h"
#implementation Base64
#define ArrayLength(x) (sizeof(x)/sizeof(*(x)))
static char encodingTable[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static char decodingTable[128];
+ (void) initialize {
if (self == [Base64 class]) {
memset(decodingTable, 0, ArrayLength(decodingTable));
for (NSInteger i = 0; i < ArrayLength(encodingTable); i++) {
decodingTable[encodingTable[i]] = i;
}
}
}
+ (NSString*) encode:(const uint8_t*) input length:(NSInteger) length {
NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
uint8_t* output = (uint8_t*)data.mutableBytes;
for (NSInteger i = 0; i < length; i += 3) {
NSInteger value = 0;
for (NSInteger j = i; j < (i + 3); j++) {
value <<= 8;
if (j < length) {
value |= (0xFF & input[j]);
}
}
NSInteger index = (i / 3) * 4;
output[index + 0] = encodingTable[(value >> 18) & 0x3F];
output[index + 1] = encodingTable[(value >> 12) & 0x3F];
output[index + 2] = (i + 1) < length ? encodingTable[(value >> 6) & 0x3F] : '=';
output[index + 3] = (i + 2) < length ? encodingTable[(value >> 0) & 0x3F] : '=';
}
return [[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding];
}
+ (NSString*) encode:(NSData*) rawBytes {
return [self encode:(const uint8_t*) rawBytes.bytes length:rawBytes.length];
}
+ (NSData*) decode:(const char*) string length:(NSInteger) inputLength {
if ((string == NULL) || (inputLength % 4 != 0)) {
return nil;
}
while (inputLength > 0 && string[inputLength - 1] == '=') {
inputLength--;
}
NSInteger outputLength = inputLength * 3 / 4;
NSMutableData* data = [NSMutableData dataWithLength:outputLength];
uint8_t* output = data.mutableBytes;
NSInteger inputPoint = 0;
NSInteger outputPoint = 0;
while (inputPoint < inputLength) {
char i0 = string[inputPoint++];
char i1 = string[inputPoint++];
char i2 = inputPoint < inputLength ? string[inputPoint++] : 'A'; /* 'A' will
decode to \0 */
char i3 = inputPoint < inputLength ? string[inputPoint++] : 'A';
output[outputPoint++] = (decodingTable[i0] << 2) | (decodingTable[i1] >> 4);
if (outputPoint < outputLength) {
output[outputPoint++] = ((decodingTable[i1] & 0xf) << 4) |
(decodingTable[i2] >> 2);
}
if (outputPoint < outputLength) {
output[outputPoint++] = ((decodingTable[i2] & 0x3) << 6) |
decodingTable[i3];
}
}
return data;
}
+ (NSData*) decode:(NSString*) string {
return [self decode:[string cStringUsingEncoding:NSASCIIStringEncoding]
length:string.length];
}
#end
now import the above category to any class and convert the string like below
NSString *authString = [[NSString stringWithFormat:#"OD0EK819OJFIFT6OJZZXT09Y1YUT1EJ2"]
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSData *inputData = [authString dataUsingEncoding:NSUTF8StringEncoding];
NSString *finalAuth =[Base64 encode:inputData];
NSLog(#"Encoded string =%#", finalAuth);
reference
NSString *plainString = #"foo";
Encoding
NSData *plainData = [plainString dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64String = [plainData base64EncodedStringWithOptions:0];
NSLog(#"%#", base64String); // Zm9v
Decoding
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
NSString *decodedString = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding];
NSLog(#"%#", decodedString); // foo
Seems as of iOS 7 you no longer need any libraries to encode in Base64. Following methods on NSData can be used to Base64 encode:
base64EncodedDataWithOptions:
– base64EncodedStringWithOptions:
I did my own implementation, where has been removed all checks inside the loop. So on big amount of data, it works faster. You can take it as a basis for own solution.
static char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ (NSString *)encodeString:(NSString *)data
{
const char *input = [data cStringUsingEncoding:NSUTF8StringEncoding];
unsigned long inputLength = [data length];
unsigned long modulo = inputLength % 3;
unsigned long outputLength = (inputLength / 3) * 4 + (modulo ? 4 : 0);
unsigned long j = 0;
// Do not forget about trailing zero
unsigned char *output = malloc(outputLength + 1);
output[outputLength] = 0;
// Here are no checks inside the loop, so it works much faster than other implementations
for (unsigned long i = 0; i < inputLength; i += 3) {
output[j++] = alphabet[ (input[i] & 0xFC) >> 2 ];
output[j++] = alphabet[ ((input[i] & 0x03) << 4) | ((input[i + 1] & 0xF0) >> 4) ];
output[j++] = alphabet[ ((input[i + 1] & 0x0F)) << 2 | ((input[i + 2] & 0xC0) >> 6) ];
output[j++] = alphabet[ (input[i + 2] & 0x3F) ];
}
// Padding in the end of encoded string directly depends of modulo
if (modulo > 0) {
output[outputLength - 1] = '=';
if (modulo == 1)
output[outputLength - 2] = '=';
}
NSString *s = [NSString stringWithUTF8String:(const char *)output];
free(output);
return s;
}
This answer is outdated, use https://stackoverflow.com/a/24468530/669586 since iOS 7.
A method in a NSData category
- (NSString*)encodeBase64 {
static char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
unsigned int length = self.length;
unsigned const char* rawData = self.bytes;
//empty data = empty output
if (length == 0) {
return #"";
}
unsigned int outputLength = (((length + 2) / 3) * 4);
//let's allocate buffer for the output
char* rawOutput = malloc(outputLength + 1);
//with each step we get 3 bytes from the input and write 4 bytes to the output
for (unsigned int i = 0, outputIndex = 0; i < length; i += 3, outputIndex += 4) {
BOOL triple = NO;
BOOL quad = NO;
//get 3 bytes (or only 1 or 2 when we have reached the end of input)
unsigned int value = rawData[i];
value <<= 8;
if (i + 1 < length) {
value |= rawData[i + 1];
triple = YES;
}
value <<= 8;
if (i + 2 < length) {
value |= rawData[i + 2];
quad = YES;
}
//3 * 8 bits written as 4 * 6 bits (indexing the 64 chars of the alphabet)
//write = if end of input reached
rawOutput[outputIndex + 3] = (quad) ? alphabet[value & 0x3F] : '=';
value >>= 6;
rawOutput[outputIndex + 2] = (triple) ? alphabet[value & 0x3F] : '=';
value >>= 6;
rawOutput[outputIndex + 1] = alphabet[value & 0x3F];
value >>= 6;
rawOutput[outputIndex] = alphabet[value & 0x3F];
}
rawOutput[outputLength] = 0;
NSString* output = [NSString stringWithCString:rawOutput encoding:NSASCIIStringEncoding];
free(rawOutput);
return output;
}