Могу ли я немного помочь с этим?Формат AudioTimeStamp + 'MusicDeviceMIDIEvent'
В тестовом проекте у меня есть AUSampler -> MixerUnit -> ioUnit
и настроен на обратный вызов рендеринга. Все работает. Я использую метод MusicDeviceMIDIEvent
, как определено в MusicDevice.h
, чтобы сыграть midi noteOn & noteOff. Таким образом, в приведенном ниже коде для взлома, noteOn происходит в течение 0,5 сек. каждые 2 секунды.
MusicDeviceMIDIEvent
(см. Ниже) принимает параметр: inOffsetSampleFrame
, чтобы запланировать событие в будущем. То, что я хотел бы сделать, это сыграть noteOn и запланировать noteOff в одно и то же время (без проверки времени взлома, которое я делаю ниже). Я просто не понимаю, каково должно быть значение inOffsetSampleFrame
(например: для воспроизведения .5 секунд или .2 секунды примечания (другими словами, я не понимаю основы синхронизации звука ...).
Так что, если кто-то может ходить мне через арифметику, чтобы получить правильные значения из входящего AudioTimeStamp
, что было бы здорово Кроме того, возможно, поправьте меня/уточнить любой из них:
AudioTimeStamp->mSampleTime
- sampleTime это время из текущий образец «срез»? Это в миллисекундах?AudioTimeStamp->mHostTime
-? host - компьютер, на котором запущено приложение, и это время (в миллисекундах?) с момента запуска компьютера? Это номер HUGE. Разве это не опрокидывается, а затем вызывает проблемы?inNumberFrames
- похоже, что это 512 на iOS5 (устанавливается черезkAudioUnitProperty_MaximumFramesPerSlice
). Итак, образец сделан до 512 кадров?Я видел много наставлений, чтобы не перегружать оказывать 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);
}
}
'mSampleTime' - количество выборок (отсчетных кадров), как вы уже выяснили (можете видеть это в вашем коде :)). В каждом вызове рендеринга это число увеличивается на размер буфера, например. 512. Этот размер буфера установлен в 'AVAudioSession' с помощью' preferredIOBufferDuration'. – bio
Для связи с пользовательским интерфейсом вы можете использовать блокировки или счетчики без блокировки. Без блокировки очередь - это просто буфер, который вы заполняете из потока реального времени, и после его заполнения вы атомизировали смещение записи (например, 'OSAtomicCompareAndSwap32Barrier'). Не забудьте использовать барьер здесь. В потоке пользовательского интерфейса вы устанавливаете таймер, скажем, 10 раз в секунду (все, что достаточно). Таймер proc проверяет смещение чтения очереди, которое является другой переменной, чем смещение записи. Если смещение записи продвинулось (т. Е. Не равно смещению чтения), вы читаете новые байты и обрабатываете их до тех пор, пока все новые байты не будут обработаны. – bio