I have some raw sound data that I want to make into an AIFF file format. I know the specifics of the audio data. I tried creating a wave from the audio, but that didn't work. OS X does have a function to create the header, but it directly addresses a file and I might not want to do that (that and the function, SetupAIFFHeader is deprecated and unavailable in 64-bit code).

有帮助吗?

解决方案

Apple's Core Audio API will create and write data to an AIFF file, and other formats. It works pretty well, but in my opinion the API is difficult to use. I'll paste some example code below, but you'd probably want to change it. AudioFileWriteBytes can write more than 2 bytes at a time. There is another wrapper API in AudioToolbox/ExtendedAudioFile.h which will let you write a format like 32 bit floats, and have it translated to an underlying format, be it AIFF/PCM or a compressed format.

double sampleRate = 44100;
double duration = ...;
long nSamples = (long)(sampleRate * duration);

// Format struct for 1 channel, 16 bit PCM audio
AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mSampleRate = sampleRate;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFormatFlags = kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger;
asbd.mBitsPerChannel = 16;
asbd.mChannelsPerFrame = 1;
asbd.mFramesPerPacket = 1;
asbd.mBytesPerFrame = 2;
asbd.mBytesPerPacket = 2;

CFURLRef url = makeUrl("hello.aiff");

AudioFileID audioFile;
OSStatus res;
res = AudioFileCreateWithURL(url, kAudioFileAIFFType, &asbd, 
                             kAudioFileFlags_EraseFile, &audioFile);
checkError(res);

UInt32 numBytes = 2;
for (int i=0; i<nSamples; i++) {
    SInt16 sample = ... // something between SHRT_MIN and SHRT_MAX;
    sample = OSSwapHostToBigInt16(sample);       
    res = AudioFileWriteBytes(audioFile, false, i*2, &numBytes, &sample);
    checkError(res);
}

res = AudioFileClose(audioFile);
checkError(res);

checkError is asserting that res == noErr. makeUrl looks like:

CFURLRef makeUrl(const char *cstr) {
    CFStringRef path = CFStringCreateWithCString(0, cstr, kCFStringEncodingUTF8);
    CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path, 0, false);
    CFRelease(path);
    return url;
}

其他提示

As much as I hate wheel-reinvention, I suspect your best bet might be to roll your own AIFF save routines.

AIFF is an extension of the old Electronic Arts EA-IFF format which was used on the Amiga; it's a series of 4-byte identifiers (similar to FOURCCs), block lengths and data payloads. The Wikipedia article is quite informative and provides links to other sites which contain detailed information about the format.

http://en.wikipedia.org/wiki/Audio_Interchange_File_Format

I was able to write a proper AIFF file. The last bit that was getting me was I was using a sizeof() for a structure's size, where the size omits the first eight bytes. I did use Apple's deprecated AIFF.h header to get the structures, and it seems that neither QuickTime X nor 7 reads the metadata I set in it.

You can see my work at PlayerPRO's PlayerPRO 6 branch. It's in a file called PPApp_AppDelegate.m in the function -createAIFFDataFromSettings:data:

Here is some C code that will create an AIFF file using the Apple CoreAudio and AudioToolbox frameworks for macOS.

#include <string.h>
#include <math.h>
#include "CoreAudio/CoreAudio.h"
#include "CoreAudio/CoreAudioTypes.h"
#include "AudioToolbox/AudioToolbox.h"
#include "AudioToolbox/AudioFile.h"

CFURLRef MakeUrl(const char *cstr);
void CheckError(OSStatus res);

AudioStreamBasicDescription asbd;

AudioFileID audioFile;
OSStatus res;

void CheckError(OSStatus result) {
  if (result == noErr) return;
  switch(result) {
    case kAudioFileUnspecifiedError:
      printf("kAudioFileUnspecifiedError");
      break;
    case kAudioFileUnsupportedFileTypeError:
      printf("kAudioFileUnsupportedFileTypeError");
      break;
    case kAudioFileUnsupportedDataFormatError:
      printf("kAudioFileUnsupportedDataFormatError");
      break;
    case kAudioFileUnsupportedPropertyError:
      printf("kAudioFileUnsupportedPropertyError");
      break;
    case kAudioFileBadPropertySizeError:
      printf("kAudioFileBadPropertySizeError");
      break;
    case kAudioFilePermissionsError:
      printf("kAudioFilePermissionsError");
      break;
    case kAudioFileNotOptimizedError:
      printf("kAudioFileNotOptimizedError");
      break;
    case kAudioFileInvalidChunkError:
      printf("kAudioFileInvalidChunkError");
      break;
    case kAudioFileDoesNotAllow64BitDataSizeError:
      printf("kAudioFileDoesNotAllow64BitDataSizeError");
      break;
    case kAudioFileInvalidPacketOffsetError:
      printf("kAudioFileInvalidPacketOffsetError");
      break;
    case kAudioFileInvalidFileError:
      printf("kAudioFileInvalidFileError");
      break;
    case kAudioFileOperationNotSupportedError:
      printf("kAudioFileOperationNotSupportedError");
      break;
    case kAudioFileNotOpenError:
      printf("kAudioFileNotOpenError");
      break;
    case kAudioFileEndOfFileError:
      printf("kAudioFileEndOfFileError");
      break;
    case kAudioFilePositionError:
      printf("kAudioFilePositionError");
      break;
    case kAudioFileFileNotFoundError:
      printf("kAudioFileFileNotFoundError");
      break;
    default:
      printf("unknown error");
      break;
  }
  exit(result);
}

CFURLRef MakeUrl(const char *cstr) {
  CFStringRef path = CFStringCreateWithCString(0, cstr, kCFStringEncodingUTF8);
  CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path, 0, false);
  CFRelease(path);
  return url;
}

int main() {
  double sampleRate = 44100.0;
  double duration = 10.0;
  long nSamples = (long)(sampleRate * duration);

  memset(&asbd, 0, sizeof(asbd));
  // Format struct for 1 channel, 16 bit PCM audio
  asbd.mSampleRate = sampleRate;
  asbd.mFormatID = kAudioFormatLinearPCM;
  asbd.mFormatFlags = kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger;
  asbd.mBitsPerChannel = 16;
  asbd.mChannelsPerFrame = 1;
  asbd.mFramesPerPacket = 1;
  asbd.mBytesPerFrame = 2;
  asbd.mBytesPerPacket = 2;

  CFURLRef url = MakeUrl("sinpos.aiff");
  res = AudioFileCreateWithURL(url, kAudioFileAIFFType, &asbd,
      kAudioFileFlags_EraseFile, &audioFile);

  CheckError(res);

  UInt32 numBytes = 2;
  int freq = 44; // 100 for approx 440Hz, 2940 for 15Hz, 44 for 1000Hz
  for (int i=0; i<nSamples; i++) {
    int x = (i % freq);
    double angle = 2.0*3.1459*x/freq;
    double s = 1.0*32767*sin(angle);
    SInt16 sample = (SInt16) s;
    sample = OSSwapHostToBigInt16(sample);
    res = AudioFileWriteBytes(audioFile, false, i*2, &numBytes, &sample);
    CheckError(res);
  }

  res = AudioFileClose(audioFile);
  CheckError(res);

  exit(0);
}

The Makefile is as follows:

aiffcreate: aiffcreate.c
    gcc -o $@ $< -framework AudioToolbox -framework CoreFoundation -framework CoreAudio -lm

clean:
    rm *.aiff aiffcreate || true

This can be run by simply issuing a ./aiffcreate command on the command line and a file will be created named sinpos.aiff which is a pure 1000Hz tone lasting 10 seconds.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top