Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/io/sink_apple_audio.c

    rcf387e3 r99365e9  
    3232#include <AudioToolbox/AudioToolbox.h>
    3333
    34 #define FLOAT_TO_SHORT(x) (short)(x * 32768)
    35 
    36 extern int createAubioBufferList(AudioBufferList *bufferList, int channels, int segmentSize);
     34extern int createAudioBufferList(AudioBufferList *bufferList, int channels, int segmentSize);
    3735extern void freeAudioBufferList(AudioBufferList *bufferList);
    3836extern CFURLRef createURLFromPath(const char * path);
     
    4038
    4139uint_t aubio_sink_apple_audio_open(aubio_sink_apple_audio_t *s);
     40
     41uint_t aubio_str_extension_matches(const char_t *ext,
     42    const char_t *pattern);
     43const char_t *aubio_str_get_extension(const char_t *filename);
    4244
    4345#define MAX_SIZE 4096 // the maximum number of frames that can be written at a time
     
    5557  ExtAudioFileRef audioFile;
    5658  bool async;
     59  AudioFileTypeID fileType;
    5760};
    5861
     
    7376  s->channels = 0;
    7477
     78  aubio_sink_apple_audio_preset_format(s, aubio_str_get_extension(uri));
     79
    7580  // zero samplerate given. do not open yet
    7681  if ((sint_t)samplerate == 0) {
    7782    return s;
    7883  }
     84
    7985  // invalid samplerate given, abort
    8086  if (aubio_io_validate_samplerate("sink_apple_audio", s->path, samplerate)) {
     
    122128}
    123129
     130uint_t aubio_sink_apple_audio_preset_format(aubio_sink_apple_audio_t *s,
     131    const char_t *fmt)
     132{
     133  if (aubio_str_extension_matches(fmt, "wav")) {
     134    s->fileType = kAudioFileWAVEType;
     135  } else if (aubio_str_extension_matches(fmt, "m4a")
     136      || aubio_str_extension_matches(fmt, "mp4") ) {
     137    // use alac for "mp4" and "m4a"
     138    s->fileType = kAudioFileM4AType;
     139  } else if (aubio_str_extension_matches(fmt, "aac") ) {
     140    // only use lossy codec for "aac"
     141    s->fileType = kAudioFileMPEG4Type;
     142  } else if (aubio_str_extension_matches(fmt, "aiff") ) {
     143    // only use lossy codec for "aac"
     144    s->fileType = kAudioFileAIFFType;
     145  } else {
     146    AUBIO_WRN("sink_apple_audio: could not guess format for %s,"
     147       " using default (wav)\n", s->path);
     148    s->fileType = kAudioFileWAVEType;
     149    return AUBIO_FAIL;
     150  }
     151  return AUBIO_OK;
     152}
     153
     154static void aubio_sink_apple_audio_set_client_format(aubio_sink_apple_audio_t* s,
     155    AudioStreamBasicDescription *clientFormat)
     156{
     157  memset(clientFormat, 0, sizeof(AudioStreamBasicDescription));
     158  // always set samplerate and channels first
     159  clientFormat->mSampleRate       = (Float64)(s->samplerate);
     160  clientFormat->mChannelsPerFrame = s->channels;
     161
     162  switch (s->fileType) {
     163    case kAudioFileM4AType:
     164      clientFormat->mFormatID         = kAudioFormatAppleLossless;
     165      break;
     166    case kAudioFileMPEG4Type:
     167      clientFormat->mFormatID         = kAudioFormatMPEG4AAC;
     168      clientFormat->mFormatFlags      = kMPEG4Object_AAC_Main;
     169      clientFormat->mFormatFlags     |= kAppleLosslessFormatFlag_16BitSourceData;
     170      clientFormat->mFramesPerPacket  = 1024;
     171      break;
     172    case kAudioFileWAVEType:
     173      clientFormat->mFormatID         = kAudioFormatLinearPCM;
     174      clientFormat->mFormatFlags      = kAudioFormatFlagIsSignedInteger;
     175      clientFormat->mFormatFlags     |= kAudioFormatFlagIsPacked;
     176      clientFormat->mBitsPerChannel   = sizeof(short) * 8;
     177      clientFormat->mFramesPerPacket  = 1;
     178      clientFormat->mBytesPerFrame    = clientFormat->mBitsPerChannel * clientFormat->mChannelsPerFrame / 8;
     179      clientFormat->mBytesPerPacket   = clientFormat->mFramesPerPacket * clientFormat->mBytesPerFrame;
     180      break;
     181    case kAudioFileAIFFType:
     182      clientFormat->mFormatID         = kAudioFormatLinearPCM;
     183      clientFormat->mFormatFlags      = kAudioFormatFlagIsSignedInteger;
     184      clientFormat->mFormatFlags     |= kAudioFormatFlagIsPacked;
     185      clientFormat->mFormatFlags     |= kAudioFormatFlagIsBigEndian;
     186      clientFormat->mBitsPerChannel   = sizeof(short) * 8;
     187      clientFormat->mFramesPerPacket  = 1;
     188      clientFormat->mBytesPerFrame    = clientFormat->mBitsPerChannel * clientFormat->mChannelsPerFrame / 8;
     189      clientFormat->mBytesPerPacket   = clientFormat->mFramesPerPacket * clientFormat->mBytesPerFrame;
     190      break;
     191    default:
     192      break;
     193  }
     194}
     195
    124196uint_t aubio_sink_apple_audio_get_samplerate(const aubio_sink_apple_audio_t *s)
    125197{
     
    136208  if (s->samplerate == 0 || s->channels == 0) return AUBIO_FAIL;
    137209
    138   AudioStreamBasicDescription clientFormat;
    139   memset(&clientFormat, 0, sizeof(AudioStreamBasicDescription));
    140   clientFormat.mFormatID         = kAudioFormatLinearPCM;
    141   clientFormat.mSampleRate       = (Float64)(s->samplerate);
    142   clientFormat.mFormatFlags      = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    143   clientFormat.mChannelsPerFrame = s->channels;
    144   clientFormat.mBitsPerChannel   = sizeof(short) * 8;
    145   clientFormat.mFramesPerPacket  = 1;
    146   clientFormat.mBytesPerFrame    = clientFormat.mBitsPerChannel * clientFormat.mChannelsPerFrame / 8;
    147   clientFormat.mBytesPerPacket   = clientFormat.mFramesPerPacket * clientFormat.mBytesPerFrame;
    148   clientFormat.mReserved         = 0;
    149 
    150   AudioFileTypeID fileType = kAudioFileWAVEType;
    151210  CFURLRef fileURL = createURLFromPath(s->path);
    152211  bool overwrite = true;
     212
     213  // set the in-memory format
     214  AudioStreamBasicDescription inputFormat;
     215  memset(&inputFormat, 0, sizeof(AudioStreamBasicDescription));
     216  inputFormat.mFormatID         = kAudioFormatLinearPCM;
     217  inputFormat.mSampleRate       = (Float64)(s->samplerate);
     218  inputFormat.mFormatFlags      = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
     219  inputFormat.mChannelsPerFrame = s->channels;
     220  inputFormat.mBitsPerChannel   = sizeof(smpl_t) * 8;
     221  inputFormat.mFramesPerPacket  = 1;
     222  inputFormat.mBytesPerFrame    = inputFormat.mBitsPerChannel * inputFormat.mChannelsPerFrame / 8;
     223  inputFormat.mBytesPerPacket   = inputFormat.mFramesPerPacket * inputFormat.mBytesPerFrame;
     224
     225  // get the in-file format
     226  AudioStreamBasicDescription clientFormat;
     227  aubio_sink_apple_audio_set_client_format(s, &clientFormat);
     228
    153229  OSStatus err = noErr;
    154   err = ExtAudioFileCreateWithURL(fileURL, fileType, &clientFormat, NULL,
     230  err = ExtAudioFileCreateWithURL(fileURL, s->fileType, &clientFormat, NULL,
    155231     overwrite ? kAudioFileFlags_EraseFile : 0, &s->audioFile);
    156232  CFRelease(fileURL);
     
    162238    goto beach;
    163239  }
    164   if (createAubioBufferList(&s->bufferList, s->channels, s->max_frames * s->channels)) {
     240
     241#if defined(kAppleSoftwareAudioCodecManufacturer)
     242  // on iOS, set software based encoding before setting clientDataFormat
     243  UInt32 codecManf = kAppleSoftwareAudioCodecManufacturer;
     244  err = ExtAudioFileSetProperty(s->audioFile,
     245      kExtAudioFileProperty_CodecManufacturer,
     246      sizeof(UInt32), &codecManf);
     247  if (err) {
     248    char_t errorstr[20];
     249    AUBIO_ERR("sink_apple_audio: error when trying to set sofware codec on %s "
     250        "(%s)\n", s->path, getPrintableOSStatusError(errorstr, err));
     251    goto beach;
     252  }
     253#endif
     254
     255  err = ExtAudioFileSetProperty(s->audioFile,
     256      kExtAudioFileProperty_ClientDataFormat,
     257      sizeof(AudioStreamBasicDescription), &inputFormat);
     258  if (err) {
     259    char_t errorstr[20];
     260    AUBIO_ERR("sink_apple_audio: error when trying to set output format on %s "
     261        "(%s)\n", s->path, getPrintableOSStatusError(errorstr, err));
     262    goto beach;
     263  }
     264
     265  if (createAudioBufferList(&s->bufferList, s->channels, s->max_frames * s->channels)) {
    165266    AUBIO_ERR("sink_apple_audio: error when creating buffer list for %s, "
    166267        "out of memory? \n", s->path);
     
    175276void aubio_sink_apple_audio_do(aubio_sink_apple_audio_t * s, fvec_t * write_data, uint_t write) {
    176277  UInt32 c, v;
    177   short *data = (short*)s->bufferList.mBuffers[0].mData;
     278  smpl_t *data = (smpl_t*)s->bufferList.mBuffers[0].mData;
    178279  uint_t length = aubio_sink_validate_input_length("sink_apple_audio", s->path,
    179280      s->max_frames, write_data->length, write);
     
    181282  for (c = 0; c < s->channels; c++) {
    182283    for (v = 0; v < length; v++) {
    183       data[v * s->channels + c] = FLOAT_TO_SHORT(write_data->data[v]);
     284      data[v * s->channels + c] = write_data->data[v];
    184285    }
    185286  }
     
    190291void aubio_sink_apple_audio_do_multi(aubio_sink_apple_audio_t * s, fmat_t * write_data, uint_t write) {
    191292  UInt32 c, v;
    192   short *data = (short*)s->bufferList.mBuffers[0].mData;
     293  smpl_t *data = (smpl_t*)s->bufferList.mBuffers[0].mData;
    193294  uint_t channels = aubio_sink_validate_input_channels("sink_apple_audio",
    194295      s->path, s->channels, write_data->height);
     
    198299  for (c = 0; c < channels; c++) {
    199300    for (v = 0; v < length; v++) {
    200       data[v * s->channels + c] = FLOAT_TO_SHORT(write_data->data[c][v]);
     301      data[v * s->channels + c] = write_data->data[c][v];
    201302    }
    202303  }
     
    207308void aubio_sink_apple_audio_write(aubio_sink_apple_audio_t *s, uint_t write) {
    208309  OSStatus err = noErr;
     310  // set mDataByteSize to match the number of frames to be written
     311  // see https://www.mail-archive.com/coreaudio-api@lists.apple.com/msg01109.html
     312  s->bufferList.mBuffers[0].mDataByteSize = write * s->channels
     313    * sizeof(smpl_t);
    209314  if (s->async) {
    210315    err = ExtAudioFileWriteAsync(s->audioFile, write, &s->bufferList);
Note: See TracChangeset for help on using the changeset viewer.