📄 ao_macosx.c
字号:
fprintf(stderr, "ao_macosx_open: pthread_mutex_init returned %d\n", rc); return 0; } rc = pthread_cond_init(&internal->condition, NULL); if (rc) { fprintf(stderr, "ao_macosx_open: pthread_cond_init returned %d\n", rc); return 0; } /* Since we don't know how big to make the buffer until we open the device we allocate the buffer here instead of ao_plugin_device_init() */ internal->bufferByteCount = BUFFER_COUNT * internal->outputBufferByteCount; internal->firstValidByteOffset = 0; internal->validByteCount = 0; internal->buffer = malloc(internal->bufferByteCount); memset(internal->buffer, 0, internal->bufferByteCount); if (!internal->buffer) { fprintf(stderr, "ao_macosx_open: Unable to allocate queue buffer.\n"); return 0; } /* initialize debugging state */ internal->bytesQueued = 0; internal->bytesDequeued = 0; device->driver_byte_format = AO_FMT_NATIVE; return 1;}int ao_plugin_play(ao_device *device, const char *output_samples, uint_32 num_bytes){ ao_macosx_internal *internal = (ao_macosx_internal *) device->internal; OSStatus status;#if DEBUG_PIPE fprintf(stderr, "Enqueue: 0x%08x %d bytes\n", output_samples, num_bytes);#endif while (num_bytes) { unsigned int bytesToCopy; unsigned int firstEmptyByteOffset, emptyByteCount; // Get a consistent set of data about the available space in the queue, // figure out the maximum number of bytes we can copy in this chunk, // and claim that amount of space pthread_mutex_lock(&internal->mutex); // Wait until there is some empty space in the queue emptyByteCount = internal->bufferByteCount - internal->validByteCount; while (emptyByteCount == 0) { pthread_cond_wait(&internal->condition, &internal->mutex); emptyByteCount = internal->bufferByteCount - internal->validByteCount; } // Compute the offset to the first empty byte and the maximum number of // bytes we can copy given the fact that the empty space might wrap // around the end of the queue. firstEmptyByteOffset = (internal->firstValidByteOffset + internal->validByteCount) % internal->bufferByteCount; if (firstEmptyByteOffset + emptyByteCount > internal->bufferByteCount) bytesToCopy = MIN(num_bytes, internal->bufferByteCount - firstEmptyByteOffset); else bytesToCopy = MIN(num_bytes, emptyByteCount); // Copy the bytes and get ready for the next chunk, if any#if DEBUG_PIPE fprintf(stderr, "Enqueue:\tdst = 0x%08x src=0x%08x count=%d\n", internal->buffer + firstEmptyByteOffset, output_samples, bytesToCopy);#endif memcpy(internal->buffer + firstEmptyByteOffset, output_samples, bytesToCopy); /*{ unsigned int i; static unsigned char bufferIndex; bufferIndex++; memset(internal->buffer + firstEmptyByteOffset, bufferIndex, bytesToCopy); }*/ num_bytes -= bytesToCopy; output_samples += bytesToCopy; internal->validByteCount += bytesToCopy; internal->bytesQueued += bytesToCopy; //fprintf(stderr, "Copy: %d bytes, %d bytes left\n", bytesToCopy, internal->availableByteCount); pthread_mutex_unlock(&internal->mutex); // We have to wait to start the device until we have some data queued. // It might be better to wait until we have some minimum amount of data // larger than whatever blob got enqueued here, but if we had a short // stream, we'd have to make sure that ao_macosx_close() would start // AND stop the stream when it had finished. Yuck. If the first // blob that is passed to us is large enough (and the caller passes // data quickly enough, this shouldn't be a problem. #if 1 if (!internal->started) { internal->started = true; status = AudioDeviceStart(internal->outputDeviceID, audioDeviceIOProc); if (status) { fprintf(stderr, "ao_macosx_open: AudioDeviceStart returned %d\n", (int)status); // Can we do anything useful here? The library doesn't expect this call // to be able to fail. return 0; } }#endif } return 1;}int ao_plugin_close(ao_device *device){ ao_macosx_internal *internal = (ao_macosx_internal *) device->internal; OSStatus status; // Only stop if we ever got started if (internal->started) { internal->isStopping = true; // Wait for any pending data to get flushed pthread_mutex_lock(&internal->mutex); while (internal->validByteCount) pthread_cond_wait(&internal->condition, &internal->mutex); pthread_mutex_unlock(&internal->mutex); status = AudioDeviceStop(internal->outputDeviceID, audioDeviceIOProc); if (status) { fprintf(stderr, "ao_macosx_close: AudioDeviceStop returned %d\n", (int)status); return 0; } } status = AudioDeviceRemoveIOProc(internal->outputDeviceID, audioDeviceIOProc); if (status) { fprintf(stderr, "ao_macosx_close: AudioDeviceRemoveIOProc returned %d\n", (int)status); return 0; } return 1;}void ao_plugin_device_clear(ao_device *device){ ao_macosx_internal *internal = (ao_macosx_internal *) device->internal; free(internal->buffer); free(internal);}static OSStatus audioDeviceIOProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData){ ao_macosx_internal *internal = (ao_macosx_internal *)inClientData; short *sample; unsigned int validByteCount; float scale = (0.5f / SHRT_MAX), *outBuffer; unsigned int bytesToCopy, samplesToCopy; // Find the first valid frame and the number of valid frames pthread_mutex_lock(&internal->mutex); bytesToCopy = internal->outputBufferByteCount/2; validByteCount = internal->validByteCount; outBuffer = (float *)outOutputData->mBuffers[0].mData; if (validByteCount < bytesToCopy && !internal->isStopping) { // Not enough data ... let it build up a bit more before we start copying stuff over. // If we are stopping, of course, we should just copy whatever we have. memset(outBuffer, 0, bytesToCopy); pthread_mutex_unlock(&internal->mutex); return 0; } bytesToCopy = MIN(bytesToCopy, validByteCount); sample = internal->buffer + internal->firstValidByteOffset; samplesToCopy = bytesToCopy / sizeof(*sample); internal->bytesDequeued += bytesToCopy;#if DEBUG_PIPE fprintf(stderr, "IO: outputTime=%f firstValid=%d valid=%d toCopy=%d queued=%d dequeued=%d sample=0x%08x\n", inOutputTime->mSampleTime, internal->firstValidByteOffset, internal->validByteCount, samplesToCopy, internal->bytesQueued, internal->bytesDequeued, sample);#endif internal->validByteCount -= bytesToCopy; internal->firstValidByteOffset = (internal->firstValidByteOffset + bytesToCopy) % internal->bufferByteCount; // We don't have to deal with wrapping around in the buffer since the buffer is a // multiple of the output buffer size and we only copy on buffer at a time // (except on the last buffer when we may copy only a partial output buffer).#warning On the last buffer, zero out the part of the buffer that does not have valid samples while (samplesToCopy--) { short x = *sample;#warning The bytes in the buffer are currently in little endian, but we need big endian. Supposedly these are going to be host endian at some point and the following line of code can go away./* They will go away now, I think. --- Stan *//* x = ((x & 0xff00) >> 8) | ((x & 0x00ff) << 8); */ *outBuffer = x * scale; outBuffer++; sample++; } pthread_mutex_unlock(&internal->mutex); pthread_cond_signal(&internal->condition); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -