Commits

Dave Dribin  committed 213b007

Add fence buffer and delegate

  • Participants
  • Parent commits 087fece

Comments (0)

Files changed (6)

File Source/DDAudioQueue.h

 
 - (BOOL)enqueueBuffer:(DDAudioQueueBuffer *)buffer;
 
+- (void)enqueueFenceBuffer;
+
 @end
 
 DDAudioQueueBuffer * DDAudioQueueDequeueBuffer(DDAudioQueue * queue);
 
 void DDAudioQueueMakeBufferAvailable(DDAudioQueue * queue, DDAudioQueueBuffer * buffer);
+
+DDAudioQueueBuffer * DDAudioQueueFenceBuffer;

File Source/DDAudioQueue.m

 
 COMPILE_ASSERT(offsetof(DDAudioQueueListNode, buffer) == 0, invalid_node_offset);
 
+static DDAudioQueueListNode sFenceNode;
+DDAudioQueueBuffer * DDAudioQueueFenceBuffer = &sFenceNode.buffer;
+
 @interface DDAudioQueue ()
 - (void *)malloc:(size_t)size;
 - (void)free:(void *)bytes;
 
 @implementation DDAudioQueue
 
++ (void)initialize
+{
+    if (self != [DDAudioQueue class]) {
+        return;
+    }
+    
+    DDAudioQueueBuffer tempBuffer = {
+        .capacity = 0,
+        .length = 0,
+        .bytes = NULL,
+    };
+    memcpy(DDAudioQueueFenceBuffer, &tempBuffer, sizeof(DDAudioQueueFenceBuffer));
+}
+
 - (id)initWithDelegate:(id<DDAudioQueueDelegate>)delegate;
 {
     self = [super init];
     [super dealloc];
 }
 
+- (void)callDelegateForBuffer:(DDAudioQueueBuffer *)buffer
+{
+    if (buffer == NULL) {
+        return;
+    }
+
+    NSLog(@"buffer: %p, fence: %p", buffer, DDAudioQueueFenceBuffer);
+    buffer->length = 0;
+    if (buffer == DDAudioQueueFenceBuffer) {
+        if ([_delegate respondsToSelector:@selector(audioQueueDidReceiveFence:)]) {
+            [_delegate audioQueueDidReceiveFence:self];
+        }
+    } else {
+        [self->_delegate audioQueue:self bufferIsAvailable:buffer];
+    }
+}
+
 - (void)sendAvaialableBuffersToDelegate;
 {
     DDAudioQueueBuffer * buffer = NULL;
     do {
         buffer = DDAtomicListPop(&_availableList, NODE_OFFSET);
-        if (buffer != NULL) {
-            buffer->length = 0;
-            [self->_delegate audioQueue:self bufferIsAvailable:buffer];
-        }
+        [self callDelegateForBuffer:buffer];
     } while (buffer != NULL);
 }
 
     return YES;
 }
 
+- (void)enqueueFenceBuffer;
+{
+    [self enqueueBuffer:DDAudioQueueFenceBuffer];
+}
+
+- (void *)malloc:(size_t)size;
+{
+    NSMutableData * data = [NSMutableData dataWithLength:size];
+    void * bytes = [data mutableBytes];
+    NSValue * bytesValue = [NSValue valueWithPointer:bytes];
+    [_mallocData setObject:data forKey:bytesValue];
+    return bytes;
+}
+
+- (void)free:(void *)bytes;
+{
+    NSValue * bytesValue = [NSValue valueWithPointer:bytes];
+    [_mallocData removeObjectForKey:bytesValue];
+}
+
+#pragma mark -
+#pragma mark C API
+
 DDAudioQueueBuffer * DDAudioQueueDequeueBuffer(DDAudioQueue * queue)
 {
     DDAudioQueueBuffer * buffer = DDAtomicListPop(&queue->_renderList, NODE_OFFSET);
     CFRunLoopWakeUp(queue->_runLoop);
 }
 
-- (void *)malloc:(size_t)size;
-{
-    NSMutableData * data = [NSMutableData dataWithLength:size];
-    void * bytes = [data mutableBytes];
-    NSValue * bytesValue = [NSValue valueWithPointer:bytes];
-    [_mallocData setObject:data forKey:bytesValue];
-    return bytes;
-}
-
-- (void)free:(void *)bytes;
-{
-    NSValue * bytesValue = [NSValue valueWithPointer:bytes];
-    [_mallocData removeObjectForKey:bytesValue];
-}
-
 @end

File Source/DDAudioQueueDelegate.h

 
 - (void)audioQueue:(DDAudioQueue *)queue bufferIsAvailable:(DDAudioQueueBuffer *)buffer;
 
+@optional
+
+- (void)audioQueueDidReceiveFence:(DDAudioQueue *)queue;
+
 @end

File Tests/DDAudioQueueReaderTest.m

 - (void)audioQueue:(DDAudioQueue *)queue bufferIsAvailable:(DDAudioQueueBuffer *)buffer;
 {
     STAssertEquals(_queue, queue, nil);
-    NSLog(@"bufferIsAvailable: %p", buffer);
     [_availableBuffers addObject:[NSValue valueWithPointer:buffer]];
 }
 
     STAssertEquals([self availableBuffer:0], [self buffer:0], nil);
 }
 
-- (void)testMakesBothBuffersAavailableAfterMultiBufferRead
+- (void)testMakesAllBuffersAavailableAfterMultiBufferRead
 {
     [self enqueueBuffer:0 withValue:0x01 length:10];
     [self enqueueBuffer:1 withValue:0x02 length:10];
+    [self enqueueBuffer:2 withValue:0x03 length:10];
     
-    [self readBytes:20];
+    [self readBytes:30];
     [self spinRunLoop];
     
+    STAssertEquals([_availableBuffers count], (NSUInteger)3, nil);
+    // They become available in reverse order
+    STAssertEquals([self availableBuffer:0], [self buffer:2], nil);
+    STAssertEquals([self availableBuffer:1], [self buffer:1], nil);
+    STAssertEquals([self availableBuffer:2], [self buffer:0], nil);
+}
+
+- (void)testReadingMoreBytesThanEqueued
+{
+    [self enqueueBuffer:0 withValue:0x01 length:10];
+    [self enqueueBuffer:1 withValue:0x02 length:10];
+    [self enqueueBuffer:2 withValue:0x03 length:10];
+    
+    [self readBytes:40];
+    [self spinRunLoop];
+    
+    STAssertEquals([_availableBuffers count], (NSUInteger)3, nil);
+    // They become available in reverse order
+    STAssertEquals([self availableBuffer:0], [self buffer:2], nil);
+    STAssertEquals([self availableBuffer:1], [self buffer:1], nil);
+    STAssertEquals([self availableBuffer:2], [self buffer:0], nil);
+}
+
+- (void)testStopsReadingAtFence
+{
+    [self enqueueBuffer:0 withValue:0x01 length:10];
+    [self enqueueBuffer:1 withValue:0x02 length:10];
+    [_queue enqueueFenceBuffer];
+    [self enqueueBuffer:2 withValue:0x03 length:10];
+    
+    UInt32 bytesRead = [self readBytes:READ_BUFFER_SIZE];
+    [self spinRunLoop];
+    
+    STAssertEquals(bytesRead, (UInt32)20, nil);
     STAssertEquals([_availableBuffers count], (NSUInteger)2, nil);
     // They become available in reverse order
     STAssertEquals([self availableBuffer:0], [self buffer:1], nil);
     STAssertEquals([self availableBuffer:1], [self buffer:0], nil);
 }
 
+- (void)testContinuesReadingAtFence
+{
+    [self enqueueBuffer:0 withValue:0x01 length:10];
+    [self enqueueBuffer:1 withValue:0x02 length:10];
+    [_queue enqueueFenceBuffer];
+    [self enqueueBuffer:2 withValue:0x03 length:10];
+    [self readBytes:READ_BUFFER_SIZE];
+    [self spinRunLoop];
+    [_availableBuffers removeAllObjects];
+    
+    UInt32 bytesRead = [self readBytes:READ_BUFFER_SIZE];
+    [self spinRunLoop];
+    
+    STAssertEquals(bytesRead, (UInt32)10, nil);
+    STAssertEquals([_availableBuffers count], (NSUInteger)1, nil);
+    STAssertEquals([self availableBuffer:0], [self buffer:2], nil);
+}
+
 @end

File Tests/DDAudioQueueTest.h

     DDAudioQueue * _queue;
     NSMutableArray * _buffers;
     NSMutableArray * _availableBuffers;
+    int _fenceCount;
 }
 
 @end

File Tests/DDAudioQueueTest.m

     [_availableBuffers addObject:[NSValue valueWithPointer:buffer]];
 }
 
+- (void)audioQueueDidReceiveFence:(DDAudioQueue *)queue;
+{
+    _fenceCount++;
+}
+
 - (DDAudioQueueBuffer *)availableBuffer:(NSUInteger)index
 {
     NSValue * value = [_availableBuffers objectAtIndex:index];
     [_queue scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
     _buffers = [NSMutableArray array];
     _availableBuffers = [NSMutableArray array];
+    _fenceCount = 0;
 }
 
 - (void)tearDown
     STAssertNull(DDAudioQueueDequeueBuffer(_queue), nil);
 }
 
+#pragma mark -
+
+- (void)testDoesNotCallFenceDelegateIfNoFenceIsEnqueued
+{
+    [self allocateBuffers:1];
+    [_queue enqueueBuffer:[self buffer:0]];
+    [self dequeueAndMakeAvailable];
+    
+    [self spinRunLoop];
+    
+    STAssertEquals(_fenceCount, 0, nil);
+}
+
+- (void)testCallsFenceDelegateAfterEnqueuingAndProcessingFence
+{
+    [_queue enqueueFenceBuffer];
+    [self dequeueAndMakeAvailable];
+    
+    [self spinRunLoop];
+    
+    STAssertEquals(_fenceCount, 1, nil);
+}
+
+- (void)testCallsFenceDelegateAfterDequeingAllBuffers
+{
+    [self allocateBuffers:1];
+    [_queue enqueueBuffer:[self buffer:0]];
+    [self dequeueAndMakeAvailable];
+
+    [_queue enqueueFenceBuffer];
+    [self dequeueAndMakeAvailable];
+    
+    [self spinRunLoop];
+    
+    STAssertEquals(_fenceCount, 1, nil);
+}
+
 @end