2012-03-04 6 views
4

Могу ли я немного помочь с этим?Формат AudioTimeStamp + 'MusicDeviceMIDIEvent'

В тестовом проекте у меня есть AUSampler -> MixerUnit -> ioUnit и настроен на обратный вызов рендеринга. Все работает. Я использую метод MusicDeviceMIDIEvent, как определено в MusicDevice.h, чтобы сыграть midi noteOn & noteOff. Таким образом, в приведенном ниже коде для взлома, noteOn происходит в течение 0,5 сек. каждые 2 секунды.

MusicDeviceMIDIEvent (см. Ниже) принимает параметр: inOffsetSampleFrame, чтобы запланировать событие в будущем. То, что я хотел бы сделать, это сыграть noteOn и запланировать noteOff в одно и то же время (без проверки времени взлома, которое я делаю ниже). Я просто не понимаю, каково должно быть значение inOffsetSampleFrame (например: для воспроизведения .5 секунд или .2 секунды примечания (другими словами, я не понимаю основы синхронизации звука ...).

Так что, если кто-то может ходить мне через арифметику, чтобы получить правильные значения из входящего AudioTimeStamp, что было бы здорово Кроме того, возможно, поправьте меня/уточнить любой из них:

  1. AudioTimeStamp->mSampleTime - sampleTime это время из текущий образец «срез»? Это в миллисекундах?

  2. AudioTimeStamp->mHostTime -? host - компьютер, на котором запущено приложение, и это время (в миллисекундах?) с момента запуска компьютера? Это номер HUGE. Разве это не опрокидывается, а затем вызывает проблемы?

  3. inNumberFrames - похоже, что это 512 на iOS5 (устанавливается через kAudioUnitProperty_MaximumFramesPerSlice). Итак, образец сделан до 512 кадров?

  4. Я видел много наставлений, чтобы не перегружать оказывать CallBack функции - в частности, чтобы избежать Объективных вызовов C - Я понимаю причину, но как одно того сообщения пользовательского интерфейса или делать другие обработки?

Думаю, все. Спасибо, что согласились со мной!

inOffsetSampleFrame При планировании на MIDI события с визуализацией нити Аудиоблока, затем вы можете поставить образец смещения, что аудио устройство может применяться при применении этого события в его следующем аудиоблоке визуализации. Это позволяет вам запланировать выборку, время, когда применяется команда MIDI, и особенно важно при запуске новых заметок, особенно .Если вы не планировать в потоке визуализации Аудиоблок, в , то вы должны установить это значение в 0

// MusicDeviceMIDIEvent функция четкости:

extern OSStatus 
MusicDeviceMIDIEvent( MusicDeviceComponent inUnit, 
        UInt32     inStatus, 
        UInt32     inData1, 
        UInt32     inData2, 
        UInt32     inOffsetSampleFrame) 

// мой обратный вызов

OSStatus MyCallback( void *       inRefCon, 
       AudioUnitRenderActionFlags * ioActionFlags, 
       const AudioTimeStamp *   inTimeStamp, 
       UInt32       inBusNumber, 
       UInt32       inNumberFrames, 
       AudioBufferList *    ioData) 
{ 

Float64 sampleTime = inTimeStamp->mSampleTime; 
UInt64 hostTime = inTimeStamp->mHostTime; 

[(__bridge Audio*)inRefCon audioEvent:sampleTime andHostTime:hostTime]; 

return 1; 
} 

// Метод OBJ-C

- (void)audioEvent:(Float64) sampleTime andHostTime:(UInt64)hostTime 
{ 
OSStatus result = noErr; 

Float64 nowTime = (sampleTime/self.graphSampleRate); // sample rate: 44100.0 
if (nowTime - lastTime > 2) { 

    UInt32 noteCommand = kMIDIMessage_NoteOn << 4 | 0; 
    result = MusicDeviceMIDIEvent (mySynthUnit, noteCommand, 60, 120, 0); 
    lastTime = sampleTime/self.graphSampleRate; 
} 

if (nowTime - lastTime > .5) { 
    UInt32 noteCommand = kMIDIMessage_NoteOff << 4 | 0; 
    result = MusicDeviceMIDIEvent (mySynthUnit, noteCommand, 60, 0, 0); 
} 
} 
+0

'mSampleTime' - количество выборок (отсчетных кадров), как вы уже выяснили (можете видеть это в вашем коде :)). В каждом вызове рендеринга это число увеличивается на размер буфера, например. 512. Этот размер буфера установлен в 'AVAudioSession' с помощью' preferredIOBufferDuration'. – bio

+0

Для связи с пользовательским интерфейсом вы можете использовать блокировки или счетчики без блокировки. Без блокировки очередь - это просто буфер, который вы заполняете из потока реального времени, и после его заполнения вы атомизировали смещение записи (например, 'OSAtomicCompareAndSwap32Barrier'). Не забудьте использовать барьер здесь. В потоке пользовательского интерфейса вы устанавливаете таймер, скажем, 10 раз в секунду (все, что достаточно). Таймер proc проверяет смещение чтения очереди, которое является другой переменной, чем смещение записи. Если смещение записи продвинулось (т. Е. Не равно смещению чтения), вы читаете новые байты и обрабатываете их до тех пор, пока все новые байты не будут обработаны. – bio

ответ

2

Ответ здесь заключается в том, что я неправильно понял цель inOffsetSampleFrame, несмотря на то, что он был точно назван. Я думал, что смогу использовать его для планирования события noteOff в какое-то время в будущем, поэтому мне не нужно было управлять noteOffs, но область действия этого объекта просто находится в пределах текущего кадра выборки. Ну что ж.

+0

Вы придумали лучший способ сыграть заметку в течение продолжительности? – drewish

+1

В итоге я использовал API MusicPlayer и добавляю заметки к трекам на лету. Работает хорошо. Я также попытался * выполнитьSelector: withObject: afterDelay *. Это тоже работает. –

+0

Конечно, методы 'performSelector' так или иначе неточны, потому что время зависит от других задач, происходящих в GCD. Для точного момента выборки вы можете отслеживать все заметки и снимать заметки, когда находитесь в вызове рендеринга, который имеет временное окно, соответствующее их отметкам времени. Использование 'MusicPlayer' (или' AVAudioSequencer') также должно дать вам примерный точный выбор времени. – bio