📄 aq.c
字号:
static void alloc_soft_queue(void){ int i; char *base; if(base_buckets) { free(base_buckets[0].data); free(base_buckets); base_buckets = NULL; } base_buckets = (AudioBucket *)safe_malloc(nbuckets * sizeof(AudioBucket)); base = (char *)safe_malloc(nbuckets * bucket_size); for(i = 0; i < nbuckets; i++) base_buckets[i].data = base + i * bucket_size; flush_buckets();}/* aq_fill_one() transfers one audio bucket to device. */static int aq_fill_one(void){ AudioBucket *tmp; if(head == NULL) return 0; if(aq_output_data(head->data, bucket_size) == -1) return -1; tmp = head; head = head->next; reuse_audio_bucket(tmp); return 0;}/* aq_fill_nonblocking() transfers some audio buckets to device. * This function is non-blocking. But it is possible to block because * of miss-estimated aq_fillable() calculation. */int aq_fill_nonblocking(void){ int32 i, nfills; AudioBucket *tmp; if(head == NULL || head->len != bucket_size || !IS_STREAM_TRACE) return 0; nfills = (aq_fillable() * Bps) / bucket_size; for(i = 0; i < nfills; i++) { if(head == NULL || head->len != bucket_size) break; if(aq_output_data(head->data, bucket_size) == -1) return RC_ERROR; tmp = head; head = head->next; reuse_audio_bucket(tmp); } return 0;}int32 aq_samples(void){ double realtime, es; int s; if(play_mode->acntl(PM_REQ_GETSAMPLES, &s) != -1) { /* Reset counter & timer */ if(play_counter) { play_start_time = get_current_calender_time(); play_offset_counter = s; play_counter = 0; } return s; } if(!IS_STREAM_TRACE) return -1; realtime = get_current_calender_time(); if(play_counter == 0) { play_start_time = realtime; return play_offset_counter; } es = play_mode->rate * (realtime - play_start_time); if(es >= play_counter) { /* Ouch! * Audio device queue may be empty! * Reset counters. */ play_offset_counter += play_counter; play_counter = 0; play_start_time = realtime; return play_offset_counter; } return (int32)es + play_offset_counter;}int32 aq_filled(void){ double realtime, es; int filled; if(!IS_STREAM_TRACE) return 0; if(play_mode->acntl(PM_REQ_GETFILLED, &filled) != -1) return filled; realtime = get_current_calender_time(); if(play_counter == 0) { play_start_time = realtime; return 0; } es = play_mode->rate * (realtime - play_start_time); if(es >= play_counter) { /* out of play counter */ play_offset_counter += play_counter; play_counter = 0; play_start_time = realtime; return 0; } return play_counter - (int32)es;}int32 aq_soft_filled(void){ int32 bytes; AudioBucket *cur; bytes = 0; for(cur = head; cur != NULL; cur = cur->next) bytes += cur->len; return bytes / Bps;}int32 aq_fillable(void){ int fillable; if(!IS_STREAM_TRACE) return 0; if(play_mode->acntl(PM_REQ_GETFILLABLE, &fillable) != -1) return fillable; return device_qsize / Bps - aq_filled();}double aq_filled_ratio(void){ double ratio; if(!IS_STREAM_TRACE) return 1.0; ratio = (double)aq_filled() * Bps / device_qsize; if(ratio > 1.0) return 1.0; /* for safety */ return ratio;}int aq_get_dev_queuesize(void){ if(!IS_STREAM_TRACE) return 0; return device_qsize / Bps;}int aq_soft_flush(void){ int rc; while(head) { if(head->len < bucket_size) { /* Add silence code */ memset (head->data + head->len, 0, bucket_size - head->len); head->len = bucket_size; } if(aq_fill_one() == -1) return RC_ERROR; trace_loop(); rc = check_apply_control(); if(RC_IS_SKIP_FILE(rc)) { play_mode->acntl(PM_REQ_DISCARD, NULL); flush_buckets(); return rc; } } play_mode->acntl(PM_REQ_OUTPUT_FINISH, NULL); return RC_NONE;}int aq_flush(int discard){ int rc; int more_trace; /* to avoid infinite loop */ double t, timeout_expect; aq_add_count = 0; init_effect(); if(discard) { trace_flush(); if(play_mode->acntl(PM_REQ_DISCARD, NULL) != -1) { flush_buckets(); return RC_NONE; } ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "ERROR: Can't discard audio buffer"); } if(!IS_STREAM_TRACE) { play_mode->acntl(PM_REQ_FLUSH, NULL); play_counter = play_offset_counter = 0; return RC_NONE; } rc = aq_soft_flush(); if(RC_IS_SKIP_FILE(rc)) return rc; more_trace = 1; t = get_current_calender_time(); timeout_expect = t + (double)aq_filled() / play_mode->rate; while(more_trace || aq_filled() > 0) { rc = check_apply_control(); if(RC_IS_SKIP_FILE(rc)) { play_mode->acntl(PM_REQ_DISCARD, NULL); flush_buckets(); return rc; } more_trace = trace_loop(); t = get_current_calender_time(); if(t >= timeout_expect - 0.1) break; if(!more_trace) usleep((unsigned long)((timeout_expect - t) * 1000000)); else aq_wait_ticks(); } trace_flush(); play_mode->acntl(PM_REQ_FLUSH, NULL); flush_buckets(); return RC_NONE;}/* Wait a moment */static void aq_wait_ticks(void){ int32 trace_wait, wait_samples; if(device_qsize == 0 || (trace_wait = trace_wait_samples()) == 0) return; /* No wait */ wait_samples = (device_qsize / Bps) / 5; /* 20% */ if(trace_wait != -1 && /* There are more trace events */ trace_wait < wait_samples) wait_samples = trace_wait; usleep((unsigned int)((double)wait_samples / play_mode->rate * 1000000.0));}/* add_play_bucket() attempts to add buf to audio bucket. * It returns actually added bytes. */static int add_play_bucket(const char *buf, int n){ int total; if(n == 0) return 0; if(!nbuckets) { play_mode->output_data((char *)buf, n); return n; } if(head == NULL) head = tail = next_allocated_bucket(); total = 0; while(n > 0) { int i; if(tail->len == bucket_size) { AudioBucket *b; if((b = next_allocated_bucket()) == NULL) break; if(head == NULL) head = tail = b; else tail = tail->next = b; } i = bucket_size - tail->len; if(i > n) i = n; memcpy(tail->data + tail->len, buf + total, i); total += i; n -= i; tail->len += i; } return total;}/* Flush and clear audio bucket */static void flush_buckets(void){ int i; allocated_bucket_list = NULL; for(i = 0; i < nbuckets; i++) reuse_audio_bucket(&base_buckets[i]); head = tail = NULL; aq_fill_buffer_flag = (aq_start_count > 0); play_counter = play_offset_counter = 0;}/* next_allocated_bucket() gets free bucket. If all buckets is used, it * returns NULL. */static AudioBucket *next_allocated_bucket(void){ AudioBucket *b; if(allocated_bucket_list == NULL) return NULL; b = allocated_bucket_list; allocated_bucket_list = allocated_bucket_list->next; b->len = 0; b->next = NULL; return b;}/* Reuse specified bucket */static void reuse_audio_bucket(AudioBucket *bucket){ bucket->next = allocated_bucket_list; allocated_bucket_list = bucket;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -