diff options
Diffstat (limited to 'audio/legacy/audio_hw.c')
-rw-r--r-- | audio/legacy/audio_hw.c | 531 |
1 files changed, 507 insertions, 24 deletions
diff --git a/audio/legacy/audio_hw.c b/audio/legacy/audio_hw.c index d25fedf..e54ee45 100644 --- a/audio/legacy/audio_hw.c +++ b/audio/legacy/audio_hw.c | |||
@@ -44,6 +44,12 @@ | |||
44 | /* yet another definition of ARRAY_SIZE macro) */ | 44 | /* yet another definition of ARRAY_SIZE macro) */ |
45 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) | 45 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) |
46 | 46 | ||
47 | /* | ||
48 | * additional space in resampler buffer allowing for extra samples to be returned | ||
49 | * by speex resampler when sample rates ratio is not an integer | ||
50 | */ | ||
51 | #define RESAMPLER_HEADROOM_FRAMES 10 | ||
52 | |||
47 | /* buffer_remix: functor for doing in-place buffer manipulations. | 53 | /* buffer_remix: functor for doing in-place buffer manipulations. |
48 | * | 54 | * |
49 | * NB. When remix_func is called, the memory at `buf` must be at least | 55 | * NB. When remix_func is called, the memory at `buf` must be at least |
@@ -56,10 +62,34 @@ struct buffer_remix { | |||
56 | size_t out_chans; /* number of output channels */ | 62 | size_t out_chans; /* number of output channels */ |
57 | }; | 63 | }; |
58 | 64 | ||
65 | struct j6_voice_stream { | ||
66 | struct j6_audio_device *dev; | ||
67 | struct pcm *pcm_in; | ||
68 | struct pcm *pcm_out; | ||
69 | struct pcm_config in_config; | ||
70 | struct pcm_config out_config; | ||
71 | struct resampler_itfe *resampler; | ||
72 | struct resampler_buffer_provider buf_provider; | ||
73 | struct buffer_remix *remix; | ||
74 | pthread_t thread; | ||
75 | int16_t *in_buffer; | ||
76 | int16_t *out_buffer; | ||
77 | size_t in_frames; | ||
78 | size_t out_frames; | ||
79 | size_t frame_size; | ||
80 | char *name; | ||
81 | }; | ||
82 | |||
83 | struct j6_voice { | ||
84 | struct j6_voice_stream ul; | ||
85 | struct j6_voice_stream dl; | ||
86 | }; | ||
87 | |||
59 | struct j6_audio_device { | 88 | struct j6_audio_device { |
60 | struct audio_hw_device device; | 89 | struct audio_hw_device device; |
61 | struct j6_stream_in *in; | 90 | struct j6_stream_in *in; |
62 | struct j6_stream_out *out; | 91 | struct j6_stream_out *out; |
92 | struct j6_voice voice; | ||
63 | struct audio_route *route; | 93 | struct audio_route *route; |
64 | audio_devices_t in_device; | 94 | audio_devices_t in_device; |
65 | audio_devices_t out_device; | 95 | audio_devices_t out_device; |
@@ -67,7 +97,10 @@ struct j6_audio_device { | |||
67 | unsigned int card; | 97 | unsigned int card; |
68 | unsigned int in_port; | 98 | unsigned int in_port; |
69 | unsigned int out_port; | 99 | unsigned int out_port; |
100 | unsigned int bt_port; | ||
70 | bool mic_mute; | 101 | bool mic_mute; |
102 | bool in_call; | ||
103 | audio_mode_t mode; | ||
71 | }; | 104 | }; |
72 | 105 | ||
73 | struct j6_stream_in { | 106 | struct j6_stream_in { |
@@ -93,6 +126,7 @@ struct j6_stream_out { | |||
93 | struct j6_audio_device *dev; | 126 | struct j6_audio_device *dev; |
94 | struct pcm_config config; | 127 | struct pcm_config config; |
95 | struct pcm *pcm; | 128 | struct pcm *pcm; |
129 | struct timespec last; | ||
96 | pthread_mutex_t lock; | 130 | pthread_mutex_t lock; |
97 | bool standby; | 131 | bool standby; |
98 | int64_t written; /* total frames written, not cleared when entering standby */ | 132 | int64_t written; /* total frames written, not cleared when entering standby */ |
@@ -113,8 +147,8 @@ static const char *supported_cards[] = { | |||
113 | AUDIO_DEVICE_OUT_DEFAULT) | 147 | AUDIO_DEVICE_OUT_DEFAULT) |
114 | 148 | ||
115 | #define CAPTURE_SAMPLE_RATE 44100 | 149 | #define CAPTURE_SAMPLE_RATE 44100 |
116 | #define CAPTURE_PERIOD_SIZE 320 | 150 | #define CAPTURE_PERIOD_SIZE 960 |
117 | #define CAPTURE_PERIOD_COUNT 2 | 151 | #define CAPTURE_PERIOD_COUNT 4 |
118 | #define CAPTURE_BUFFER_SIZE (CAPTURE_PERIOD_SIZE * CAPTURE_PERIOD_COUNT) | 152 | #define CAPTURE_BUFFER_SIZE (CAPTURE_PERIOD_SIZE * CAPTURE_PERIOD_COUNT) |
119 | 153 | ||
120 | #define PLAYBACK_SAMPLE_RATE 44100 | 154 | #define PLAYBACK_SAMPLE_RATE 44100 |
@@ -122,6 +156,11 @@ static const char *supported_cards[] = { | |||
122 | #define PLAYBACK_PERIOD_COUNT 4 | 156 | #define PLAYBACK_PERIOD_COUNT 4 |
123 | #define PLAYBACK_BUFFER_SIZE (PLAYBACK_PERIOD_SIZE * PLAYBACK_PERIOD_COUNT) | 157 | #define PLAYBACK_BUFFER_SIZE (PLAYBACK_PERIOD_SIZE * PLAYBACK_PERIOD_COUNT) |
124 | 158 | ||
159 | #define BT_SAMPLE_RATE 8000 | ||
160 | #define BT_PERIOD_SIZE 160 | ||
161 | #define BT_PERIOD_COUNT 4 | ||
162 | #define BT_BUFFER_SIZE (BT_PERIOD_SIZE * BT_PERIOD_COUNT) | ||
163 | |||
125 | struct pcm_config pcm_config_capture = { | 164 | struct pcm_config pcm_config_capture = { |
126 | .channels = 2, | 165 | .channels = 2, |
127 | .rate = CAPTURE_SAMPLE_RATE, | 166 | .rate = CAPTURE_SAMPLE_RATE, |
@@ -143,6 +182,27 @@ struct pcm_config pcm_config_playback = { | |||
143 | .avail_min = PLAYBACK_PERIOD_SIZE, | 182 | .avail_min = PLAYBACK_PERIOD_SIZE, |
144 | }; | 183 | }; |
145 | 184 | ||
185 | struct pcm_config pcm_config_bt_in = { | ||
186 | .channels = 2, | ||
187 | .rate = BT_SAMPLE_RATE, | ||
188 | .format = PCM_FORMAT_S16_LE, | ||
189 | .period_size = BT_PERIOD_SIZE, | ||
190 | .period_count = BT_PERIOD_COUNT, | ||
191 | .start_threshold = 1, | ||
192 | .stop_threshold = BT_BUFFER_SIZE, | ||
193 | }; | ||
194 | |||
195 | struct pcm_config pcm_config_bt_out = { | ||
196 | .channels = 2, | ||
197 | .rate = BT_SAMPLE_RATE, | ||
198 | .format = PCM_FORMAT_S16_LE, | ||
199 | .period_size = BT_PERIOD_SIZE, | ||
200 | .period_count = BT_PERIOD_COUNT, | ||
201 | .start_threshold = BT_BUFFER_SIZE / 2, | ||
202 | .stop_threshold = BT_BUFFER_SIZE, | ||
203 | .avail_min = BT_PERIOD_SIZE, | ||
204 | }; | ||
205 | |||
146 | static int find_supported_card(void) | 206 | static int find_supported_card(void) |
147 | { | 207 | { |
148 | char name[256] = ""; | 208 | char name[256] = ""; |
@@ -174,6 +234,8 @@ static int find_supported_card(void) | |||
174 | return card; | 234 | return card; |
175 | } | 235 | } |
176 | 236 | ||
237 | static void do_out_standby(struct j6_stream_out *out); | ||
238 | |||
177 | /* must be called with device lock held */ | 239 | /* must be called with device lock held */ |
178 | static void select_input_device(struct j6_audio_device *adev) | 240 | static void select_input_device(struct j6_audio_device *adev) |
179 | { | 241 | { |
@@ -259,6 +321,362 @@ static int setup_stereo_to_mono_input_remix(struct j6_stream_in *in) | |||
259 | return 0; | 321 | return 0; |
260 | } | 322 | } |
261 | 323 | ||
324 | /* | ||
325 | * Implementation of buffer_remix::remix_func that duplicates the first | ||
326 | * channel into the rest of channels in the frame without doing any other | ||
327 | * processing. It assumes data in 16-bits, but it's not explicitly checked | ||
328 | */ | ||
329 | static void mono_remix(struct buffer_remix *data, void *buf, size_t frames) | ||
330 | { | ||
331 | int16_t *buffer = (int16_t*)buf; | ||
332 | size_t i; | ||
333 | |||
334 | ALOGVV("mono_remix() remix=%p buf=%p frames=%u", data, buf, frames); | ||
335 | |||
336 | if (frames == 0) | ||
337 | return; | ||
338 | |||
339 | /* duplicate first channel into the rest of channels in the frame */ | ||
340 | while (frames--) { | ||
341 | for (i = 1; i < data->out_chans; i++) | ||
342 | buffer[i] = buffer[0]; | ||
343 | buffer += data->out_chans; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | static int setup_mono_input_remix(struct j6_voice_stream *stream) | ||
348 | { | ||
349 | ALOGV("setup_mono_input_remix() %s stream", stream->name); | ||
350 | |||
351 | struct buffer_remix *br = (struct buffer_remix *)malloc(sizeof(struct buffer_remix)); | ||
352 | if (!br) | ||
353 | return -ENOMEM; | ||
354 | |||
355 | br->remix_func = mono_remix; | ||
356 | br->sample_size = sizeof(int16_t); | ||
357 | br->in_chans = stream->in_config.channels; | ||
358 | br->out_chans = stream->out_config.channels; | ||
359 | stream->remix = br; | ||
360 | |||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | static int voice_get_next_buffer(struct resampler_buffer_provider *buffer_provider, | ||
365 | struct resampler_buffer* buffer) | ||
366 | { | ||
367 | struct j6_voice_stream *stream; | ||
368 | int ret; | ||
369 | |||
370 | if (buffer_provider == NULL || buffer == NULL) { | ||
371 | ALOGE("voice_get_next_buffer() invalid buffer/provider"); | ||
372 | return -EINVAL; | ||
373 | } | ||
374 | |||
375 | stream = (struct j6_voice_stream *)((char *)buffer_provider - | ||
376 | offsetof(struct j6_voice_stream, buf_provider)); | ||
377 | |||
378 | if (stream->pcm_in == NULL) { | ||
379 | buffer->raw = NULL; | ||
380 | buffer->frame_count = 0; | ||
381 | return -ENODEV; | ||
382 | } | ||
383 | |||
384 | if (buffer->frame_count > stream->in_frames) { | ||
385 | ALOGW("voice_get_next_buffer() %s unexpected frame count %u, " | ||
386 | "buffer was allocated for %u frames", | ||
387 | stream->name, buffer->frame_count, stream->in_frames); | ||
388 | buffer->frame_count = stream->in_frames; | ||
389 | } | ||
390 | |||
391 | ret = pcm_read(stream->pcm_in, stream->in_buffer, | ||
392 | buffer->frame_count * stream->frame_size); | ||
393 | if (ret) { | ||
394 | ALOGE("voice_get_next_buffer() failed to read %s: %s", | ||
395 | stream->name, pcm_get_error(stream->pcm_in)); | ||
396 | buffer->raw = NULL; | ||
397 | buffer->frame_count = 0; | ||
398 | return ret; | ||
399 | } | ||
400 | |||
401 | buffer->i16 = stream->in_buffer; | ||
402 | |||
403 | return ret; | ||
404 | } | ||
405 | |||
406 | static void voice_release_buffer(struct resampler_buffer_provider *buffer_provider, | ||
407 | struct resampler_buffer* buffer) | ||
408 | { | ||
409 | } | ||
410 | |||
411 | static void *voice_thread_func(void *arg) | ||
412 | { | ||
413 | struct j6_voice_stream *stream = (struct j6_voice_stream *)arg; | ||
414 | struct j6_audio_device *adev = stream->dev; | ||
415 | struct timespec now; | ||
416 | size_t frames = stream->out_frames; | ||
417 | uint32_t periods = 0; | ||
418 | uint32_t avail; | ||
419 | bool in_steady = false; | ||
420 | bool out_steady = false; | ||
421 | int ret = 0; | ||
422 | |||
423 | pcm_start(stream->pcm_in); | ||
424 | |||
425 | memset(stream->out_buffer, 0, stream->out_frames * stream->frame_size); | ||
426 | |||
427 | while (adev->in_call) { | ||
428 | if (out_steady) { | ||
429 | if (in_steady) { | ||
430 | stream->resampler->resample_from_provider(stream->resampler, | ||
431 | stream->out_buffer, | ||
432 | &frames); | ||
433 | } else { | ||
434 | ret = pcm_get_htimestamp(stream->pcm_in, &avail, &now); | ||
435 | if (!ret && (avail > 0)) { | ||
436 | in_steady = true; | ||
437 | continue; | ||
438 | } | ||
439 | } | ||
440 | } else if (++periods == stream->out_config.period_count) { | ||
441 | out_steady = true; | ||
442 | } | ||
443 | |||
444 | if (stream->remix) | ||
445 | stream->remix->remix_func(stream->remix, stream->out_buffer, frames); | ||
446 | |||
447 | ret = pcm_write(stream->pcm_out, stream->out_buffer, | ||
448 | frames * stream->frame_size); | ||
449 | if (ret) { | ||
450 | ALOGE("voice_thread_func() failed to write %s: %s", | ||
451 | stream->name, pcm_get_error(stream->pcm_out)); | ||
452 | usleep((frames * 1000000) / stream->out_config.rate); | ||
453 | } | ||
454 | } | ||
455 | |||
456 | return (void*)ret; | ||
457 | } | ||
458 | |||
459 | static void voice_stream_exit(struct j6_voice_stream *stream) | ||
460 | { | ||
461 | if (stream->resampler) { | ||
462 | release_resampler(stream->resampler); | ||
463 | stream->resampler = NULL; | ||
464 | } | ||
465 | |||
466 | if (stream->pcm_out) { | ||
467 | pcm_close(stream->pcm_out); | ||
468 | stream->pcm_out = NULL; | ||
469 | } | ||
470 | |||
471 | if (stream->pcm_in) { | ||
472 | pcm_close(stream->pcm_in); | ||
473 | stream->pcm_in = NULL; | ||
474 | } | ||
475 | |||
476 | if (stream->in_buffer) { | ||
477 | free(stream->in_buffer); | ||
478 | stream->in_buffer = NULL; | ||
479 | stream->in_frames = 0; | ||
480 | } | ||
481 | |||
482 | if (stream->out_buffer) { | ||
483 | free(stream->out_buffer); | ||
484 | stream->out_buffer = NULL; | ||
485 | stream->out_frames = 0; | ||
486 | } | ||
487 | |||
488 | if (stream->remix) { | ||
489 | free(stream->remix); | ||
490 | stream->remix = NULL; | ||
491 | } | ||
492 | |||
493 | if (stream->name) { | ||
494 | free(stream->name); | ||
495 | stream->name = NULL; | ||
496 | } | ||
497 | } | ||
498 | |||
499 | static int voice_stream_init(struct j6_voice_stream *stream, | ||
500 | unsigned int in_port, | ||
501 | unsigned int out_port, | ||
502 | bool needs_mono_remix) | ||
503 | { | ||
504 | struct j6_audio_device *adev = stream->dev; | ||
505 | int ret; | ||
506 | |||
507 | stream->buf_provider.get_next_buffer = voice_get_next_buffer; | ||
508 | stream->buf_provider.release_buffer = voice_release_buffer; | ||
509 | ret = create_resampler(stream->in_config.rate, | ||
510 | stream->out_config.rate, | ||
511 | 2, | ||
512 | RESAMPLER_QUALITY_DEFAULT, | ||
513 | &stream->buf_provider, | ||
514 | &stream->resampler); | ||
515 | if (ret) { | ||
516 | ALOGE("voice_stream_init() failed to create %s resampler %d", stream->name, ret); | ||
517 | return ret; | ||
518 | } | ||
519 | |||
520 | stream->pcm_in = pcm_open(adev->card, in_port, PCM_IN, &stream->in_config); | ||
521 | stream->pcm_out = pcm_open(adev->card, out_port, PCM_OUT, &stream->out_config); | ||
522 | |||
523 | if (!pcm_is_ready(stream->pcm_in) || !pcm_is_ready(stream->pcm_out)) { | ||
524 | ALOGE("voice_stream_init() failed to open pcm %s devices", stream->name); | ||
525 | voice_stream_exit(stream); | ||
526 | return -ENODEV; | ||
527 | } | ||
528 | |||
529 | stream->frame_size = pcm_frames_to_bytes(stream->pcm_in, 1); | ||
530 | |||
531 | /* out_buffer will store the resampled data */ | ||
532 | stream->out_frames = stream->out_config.period_size; | ||
533 | stream->out_buffer = malloc(stream->out_frames * stream->frame_size); | ||
534 | |||
535 | /* in_buffer will store the frames recorded from the PCM device */ | ||
536 | stream->in_frames = (stream->out_frames * stream->in_config.rate) / stream->out_config.rate + | ||
537 | RESAMPLER_HEADROOM_FRAMES; | ||
538 | stream->in_buffer = malloc(stream->in_frames * stream->frame_size); | ||
539 | |||
540 | if (!stream->in_buffer || !stream->out_buffer) { | ||
541 | ALOGE("voice_stream_init() failed to allocate %s buffers", stream->name); | ||
542 | voice_stream_exit(stream); | ||
543 | return -ENOMEM; | ||
544 | } | ||
545 | |||
546 | if (needs_mono_remix) { | ||
547 | ret = setup_mono_input_remix(stream); | ||
548 | if (ret) { | ||
549 | ALOGE("voice_stream_init() failed to setup mono remix %d", ret); | ||
550 | voice_stream_exit(stream); | ||
551 | return ret; | ||
552 | } | ||
553 | } else { | ||
554 | stream->remix = NULL; | ||
555 | } | ||
556 | |||
557 | return 0; | ||
558 | } | ||
559 | |||
560 | static int enter_voice_call(struct j6_audio_device *adev) | ||
561 | { | ||
562 | struct j6_voice *voice = &adev->voice; | ||
563 | int ret; | ||
564 | |||
565 | ALOGI("enter_voice_call() entering bluetooth voice call"); | ||
566 | |||
567 | audio_route_apply_path(adev->route, "BT SCO Master"); | ||
568 | audio_route_update_mixer(adev->route); | ||
569 | |||
570 | /* Let the primary output switch to a dummy sink */ | ||
571 | if (adev->out) | ||
572 | do_out_standby(adev->out); | ||
573 | |||
574 | /* Uplink: Mic (44.1kHz) -> BT (8kHz) */ | ||
575 | voice->ul.name = strdup("UL"); | ||
576 | voice->ul.in_config = pcm_config_capture; | ||
577 | voice->ul.out_config = pcm_config_bt_out; | ||
578 | voice->ul.dev = adev; | ||
579 | ret = voice_stream_init(&voice->ul, adev->in_port, adev->bt_port, false); | ||
580 | if (ret) { | ||
581 | ALOGE("enter_voice_call() failed to init uplink %d", ret); | ||
582 | goto err_ul_init; | ||
583 | } | ||
584 | |||
585 | /* Downlink: BT (8kHz) -> HP/Spk (44.1kHz) */ | ||
586 | voice->dl.name = strdup("DL"); | ||
587 | voice->dl.in_config = pcm_config_bt_in; | ||
588 | voice->dl.out_config = pcm_config_playback; | ||
589 | voice->dl.dev = adev; | ||
590 | ret = voice_stream_init(&voice->dl, adev->bt_port, adev->out_port, true); | ||
591 | if (ret) { | ||
592 | ALOGE("enter_voice_call() failed to init downlink %d", ret); | ||
593 | goto err_dl_init; | ||
594 | } | ||
595 | |||
596 | adev->in_call = true; | ||
597 | |||
598 | /* Create uplink thread: Mic -> BT */ | ||
599 | ret = pthread_create(&voice->ul.thread, NULL, voice_thread_func, &voice->ul); | ||
600 | if (ret) { | ||
601 | ALOGE("enter_voice_call() failed to create uplink thread %d", ret); | ||
602 | adev->in_call = false; | ||
603 | goto err_ul_thread; | ||
604 | } | ||
605 | |||
606 | /* Create downlink thread: BT -> HP/Spk */ | ||
607 | ret = pthread_create(&voice->dl.thread, NULL, voice_thread_func, &voice->dl); | ||
608 | if (ret) { | ||
609 | ALOGE("enter_voice_call() failed to create downlink thread %d", ret); | ||
610 | adev->in_call = false; | ||
611 | goto err_dl_thread; | ||
612 | } | ||
613 | |||
614 | return 0; | ||
615 | |||
616 | err_dl_thread: | ||
617 | pthread_join(voice->ul.thread, NULL); | ||
618 | err_ul_thread: | ||
619 | voice_stream_exit(&voice->ul); | ||
620 | err_dl_init: | ||
621 | voice_stream_exit(&voice->dl); | ||
622 | err_ul_init: | ||
623 | audio_route_reset_path(adev->route, "BT SCO Master"); | ||
624 | audio_route_update_mixer(adev->route); | ||
625 | |||
626 | return ret; | ||
627 | } | ||
628 | |||
629 | static void leave_voice_call(struct j6_audio_device *adev) | ||
630 | { | ||
631 | struct j6_voice *voice = &adev->voice; | ||
632 | struct j6_voice_stream *ul = &voice->ul; | ||
633 | struct j6_voice_stream *dl = &voice->dl; | ||
634 | void *ret; | ||
635 | |||
636 | ALOGI("leave_voice_call() leaving bluetooth voice call"); | ||
637 | |||
638 | adev->in_call = false; | ||
639 | |||
640 | /* | ||
641 | * The PCM ports used for Bluetooth are slaves and they can lose the | ||
642 | * BCLK and FSYNC while still active. That leads to blocking read() and | ||
643 | * write() calls, which is prevented by switching the clock source to | ||
644 | * an internal one and explicitly stopping both ports for the new source | ||
645 | * to take effect at kernel level | ||
646 | */ | ||
647 | audio_route_reset_path(adev->route, "BT SCO Master"); | ||
648 | audio_route_update_mixer(adev->route); | ||
649 | if (ul->pcm_out) | ||
650 | pcm_stop(ul->pcm_out); | ||
651 | if (dl->pcm_in) | ||
652 | pcm_stop(dl->pcm_in); | ||
653 | |||
654 | pthread_join(voice->dl.thread, &ret); | ||
655 | pthread_join(voice->ul.thread, &ret); | ||
656 | |||
657 | voice_stream_exit(&voice->dl); | ||
658 | voice_stream_exit(&voice->ul); | ||
659 | |||
660 | /* Let the primary output switch back to its ALSA PCM device */ | ||
661 | if (adev->out) | ||
662 | do_out_standby(adev->out); | ||
663 | } | ||
664 | |||
665 | static uint32_t time_diff(struct timespec t1, struct timespec t0) | ||
666 | { | ||
667 | struct timespec temp; | ||
668 | |||
669 | if ((t1.tv_nsec - t0.tv_nsec) < 0) { | ||
670 | temp.tv_sec = t1.tv_sec - t0.tv_sec-1; | ||
671 | temp.tv_nsec = 1000000000UL + t1.tv_nsec - t0.tv_nsec; | ||
672 | } else { | ||
673 | temp.tv_sec = t1.tv_sec - t0.tv_sec; | ||
674 | temp.tv_nsec = t1.tv_nsec - t0.tv_nsec; | ||
675 | } | ||
676 | |||
677 | return (temp.tv_sec * 1000000UL + temp.tv_nsec / 1000); | ||
678 | } | ||
679 | |||
262 | /* audio HAL functions */ | 680 | /* audio HAL functions */ |
263 | 681 | ||
264 | static uint32_t out_get_sample_rate(const struct audio_stream *stream) | 682 | static uint32_t out_get_sample_rate(const struct audio_stream *stream) |
@@ -324,9 +742,13 @@ static void do_out_standby(struct j6_stream_out *out) | |||
324 | struct j6_audio_device *adev = out->dev; | 742 | struct j6_audio_device *adev = out->dev; |
325 | 743 | ||
326 | if (!out->standby) { | 744 | if (!out->standby) { |
327 | ALOGI("do_out_standby() close card %u port %u", adev->card, adev->out_port); | 745 | if (adev->mode != AUDIO_MODE_IN_CALL) { |
328 | pcm_close(out->pcm); | 746 | ALOGI("do_out_standby() close card %u port %u", adev->card, adev->out_port); |
329 | out->pcm = NULL; | 747 | pcm_close(out->pcm); |
748 | out->pcm = NULL; | ||
749 | } else { | ||
750 | ALOGI("do_out_standby() close dummy card"); | ||
751 | } | ||
330 | out->standby = true; | 752 | out->standby = true; |
331 | } | 753 | } |
332 | } | 754 | } |
@@ -411,10 +833,13 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, | |||
411 | { | 833 | { |
412 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); | 834 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); |
413 | struct j6_audio_device *adev = out->dev; | 835 | struct j6_audio_device *adev = out->dev; |
836 | struct timespec now; | ||
414 | const size_t frame_size = audio_stream_frame_size(&stream->common); | 837 | const size_t frame_size = audio_stream_frame_size(&stream->common); |
415 | const size_t frames = bytes / frame_size; | 838 | const size_t frames = bytes / frame_size; |
416 | uint32_t rate = out->config.rate; | 839 | uint32_t rate = out->config.rate; |
417 | uint32_t write_usecs = frames * 1000000 / rate; | 840 | uint32_t write_usecs = frames * 1000000 / rate; |
841 | uint32_t diff_usecs; | ||
842 | int ret = 0; | ||
418 | 843 | ||
419 | ALOGVV("out_write() stream=%p buffer=%p size=%u/%u time=%u usecs", | 844 | ALOGVV("out_write() stream=%p buffer=%p size=%u/%u time=%u usecs", |
420 | out, buffer, frames, rate, write_usecs); | 845 | out, buffer, frames, rate, write_usecs); |
@@ -423,18 +848,27 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, | |||
423 | pthread_mutex_lock(&out->lock); | 848 | pthread_mutex_lock(&out->lock); |
424 | 849 | ||
425 | if (out->standby) { | 850 | if (out->standby) { |
426 | select_output_device(adev); | 851 | if (!adev->in_call) { |
852 | select_output_device(adev); | ||
427 | 853 | ||
428 | ALOGI("out_write() open card %u port %u", adev->card, adev->out_port); | 854 | ALOGI("out_write() open card %u port %u", adev->card, adev->out_port); |
429 | out->pcm = pcm_open(adev->card, adev->out_port, PCM_OUT | PCM_MMAP, &out->config); | 855 | out->pcm = pcm_open(adev->card, adev->out_port, PCM_OUT, &out->config); |
430 | if (!pcm_is_ready(out->pcm)) { | 856 | if (!pcm_is_ready(out->pcm)) { |
431 | ALOGE("out_write() failed to open pcm out: %s", pcm_get_error(out->pcm)); | 857 | ALOGE("out_write() failed to open pcm out: %s", pcm_get_error(out->pcm)); |
432 | pcm_close(out->pcm); | 858 | pcm_close(out->pcm); |
433 | out->pcm = NULL; | 859 | out->pcm = NULL; |
860 | ret = -ENODEV; | ||
861 | } | ||
862 | } else { | ||
863 | ALOGI("out_write() open dummy port"); | ||
864 | clock_gettime(CLOCK_REALTIME, &out->last); | ||
865 | } | ||
866 | |||
867 | if (ret) { | ||
434 | usleep(write_usecs); /* limits the rate of error messages */ | 868 | usleep(write_usecs); /* limits the rate of error messages */ |
435 | pthread_mutex_unlock(&out->lock); | 869 | pthread_mutex_unlock(&out->lock); |
436 | pthread_mutex_unlock(&adev->lock); | 870 | pthread_mutex_unlock(&adev->lock); |
437 | return -ENODEV; | 871 | return ret; |
438 | } | 872 | } |
439 | 873 | ||
440 | out->standby = false; | 874 | out->standby = false; |
@@ -442,10 +876,19 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, | |||
442 | 876 | ||
443 | pthread_mutex_unlock(&adev->lock); | 877 | pthread_mutex_unlock(&adev->lock); |
444 | 878 | ||
445 | int ret = pcm_mmap_write(out->pcm, buffer, bytes); | 879 | if (!adev->in_call) { |
446 | if (ret) { | 880 | ret = pcm_write(out->pcm, buffer, bytes); |
447 | ALOGE("out_write() failed to write audio data %d", ret); | 881 | if (ret) { |
448 | usleep(write_usecs); /* limits the rate of error messages */ | 882 | ALOGE("out_write() failed to write audio data %d", ret); |
883 | usleep(write_usecs); /* limits the rate of error messages */ | ||
884 | } | ||
885 | } else { | ||
886 | clock_gettime(CLOCK_REALTIME, &now); | ||
887 | diff_usecs = time_diff(now, out->last); | ||
888 | if (write_usecs > diff_usecs) | ||
889 | usleep(write_usecs - diff_usecs); | ||
890 | |||
891 | clock_gettime(CLOCK_REALTIME, &out->last); | ||
449 | } | 892 | } |
450 | 893 | ||
451 | out->written += frames; | 894 | out->written += frames; |
@@ -481,18 +924,27 @@ static int out_get_presentation_position(const struct audio_stream_out *stream, | |||
481 | uint64_t *frames, struct timespec *timestamp) | 924 | uint64_t *frames, struct timespec *timestamp) |
482 | { | 925 | { |
483 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); | 926 | struct j6_stream_out *out = (struct j6_stream_out *)(stream); |
927 | struct j6_audio_device *adev = out->dev; | ||
928 | int64_t signed_frames = -1; | ||
484 | size_t avail; | 929 | size_t avail; |
485 | int ret = -1; | 930 | int ret = -1; |
486 | 931 | ||
487 | pthread_mutex_lock(&out->lock); | 932 | pthread_mutex_lock(&out->lock); |
488 | 933 | ||
489 | if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) { | 934 | if (!adev->in_call) { |
490 | int64_t signed_frames = out->written - pcm_get_buffer_size(out->pcm) + avail; | 935 | if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) { |
491 | /* It would be unusual for this value to be negative, but check just in case ... */ | 936 | signed_frames = out->written - pcm_get_buffer_size(out->pcm) + avail; |
492 | if (signed_frames >= 0) { | ||
493 | *frames = signed_frames; | ||
494 | ret = 0; | ||
495 | } | 937 | } |
938 | } else { | ||
939 | clock_gettime(CLOCK_REALTIME, timestamp); | ||
940 | signed_frames = out->written + | ||
941 | (time_diff(*timestamp, out->last) * out->config.rate) / 1000000; | ||
942 | } | ||
943 | |||
944 | /* It would be unusual for this value to be negative, but check just in case ... */ | ||
945 | if (signed_frames >= 0) { | ||
946 | *frames = signed_frames; | ||
947 | ret = 0; | ||
496 | } | 948 | } |
497 | 949 | ||
498 | pthread_mutex_unlock(&out->lock); | 950 | pthread_mutex_unlock(&out->lock); |
@@ -921,9 +1373,37 @@ static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted) | |||
921 | 1373 | ||
922 | static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) | 1374 | static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) |
923 | { | 1375 | { |
1376 | struct j6_audio_device *adev = (struct j6_audio_device *)dev; | ||
1377 | struct j6_stream_out *out = adev->out; | ||
1378 | int ret = 0; | ||
1379 | |||
924 | ALOGV("adev_set_mode() mode=0x%08x", mode); | 1380 | ALOGV("adev_set_mode() mode=0x%08x", mode); |
925 | 1381 | ||
926 | return 0; | 1382 | pthread_mutex_lock(&adev->lock); |
1383 | pthread_mutex_lock(&out->lock); | ||
1384 | |||
1385 | if (adev->mode == mode) { | ||
1386 | ALOGV("adev_set_mode() already in mode=0x%08x", mode); | ||
1387 | goto out; | ||
1388 | } | ||
1389 | |||
1390 | if (mode == AUDIO_MODE_IN_CALL) { | ||
1391 | ret = enter_voice_call(adev); | ||
1392 | if (ret) { | ||
1393 | ALOGE("adev_set_mode() failed to initialize voice call %d", ret); | ||
1394 | goto out; | ||
1395 | } | ||
1396 | } else { | ||
1397 | leave_voice_call(adev); | ||
1398 | } | ||
1399 | |||
1400 | adev->mode = mode; | ||
1401 | |||
1402 | out: | ||
1403 | pthread_mutex_unlock(&out->lock); | ||
1404 | pthread_mutex_unlock(&adev->lock); | ||
1405 | |||
1406 | return ret; | ||
927 | } | 1407 | } |
928 | 1408 | ||
929 | static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) | 1409 | static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) |
@@ -1156,7 +1636,10 @@ static int adev_open(const hw_module_t* module, const char* name, | |||
1156 | adev->card = find_supported_card(); | 1636 | adev->card = find_supported_card(); |
1157 | adev->in_port = 0; | 1637 | adev->in_port = 0; |
1158 | adev->out_port = 0; | 1638 | adev->out_port = 0; |
1639 | adev->bt_port = 2; | ||
1159 | adev->mic_mute = false; | 1640 | adev->mic_mute = false; |
1641 | adev->in_call = false; | ||
1642 | adev->mode = AUDIO_MODE_NORMAL; | ||
1160 | 1643 | ||
1161 | adev->route = audio_route_init(adev->card, NULL); | 1644 | adev->route = audio_route_init(adev->card, NULL); |
1162 | if (!adev->route) { | 1645 | if (!adev->route) { |