(sampleRate int, channelCount int, format mux.Format, bufferSizeInBytes int)
| 90 | var theContext *context |
| 91 | |
| 92 | func newContext(sampleRate int, channelCount int, format mux.Format, bufferSizeInBytes int) (*context, chan struct{}, error) { |
| 93 | // defaultOneBufferSizeInBytes is the default buffer size in bytes. |
| 94 | // |
| 95 | // 12288 seems necessary at least on iPod touch (7th) and MacBook Pro 2020. |
| 96 | // With 48000[Hz] stereo, the maximum delay is (12288*4[buffers] / 4 / 2)[samples] / 48000 [Hz] = 100[ms]. |
| 97 | // '4' is float32 size in bytes. '2' is a number of channels for stereo. |
| 98 | const defaultOneBufferSizeInBytes = 12288 |
| 99 | |
| 100 | var oneBufferSizeInBytes int |
| 101 | if bufferSizeInBytes != 0 { |
| 102 | oneBufferSizeInBytes = bufferSizeInBytes / bufferCount |
| 103 | } else { |
| 104 | oneBufferSizeInBytes = defaultOneBufferSizeInBytes |
| 105 | } |
| 106 | bytesPerSample := channelCount * 4 |
| 107 | oneBufferSizeInBytes = oneBufferSizeInBytes / bytesPerSample * bytesPerSample |
| 108 | |
| 109 | ready := make(chan struct{}) |
| 110 | |
| 111 | c := &context{ |
| 112 | cond: sync.NewCond(&sync.Mutex{}), |
| 113 | mux: mux.New(sampleRate, channelCount, format), |
| 114 | oneBufferSizeInBytes: oneBufferSizeInBytes, |
| 115 | } |
| 116 | theContext = c |
| 117 | |
| 118 | if err := initializeAPI(); err != nil { |
| 119 | return nil, nil, err |
| 120 | } |
| 121 | |
| 122 | go func() { |
| 123 | runtime.LockOSThread() |
| 124 | defer runtime.UnlockOSThread() |
| 125 | |
| 126 | var readyClosed bool |
| 127 | defer func() { |
| 128 | if !readyClosed { |
| 129 | close(ready) |
| 130 | } |
| 131 | }() |
| 132 | |
| 133 | q, bs, err := newAudioQueue(sampleRate, channelCount, oneBufferSizeInBytes) |
| 134 | if err != nil { |
| 135 | c.err.TryStore(err) |
| 136 | return |
| 137 | } |
| 138 | c.audioQueue = q |
| 139 | c.unqueuedBuffers = bs |
| 140 | |
| 141 | var retryCount int |
| 142 | try: |
| 143 | if osstatus := _AudioQueueStart(c.audioQueue, nil); osstatus != noErr { |
| 144 | if osstatus == avAudioSessionErrorCodeCannotStartPlaying && retryCount < 100 { |
| 145 | // TODO: use sleepTime() after investigating when this error happens. |
| 146 | time.Sleep(10 * time.Millisecond) |
| 147 | retryCount++ |
| 148 | goto try |
| 149 | } |
nothing calls this directly
no test coverage detected
searching dependent graphs…