Цель C Создание (пустого) файла WAV

Я пытаюсь сохранить некоторые аудиоданные в файл WAV — у меня есть аудиоданные, которые я обычно использую в RemoteIO, но сейчас я пытаюсь реализовать функцию для сохранения данных. Я знаю, что аудиоданные действительны, так что это не проблема — если я могу просто получить пустой WAV-файл правильной длины, я смогу заполнить его данными позже.

Прямо сейчас код создает файл, и он выглядит нужной длины в байтах, но, по-видимому, он неправильно отформатирован, потому что OSX, QuickTime, iTunes и т. д. не могут его распознать (они видят файл, не могут определить длина или воспроизвести)

    NSURL * tvarFilename = [savePanel URL];
    NSLog(@"doSaveAs filename = %@",tvarFilename);        

    //try to create an audio file there

    AudioFileID mRecordFile;

    AudioStreamBasicDescription audioFormat;
    audioFormat.mSampleRate         = 44100.00;
    audioFormat.mFormatID           = kAudioFormatLinearPCM;
    audioFormat.mFormatFlags        = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    audioFormat.mFramesPerPacket    = 1;
    audioFormat.mChannelsPerFrame   = 2;
    audioFormat.mBitsPerChannel     = 16;
    audioFormat.mBytesPerPacket     = 4;
    audioFormat.mBytesPerFrame      = 4;



    OSStatus status = AudioFileCreateWithURL((CFURLRef)tvarFilename, kAudioFileWAVEType, &audioFormat, kAudioFileFlags_EraseFile, &mRecordFile);

    int beatsToRecord = 4; //temporary

    int bpm = 120;

    double intervalInSamples = (double) 60 / bpm;
    intervalInSamples *= (double)44100;

    int inNumberFrames = (intervalInSamples * beatsToRecord);

    UInt32 frameBuffer[inNumberFrames];

    int sampleTime = 0;

    UInt32 thisSubBuffer[inNumberFrames];

    for (int i = 0; i < inNumberFrames; i++) { frameBuffer[i] = 0; }

    UInt32 bytesToWrite = inNumberFrames * sizeof(UInt32);
    status = AudioFileWriteBytes(mRecordFile, false, 0, &bytesToWrite, &frameBuffer);

person jnpdx    schedule 05.05.2012    source источник
comment
Вы должны проверить свои файлы с помощью шестнадцатеричного редактора и посмотреть, чем они отличаются от стандартных.   -  person Hot Licks    schedule 06.05.2012
comment
Да, думаю, это мой следующий шаг. Погружение в это сейчас   -  person jnpdx    schedule 07.05.2012
comment
Хорошо, похоже, проблема в заголовке — остальные данные записываются правильно. Итак, другими словами, вызов AudioFileCreateWithURL() является причиной основной проблемы, похоже   -  person jnpdx    schedule 07.05.2012
comment
AudioStreamBasicDescription аудиоформат; audioFormat.mSampleRate = 16000,00; audioFormat.mFormatID = kAudioFormatLinearPCM; audioFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; audioFormat.mFramesPerPacket = 1; audioFormat.mChannelsPerFrame = 1; audioFormat.mBitsPerChannel = 16; audioFormat.mBytesPerPacket = 2; audioFormat.mBytesPerFrame = 2; Это прекрасно работает.   -  person João Nunes    schedule 21.01.2015


Ответы (2)


Файл WAV действительно прост: он состоит только из раздела заголовка (обычно длиной 44 байта), а затем необработанных данных PCM. Я написал библиотеку, которая должна записывать WAV-файлы, и я уверен, что вы поймете, как я это делаю. Для уточнения:

/**
 * CD quality: 44100 Hz sample rate, 16 bit per sample, 2 channels (stereo):
**/
struct sprec_wav_header *hdr = sprec_wav_header_from_params(44100, 16, 2);
int filesize = (obtain the filesize somehow here);
/**
 * -8 bytes for the first part of the header, see the WAV specification
**/
hdr->filesize = filesize - 8;
int filedesc = open("/tmp/dummy.wav", O_WRONLY | O_CREAT, 0644);
if (sprec_wav_header_write(filedesc, hdr))
{
    printf("Error writing WAV header!\n");
}
close(filedesc);
free(hdr);

И библиотека, которую я написал: https://github.com/H2CO3/libsprec/

Надеюсь это поможет.

person Community    schedule 05.05.2012
comment
Спасибо. Я начал сканировать ваш код. Однако должен быть способ сделать это с помощью AudioFileWriteBytes, верно? Разве не для этого существует эта функция? - person jnpdx; 05.05.2012
comment
API AudioFile действительно полезен только тогда, когда вы хотите серьезно интегрировать его с AudioUnits, AudioQueues и т. д., поэтому API CoreAudio более низкого уровня. Если вы просто хотите создать пустой WAV-файл, это того не стоит. Как вы сами сказали, вы сможете заполнить его позже, используя метод, который вам больше подходит. - person ; 06.05.2012
comment
Что значит не стоит? Похоже, если я устраню какой-либо параметр, который у меня неправильный, у меня будет 1-строчная функция для записи звука, тогда как в вашем коде вы используете многие. Я готов написать дополнительный код, но это оставляет больше строк для отладки позже и т. д. Похоже, что почти противоположное было бы правдой. - person jnpdx; 07.05.2012

Проблема в коде в начальном вопросе заключалась в отсутствующей строке AudioFileClose(mRecordFile); в самом конце.

Для тех, кто все еще ищет рабочий образец без использования сторонних библиотек, вот немного измененный фрагмент кода:

- (void)createSilentWAVFileAtURL:(NSURL *)fileURL {
    AudioFileID mRecordFile;

    AudioStreamBasicDescription audioFormat;
    audioFormat.mSampleRate         = 44100.00;
    audioFormat.mFormatID           = kAudioFormatLinearPCM;
    audioFormat.mFormatFlags        = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    audioFormat.mFramesPerPacket    = 1;
    audioFormat.mChannelsPerFrame   = 2;
    audioFormat.mBitsPerChannel     = 16;
    audioFormat.mBytesPerPacket     = 4;
    audioFormat.mBytesPerFrame      = 4;

    OSStatus status = AudioFileCreateWithURL((__bridge CFURLRef)fileURL, kAudioFileWAVEType, &audioFormat, kAudioFileFlags_EraseFile, &mRecordFile);

    double intervalInSamples = 0.5;
    intervalInSamples *= audioFormat.mSampleRate * audioFormat.mChannelsPerFrame;

    int beatsToRecord = 4; //seconds of silence
    int inNumberFrames = (intervalInSamples * beatsToRecord);

    UInt32 frameBuffer[inNumberFrames];

    for (int i = 0; i < inNumberFrames; i++) { frameBuffer[i] = 0; }

    UInt32 bytesToWrite = inNumberFrames * sizeof(uint32_t);
    status = AudioFileWriteBytes(mRecordFile, false, 0, &bytesToWrite, &frameBuffer);
    status = AudioFileClose(mRecordFile);

    NSAssert(status == noErr, @"");
}

P.S.: Чтобы уменьшить окончательный размер файла, уменьшите mChannelsPerFrame с 2 до 1 и mSampleRate (например, до 11000)

person Kyrylo    schedule 16.05.2016