|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24: /*
25: * AudioStream.m
26: *
27: * Copyright (c) 1991, NeXT Computer, Inc. All rights reserved.
28: *
29: * Audio driver channel object.
30: *
31: * HISTORY
32: * 07/14/92/mtm Original coding.
33: */
34:
35: #import "AudioStream.h"
36: #import <driverkit/IOAudioPrivate.h>
37: #import "AudioChannel.h"
38: #import "audioLog.h"
39: #import "snd_reply.h"
40: #import <kernserv/prototypes.h>
41: #import <mach/mach_types.h>
42: #import "audioReply.h"
43: #import <bsd/sys/time.h>
44: #import <mach/mach_user_internal.h>
45: #import <mach/mach_interface.h>
46: #import <kernserv/kern_server_types.h>
47: #import <driverkit/kernelDriver.h>
48: #import <kernserv/i386/us_timer.h> // for microtime
49: #import <bsd/machine/limits.h> // ULONG_LONG_MAX
50:
51: /* Factory variables */
52:
53: static void *threadArg = 0;
54: static NXLock *threadArgLock = nil;
55:
56: @implementation AudioStream
57:
58: /*
59: * Initialize.
60: */
61: - initChannel:chan tag:(int)aTag user:(port_t *)user
62: owner:(port_t)owner type:(u_int)aType
63: {
64: kern_return_t krtn;
65:
66: xpr_audio_stream("AS: initChannel chan=0x%x tag=%d, user=0x%x owner=%d, "
67: "type=%d\n", chan, aTag, user, owner, aType);
68: [super init];
69: channel = chan;
70: device = [chan audioDevice];
71: tag = aTag;
72: samplingRate = 22050;
73: dataFormat = IOAudioDataFormatLinear16;
74: channelCount = 2;
75: queue_init(®ionQueue);
76: regionQueueLock = [[NXLock alloc] init];
77: krtn = port_allocate(task_self(), user);
78: if (krtn) {
79: xpr_audio_stream("AS: initChannel: stream port_allocate error %d\n",
80: krtn,2,3,4,5);
81: log_error(("Audio: initChannel: stream port_allocate: %s\n",
82: mach_error_string(krtn)));
83: [self free];
84: return nil;
85: }
86: userPort = *user;
87: kernUserPort = IOConvertPort(userPort, IO_CurrentTask, IO_Kernel);
88: ownerPort = owner;
89: type = aType;
90:
91: return self;
92: }
93:
94: /*
95: * Return instance variables.
96: */
97: - (port_t)userPort
98: {
99: return userPort;
100: }
101: - (port_t)ownerPort
102: {
103: return ownerPort;
104: }
105: - channel
106: {
107: return channel;
108: }
109: - (ASType)type
110: {
111: return type;
112: }
113:
114: /*
115: * Init snd reply msg.
116: */
117: - createSndReplyMsg
118: {
119: if (!sndReplyMsg)
120: sndReplyMsg = (msg_header_t *)IOMalloc(MSG_SIZE_MAX);
121: sndReplyMsg->msg_simple = TRUE;
122: sndReplyMsg->msg_size = sizeof(msg_header_t);
123: sndReplyMsg->msg_type = MSG_TYPE_NORMAL;
124: sndReplyMsg->msg_local_port = PORT_NULL;
125: sndReplyMsg->msg_remote_port = PORT_NULL;
126: sndReplyMsg->msg_id = 0;
127: return self;
128: }
129:
130: /*
131: * Send user stream control reply message.
132: */
133: - sendControlMessage:(ASReplyMsg)status mask:(ASMsgRequest)statusMask
134: {
135: kern_return_t krtn;
136: region_t *region;
137: port_t ourReplyPort, ourUserPort;
138:
139: xpr_audio_stream("AS: sendControlMsg status=0x%x, mask=0x%x\n", status,
140: statusMask, 3,4,5);
141:
142: /*
143: * If new interface, send one message and return.
144: */
145: if (type == AS_TypeUser) {
146: if (userReplyMessages & statusMask) {
147: ourReplyPort = IOConvertPort(userReplyPort, IO_Kernel,
148: IO_KernelIOTask);
149: ourUserPort = IOConvertPort(kernUserPort, IO_Kernel,
150: IO_KernelIOTask);
151: krtn = _NXAudioReplyStreamStatus(ourReplyPort, ourUserPort,
152: ourReplyPort, tag,
153: 0, status);
154: if (krtn != KERN_SUCCESS) {
155: xpr_audio_stream("AS: sendControlMsg: replyStreamStatus "
156: "error %s (%d)\n",
157: mach_error_string(krtn), krtn, 3,4,5);
158: }
159: }
160: return self;
161: }
162:
163: /*
164: * Old interface sends one per request, and may send none.
165: */
166: xpr_audio_stream("AS: sendControlMsg: locking regionQueue\n", 1,2,3,4,5);
167: [regionQueueLock lock];
168: if (queue_empty(®ionQueue)) {
169: [regionQueueLock unlock];
170: xpr_audio_stream("AS: sendControlMsg: unlocked regionQueue\n",
171: 1,2,3,4,5);
172: return self;
173: }
174: [self createSndReplyMsg];
175:
176: region = (region_t *)queue_first(®ionQueue);
177: while (!queue_end(®ionQueue, (queue_entry_t)region)) {
178: if (region->messages & statusMask) {
179: ourReplyPort = IOConvertPort(region->reply_port, IO_Kernel,
180: IO_KernelIOTask);
181: if (status == AS_ReplyPaused)
182: audio_snd_reply_paused(sndReplyMsg, ourReplyPort, region->tag);
183: else if (status == AS_ReplyResumed)
184: audio_snd_reply_resumed(sndReplyMsg, ourReplyPort, region->tag);
185: else if (status == AS_ReplyAborted)
186: audio_snd_reply_aborted(sndReplyMsg, ourReplyPort, region->tag);
187: krtn = msg_send(sndReplyMsg, STREAM_SEND_OPTIONS,
188: STREAM_SEND_TIMEOUT);
189: if (krtn != KERN_SUCCESS) {
190: xpr_audio_stream("AS: sendControlMsg msg_send error %s"
191: " (%d)\n", mach_error_string(krtn),
192: krtn, 3,4,5);
193: }
194: }
195: region = (region_t *)queue_next(®ion->link);
196: }
197:
198: [regionQueueLock unlock];
199: xpr_audio_stream("AS: sendControlMsg: unlocked regionQueue\n", 1,2,3,4,5);
200: return self;
201: }
202:
203: /*
204: * Send user stream status message.
205: */
206: - sendStatusMessage:(ASReplyMsg)status forRegion:(region_t *)region
207: {
208: kern_return_t krtn;
209: port_t ourReplyPort, ourUserPort;
210:
211: xpr_audio_stream("AS: sendStatusMsg %d for region 0x%x\n", status,
212: region, 3,4,5);
213:
214: ourReplyPort = IOConvertPort(region->reply_port, IO_Kernel,
215: IO_KernelIOTask);
216: ourUserPort = IOConvertPort(kernUserPort, IO_Kernel, IO_KernelIOTask);
217:
218: if (type == AS_TypeUser) {
219: if (status == AS_ReplyAborted && region->flags.excluded)
220: status = AS_ReplyExcluded;
221: krtn = _NXAudioReplyStreamStatus(ourReplyPort, ourUserPort,
222: ourReplyPort, tag,
223: region->tag, status);
224: if (krtn != KERN_SUCCESS) {
225: IOLog("AS: replyStreamStatus returns %d\n", krtn);
226: xpr_audio_stream("AS: replyStreamStatus error %s (%d)\n",
227: mach_error_string(krtn), krtn, 3,4,5);
228: }
229: } else {
230: [self createSndReplyMsg];
231: if (status == _NXAUDIO_STATUS_STARTED)
232: audio_snd_reply_started(sndReplyMsg, ourReplyPort, region->tag);
233: else if (status == _NXAUDIO_STATUS_COMPLETED)
234: audio_snd_reply_completed(sndReplyMsg, ourReplyPort, region->tag);
235: else if (status == _NXAUDIO_STATUS_UNDERRUN)
236: audio_snd_reply_overflow(sndReplyMsg, ourReplyPort, region->tag);
237: krtn = msg_send(sndReplyMsg, STREAM_SEND_OPTIONS, STREAM_SEND_TIMEOUT);
238: if (krtn != KERN_SUCCESS) {
239: xpr_audio_stream("AS: sendStatusMsg msg_send error %s (%d)\n",
240: mach_error_string(krtn), krtn, 3,4,5);
241: }
242: }
243: return self;
244: }
245:
246: /*
247: * Free a region.
248: * Output stream subclass overrides to free region data.
249: */
250: - freeRegion:(region_t *)region
251: {
252: IOFree((void *)region, sizeof(region_t));
253: return self;
254: }
255:
256: /*
257: * Subclass can implement.
258: */
259: - completeRegion:(region_t *)region descriptor:(dma_desc_t *)ddp
260: size:(u_int)xfer used:(u_int *)used
261: {
262: return self;
263: }
264:
265: /*
266: * Handle dma descriptor complete.
267: * Input streams overrides to check for recorded data to return.
268: */
269: - dmaCompleteDescriptor:(dma_desc_t *)ddp transfered:(u_int)count
270: {
271: region_t *region;
272: queue_chain_t *region_link;
273: u_int used = 0;
274: BOOL sentAbort = NO;
275:
276: xpr_audio_stream("AS: dmaComp desc=0x%x, size=%d, xfer=%d\n",
277: ddp, ddp->size, count, 4,5);
278:
279: xpr_audio_stream("AS: dmaComp: locking regionQueue\n", 1,2,3,4,5);
280: [regionQueueLock lock];
281: if (queue_empty(®ionQueue)) {
282: [regionQueueLock unlock];
283: xpr_audio_stream("AS: dmaComp: unlocked regionQueue\n", 1,2,3,4,5);
284: return self;
285: }
286:
287: region = (region_t *)queue_first(®ionQueue);
288: while (!queue_end(®ionQueue, (queue_entry_t)region)) {
289: xpr_audio_stream("AS: dmaComplete region 0x%x\n", region, 2,3,4,5);
290:
291: if (region->start_ddp == ddp) {
292: xpr_audio_stream("AS: dmaComplete started region\n", 1,2,3,4,5);
293: if (region->messages & AS_RequestStarted)
294: [self sendStatusMessage:AS_ReplyStarted forRegion:region];
295: region->start_ddp = 0;
296: }
297:
298: [self completeRegion:region descriptor:ddp size:count used:&used];
299:
300: if ((region->end_ddp == ddp) || region->flags.aborted
301: || region->flags.split) {
302: xpr_audio_stream("AS: dmaComplete completed region\n", 1,2,3,4,5);
303: /*
304: * Old style (snd) streams get an abort message
305: * for every region.
306: */
307: if (region->flags.aborted &&
308: (region->messages & AS_RequestAborted) &&
309: (!sentAbort || type == AS_TypeSnd)) {
310: [self sendStatusMessage:AS_ReplyAborted forRegion:region];
311: sentAbort = YES;
312: }
313: /*
314: * Note snd_server interface does not allow completed messsage
315: * bit to be set if recording (for compatibility with old driver).
316: */
317: if (!region->flags.aborted && !region->flags.split &&
318: (region->messages & AS_RequestCompleted))
319: [self sendStatusMessage:AS_ReplyCompleted forRegion:region];
320: /*
321: * Free and remove this completed region.
322: */
323: region_link = ®ion->link;
324: queue_remove(®ionQueue, region, region_t *, link);
325: [self freeRegion:region];
326: region = (region_t *)queue_next(region_link);
327: continue;
328: }
329: if (used >= count)
330: break;
331:
332: region = (region_t *)queue_next(®ion->link);
333: }
334:
335: [regionQueueLock unlock];
336: xpr_audio_stream("AS: dmaComp: unlocked regionQueue\n", 1,2,3,4,5);
337: return self;
338: }
339:
340: /*
341: * Subclass must implement.
342: */
343: - (u_int)mixRegion:(region_t *)region descriptor:(dma_desc_t *)ddp
344: buffer:(vm_address_t)data maxCount:(u_int)max
345: virgin:(BOOL)isVirgin
346: rate:(u_int)srate format:(IOAudioDataFormat)format
347: channelCount:(u_int)chans
348:
349: {
350: log_error(("Audio: subclass does not implement mixRegion\n"));
351: return 0;
352: }
353:
354: /*
355: * Playback stream overrides this.
356: */
357: - clearForMix:(char *)buf size:(u_int)count format:(IOAudioDataFormat)format
358: {
359: return self;
360: }
361:
362: /*
363: * Determine if data can be converted to current device parameters.
364: * Subclass overrides if it can do any conversions.
365: */
366: - (BOOL)canConvertRegion:(region_t *)region
367: rate:(u_int)srate format:(IOAudioDataFormat)format
368: channelCount:(u_int)chans
369:
370: {
371: if (srate == samplingRate &&
372: format == dataFormat &&
373: chans == channelCount)
374: return YES;
375: else
376: return NO;
377: }
378:
379: /*
380: * Mix into a dma buffer.
381: */
382: - (u_int)mixBuffer:(vm_address_t)data maxCount:(u_int)max
383: rate:(u_int *)srate format:(IOAudioDataFormat *)format
384: channelCount:(u_int *)chans descriptor:(dma_desc_t *)ddp
385: virgin:(BOOL)isVirgin streamCount:(u_int)streams
386: {
387: region_t *region;
388: u_int count = 0;
389: u_int mixCount;
390: u_int temp_align_buffer;
391:
392: if (isPaused) {
393: xpr_audio_stream("AS: mixBuffer: stream is paused\n", 1,2,3,4,5);
394: return 0;
395: }
396:
397: xpr_audio_stream("AS: mixBuffer: locking regionQueue\n", 1,2,3,4,5);
398: [regionQueueLock lock];
399: if (queue_empty(®ionQueue)) {
400: [regionQueueLock unlock];
401: xpr_audio_stream("AS: mixBuffer: unlocked regionQueue\n", 1,2,3,4,5);
402: return 0;
403: }
404:
405: region = (region_t *)queue_first(®ionQueue);
406: while (!queue_end(®ionQueue, (queue_entry_t)region)) {
407: xpr_audio_stream("AS: mixBuffer: mix region 0x%x\n", region, 2,3,4,5);
408: /*
409: * Skip completed or aborted region.
410: */
411: if ((region->enq_ptr >= region->end) || region->flags.aborted) {
412: region = (region_t *)queue_next(®ion->link);
413: continue;
414: }
415:
416: if (*srate == 0)
417: *srate = samplingRate;
418: if (*format == IOAudioDataFormatUnset)
419: *format = dataFormat;
420: if (*chans == 0)
421: *chans = channelCount;
422:
423: /*
424: * FIXME: abort/deq if can't convert?
425: */
426: if (![self canConvertRegion:region rate:*srate
427: format:*format channelCount: *chans]) {
428: xpr_audio_stream("AS: mixBuffer: mix region can not convert to "
429: "srate=%d, "
430: "fmt=%d chans=%d\n", *srate, *format, *chans,
431: 4,5);
432: region = (region_t *)queue_next(®ion->link);
433: continue;
434: }
435:
436: temp_align_buffer = (data + count);
437:
438: #if defined(sparc) || defined(hppa)
439: if ((dataFormat == IOAudioDataFormatLinear16) && (temp_align_buffer & 1))
440: temp_align_buffer &= 0xfffffffe;
441:
442: #endif
443:
444: mixCount = [self mixRegion:region descriptor:ddp
445: buffer:(temp_align_buffer)
446: maxCount:(max - count) virgin:isVirgin
447: rate:*srate format:*format
448: channelCount:*chans];
449:
450: if (!region->flags.started) {
451: region->start_ddp = ddp;
452: region->flags.started = TRUE;
453: }
454: if (region->enq_ptr >= region->end && !region->flags.ended) {
455: region->end_ddp = ddp;
456: region->flags.ended = TRUE;
457: }
458:
459: xpr_audio_stream("AS: mixBuffer: enq_ptr=0x%x\n", region->enq_ptr,
460: 2,3,4,5);
461: count += mixCount;
462: if (count >= max)
463: break;
464: region = (region_t *)queue_next(®ion->link);
465: }
466:
467: [regionQueueLock unlock];
468: xpr_audio_stream("AS: mixBuffer: unlocked regionQueue\n", 1,2,3,4,5);
469: /*
470: * First playback mix needs to clear buffer to end.
471: */
472: if (isVirgin && (count < max))
473: [self clearForMix:(char *)(data + count) size:(max - count)
474: format: *format];
475:
476: return count;
477: }
478:
479: /*
480: * Mark current regions as aborted.
481: * Return YES if any region marked.
482: */
483: - (BOOL)markAbortionsExclude:(BOOL)excluded
484: {
485: region_t *region;
486: BOOL ret = NO;
487:
488: xpr_audio_stream("AS: markAbortions: locking regionQueue\n", 1,2,3,4,5);
489: [regionQueueLock lock];
490: if (queue_empty(®ionQueue)) {
491: [regionQueueLock unlock];
492: xpr_audio_stream("AS: markAbortions: unlocked regionQueue\n",
493: 1,2,3,4,5);
494: return ret;
495: }
496:
497: region = (region_t *)queue_first(®ionQueue);
498: while (!queue_end(®ionQueue, (queue_entry_t)region)) {
499: region->flags.aborted = TRUE;
500: region->flags.excluded = excluded;
501: ret = YES;
502: xpr_audio_stream("AS: markAbortions: region=0x%x, excl=%d\n",
503: region, excluded, 3,4,5);
504: region = (region_t *)queue_next(®ion->link);
505: }
506:
507: [regionQueueLock unlock];
508: xpr_audio_stream("AS: markAbortions: unlocked regionQueue\n", 1,2,3,4,5);
509: return ret;
510: }
511:
512: /*
513: * Subtract timevals.
514: */
515: static void subtract_times(struct timeval *t1, struct timeval *t2)
516: {
517: t1->tv_sec -= t2->tv_sec;
518: t1->tv_usec -= t2->tv_usec;
519: if (t1->tv_usec < 0) {
520: t1->tv_sec--;
521: t1->tv_usec += 1000000;
522: }
523: }
524:
525: /*
526: * Thread to send timed controls.
527: * One each launched for pause, resume, and abort.
528: */
529: static void controlThread(void *arg)
530: {
531: port_t listenPort;
532: kern_return_t krtn;
533: control_msg_t *controlMsg =
534: (control_msg_t *)IOMalloc(sizeof(control_msg_t));
535: int timeout = 0;
536: AudioStream *stream = nil;
537: ACStreamControl action = 0;
538: struct timeval now, when;
539: #ifndef KERNEL
540: struct timezone tz;
541: #endif KERNEL
542:
543: #ifdef KERNEL
544: listenPort = (port_t)threadArg;
545: [threadArgLock unlock];
546: #else KERNEL
547: listenPort = (port_t)arg;
548: #endif KERNEL
549:
550: xpr_audio_stream("AS: controlThread: port=0x%x\n", listenPort, 2,3,4,5);
551:
552: while (1) {
553: controlMsg->header.msg_local_port = listenPort;
554: controlMsg->header.msg_size = sizeof(control_msg_t);
555: if (timeout > 0)
556: krtn = msg_receive(&controlMsg->header, RCV_TIMEOUT, timeout);
557: else
558: krtn = msg_receive(&controlMsg->header, MSG_OPTION_NONE, 0);
559: /*
560: * Timeout elapsed, perform stream control action.
561: */
562: if (krtn == RCV_TIMED_OUT) {
563: [stream control:action];
564: timeout = 0;
565: continue;
566: }
567: /*
568: * Exit when port goes stale.
569: */
570: if (krtn != KERN_SUCCESS) {
571: xpr_audio_stream("AS: controlThread msg_rcv error %d\n", krtn,
572: 2,3,4,5);
573: break;
574: }
575: /*
576: * Request for action, calculate timeout.
577: */
578: if (controlMsg->header.msg_id != AC_CONTROL_THREAD_MSG) {
579: xpr_audio_stream("AS: controlThread bad msg id=%d\n",
580: controlMsg->header.msg_id, 2,3,4,5);
581: break;
582: }
583: stream = (AudioStream *)controlMsg->obj;
584: action = controlMsg->action;
585: when = *controlMsg->when;
586: #ifdef KERNEL
587: microtime(&now);
588: #else KERNEL
589: gettimeofday(&now, &tz);
590: #endif KERNEL
591: subtract_times(&when, &now);
592: timeout = when.tv_sec * 1000;
593: timeout += when.tv_usec / 1000;
594: xpr_audio_stream("AS: controlThread timeout = %d\n", timeout,
595: 2,3,4,5);
596: if (timeout <= 0) {
597: [stream control:action];
598: timeout = 0;
599: }
600: }
601: xpr_audio_stream("AS: controlThread: port=0x%x exits\n", listenPort,
602: 2,3,4,5);
603: IOExitThread();
604: }
605:
606: /*
607: * Timed stream control.
608: */
609: static void timedControl(port_t *threadPort, id self, ACStreamControl action,
610: struct timeval *when)
611: {
612: static const control_msg_t msg = {
613: {
614: 0, /* msg_unused */
615: TRUE, /* msg_simple */
616: sizeof(control_msg_t), /* msg_size */
617: MSG_TYPE_NORMAL, /* msg_type */
618: PORT_NULL, /* msg_local_port */
619: PORT_NULL, /* msg_remote_port */
620: AC_CONTROL_THREAD_MSG /* msg_id */
621: },
622: {
623: MSG_TYPE_INTEGER_32, /* msg_type_name */
624: 32, /* msg_type_size */
625: 3, /* msg_type_number */
626: FALSE, /* msg_type_longform */
627: FALSE /* msg_type_deallocate */
628: },
629: 0,
630: 0,
631: 0
632: };
633: control_msg_t *controlMsg =
634: (control_msg_t *)IOMalloc(sizeof(control_msg_t));
635: kern_return_t krtn;
636:
637: if (!(*threadPort)) {
638: krtn = port_allocate(task_self(), threadPort);
639: if (krtn) {
640: log_error(("Audio: stream control thread port_allocate: %s\n",
641: mach_error_string(krtn)));
642: audio_panic();
643: }
644: #ifdef KERNEL
645: if (threadArgLock == nil)
646: threadArgLock = [[NXLock alloc] init];
647: [threadArgLock lock];
648: threadArg = (void *)*threadPort;
649: #ifdef DEBUG
650: IOLog("threadArg = 0x%x\n", threadArg);
651: #endif DEBUG
652: kernel_thread(current_task_EXTERNAL(), (void *)controlThread);
653: #else KERNEL
654: (void)IOForkThread((IOThreadFunc)controlThread, (void *)*threadPort);
655: #endif
656: }
657: *controlMsg = msg;
658: controlMsg->header.msg_remote_port = *threadPort;
659: controlMsg->obj = self;
660: controlMsg->action = action;
661: controlMsg->when = when;
662:
663: krtn = msg_send(&controlMsg->header, SEND_TIMEOUT, 1000);
664: if (krtn != KERN_SUCCESS) {
665: xpr_audio_stream("AS: timedControl msg_send error %s (%d)\n",
666: mach_error_string(krtn), krtn, 3,4,5);
667: }
668: }
669:
670: /*
671: * Stream control.
672: */
673: - control:(ACStreamControl)action atTime:(struct timeval)when
674: {
675: switch (action) {
676: case AC_ControlPause:
677: if (timerisset(&when)) {
678: pauseTime = when;
679: timedControl(&pausePort, self, action, &pauseTime);
680: } else {
681: isPaused = YES;
682: [self sendControlMessage:AS_ReplyPaused mask:AS_RequestPaused];
683: }
684: break;
685: case AC_ControlResume:
686: if (timerisset(&when)) {
687: resumeTime = when;
688: timedControl(&resumePort, self, action, &resumeTime);
689: } else {
690: isPaused = NO;
691: [self sendControlMessage:AS_ReplyResumed mask:AS_RequestResumed];
692: [device _dataPendingForChannel: [self channel]];
693: }
694: break;
695: case AC_ControlAbort:
696: if (timerisset(&when)) {
697: abortTime = when;
698: timedControl(&abortPort, self, action, &abortTime);
699: } else if (![self markAbortionsExclude:NO]) {
700: /*
701: * Send reply messages now if no regions to abort.
702: */
703: [self sendControlMessage:AS_ReplyAborted mask:AS_RequestAborted];
704: }
705: break;
706: case AC_ControlExclude:
707: [self markAbortionsExclude:YES];
708: break;
709: default:
710: log_error(("audio: unrecognized stream control %d\n", action));
711: break;
712: }
713: return self;
714: }
715:
716: /*
717: * Stream control.
718: */
719: - control:(ACStreamControl)action
720: {
721: struct timeval now;
722:
723: timerclear(&now);
724: return [self control:action atTime:now];
725: }
726:
727: /*
728: * InputStream overrides.
729: */
730: - returnRecordedData
731: {
732: return self;
733: }
734:
735: /*
736: * Free regions and region queue.
737: */
738: - freeRegions
739: {
740: region_t *region;
741:
742: xpr_audio_stream("AS: free regions\n", 1,2,3,4,5);
743: while (!queue_empty(®ionQueue)) {
744: queue_remove_first(®ionQueue, region, region_t *, link);
745: [self freeRegion:region];
746: }
747: return self;
748: }
749:
750: /*
751: * Allocate and init a region.
752: */
753: - (region_t *)newRegion
754: {
755: region_t *region = (region_t *)IOMalloc(sizeof(region_t));
756:
757: bzero((char *)region, sizeof(region_t));
758: return region;
759: }
760:
761: /*
762: * Free resources and object.
763: */
764: - free
765: {
766: kern_return_t krtn;
767:
768: if (pausePort)
769: port_deallocate(task_self(), pausePort);
770: if (resumePort)
771: port_deallocate(task_self(), resumePort);
772: if (abortPort)
773: port_deallocate(task_self(), abortPort);
774: [self freeRegions];
775: krtn = port_deallocate(task_self(), userPort);
776: if (krtn)
777: log_error(("Audio: stream port_deallocate: %s\n",
778: mach_error_string(krtn)));
779: [regionQueueLock free];
780: if (sndReplyMsg)
781: IOFree(sndReplyMsg, MSG_SIZE_MAX);
782: return [super free];
783: }
784:
785: /*
786: * Set stream owner port.
787: */
788: - setOwner:(port_t)newOwner
789: {
790: ownerPort = newOwner;
791: return self;
792: }
793:
794: /*
795: * This is dependent upon the machine representation of ns_time_t.
796: */
797:
798: - (BOOL)bytesProcessed:(unsigned int *)num atTime:(unsigned int *)ts
799: {
800: ns_time_t interruptTime, startTime;
801:
802: startTime = [device _outputStartTime];
803: interruptTime = [device _lastInterruptTimeStamp];
804:
805: /* Almost certainly the DMA hasn't started yet. */
806: if (interruptTime == 0) {
807: *num = *ts = 0;
808: return NO;
809: }
810:
811: interruptTime /= 1000; // microseconds
812: startTime /= 1000;
813:
814: xpr_audio_stream("AS: byteCount %d at %d\n",
815: bytesProcessed, interruptTime,3,4,5);
816:
817: if (ts != NULL)
818: *ts = (unsigned int) interruptTime;
819: *num = bytesProcessed;
820:
821: return YES;
822: }
823:
824: - (NXSoundParameterTag)dataEncoding
825: {
826: /* FIXME: change dataFormat to dataEncoding */
827:
828: switch (dataFormat) {
829: case IOAudioDataFormatLinear16:
830: return NX_SoundStreamDataEncoding_Linear16;
831: case IOAudioDataFormatLinear8:
832: return NX_SoundStreamDataEncoding_Linear8;
833: case IOAudioDataFormatMulaw8:
834: return NX_SoundStreamDataEncoding_Mulaw8;
835: case IOAudioDataFormatAlaw8:
836: return NX_SoundStreamDataEncoding_Alaw8;
837: default:
838: IOLog("Audio: unrecognized data format: %d\n", dataFormat);
839: return -1;
840: }
841: }
842: - (void)setDataEncoding:(NXSoundParameterTag)encoding
843: {
844: /* FIXME: change dataFormat to dataEncoding */
845:
846: switch (encoding) {
847: case NX_SoundStreamDataEncoding_Linear16:
848: dataFormat = IOAudioDataFormatLinear16;
849: break;
850: case NX_SoundStreamDataEncoding_Linear8:
851: dataFormat = IOAudioDataFormatLinear8;
852: break;
853: case NX_SoundStreamDataEncoding_Mulaw8:
854: dataFormat = IOAudioDataFormatMulaw8;
855: break;
856: case NX_SoundStreamDataEncoding_Alaw8:
857: dataFormat = IOAudioDataFormatAlaw8;
858: break;
859: case NX_SoundStreamDataEncoding_AES:
860: dataFormat = IOAudioDataFormatAES;
861: break;
862: default:
863: IOLog("Audio: supported encoding: %d\n", encoding);
864: break;
865: }
866: }
867: - (u_int)samplingRate
868: {
869: return samplingRate;
870: }
871: - (void)setSamplingRate:(u_int)rate
872: {
873: samplingRate = rate;
874: }
875: - (u_int)channelCount
876: {
877: return channelCount;
878: }
879: - (void)setChannelCount:(u_int)count
880: {
881: channelCount = count;
882: }
883: - (u_int)lowWaterMark
884: {
885: return 0;
886: }
887: - (void)setLowWaterMark:(u_int)count
888: {
889: return;
890: }
891: - (u_int)highWaterMark
892: {
893: return 0;
894: }
895: - (void)setHighWaterMark:(u_int)count
896: {
897: return;
898: }
899:
900: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.