/* Write host data to host file */ static int write_hosts(struct daemon_data *d) { struct host *h; int r = 0; assert(d); rs_log_info("writing zeroconf data.\n"); if (generic_lock(d->fd, 1, 1, 1) < 0) { rs_log_crit("lock failed: %s\n", strerror(errno)); return -1; } if (lseek(d->fd, 0, SEEK_SET) < 0) { rs_log_crit("lseek() failed: %s\n", strerror(errno)); return -1; } if (ftruncate(d->fd, 0) < 0) { rs_log_crit("ftruncate() failed: %s\n", strerror(errno)); return -1; } remove_duplicate_services(d); for (h = d->hosts; h; h = h->next) { char t[256], a[AVAHI_ADDRESS_STR_MAX]; if (h->resolver) /* Not yet fully resolved */ continue; if (h->address.proto == AVAHI_PROTO_INET6) snprintf(t, sizeof(t), "[%s]:%u/%i\n", avahi_address_snprint(a, sizeof(a), &h->address), h->port, d->n_slots * h->n_cpus); else snprintf(t, sizeof(t), "%s:%u/%i\n", avahi_address_snprint(a, sizeof(a), &h->address), h->port, d->n_slots * h->n_cpus); if (dcc_writex(d->fd, t, strlen(t)) != 0) { rs_log_crit("write() failed: %s\n", strerror(errno)); goto finish; } } r = 0; finish: generic_lock(d->fd, 1, 0, 1); return r; };
/** * 装载TTA音乐文件 * * @param spath 短路径名 * @param lpath 长路径名 * * @return 成功时返回0 */ static int tta_load(const char *spath, const char *lpath) { __init(); if (tta_read_tag(spath) != 0) { __end(); return -1; } if (g_buff != NULL) { free(g_buff); g_buff = NULL; } g_buff = calloc(TTA_BUFFER_SIZE, sizeof(*g_buff)); if (g_buff == NULL) { __end(); return -1; } if (open_tta_file(spath, &ttainfo, 0, g_io_buffer_size) < 0) { dbg_printf(d, "TTA Decoder Error - %s", get_error_str(ttainfo.STATE)); close_tta_file(&ttainfo); return -1; } if (player_init(&ttainfo) != 0) { __end(); return -1; } if (ttainfo.BPS == 0) { __end(); return -1; } g_info.samples = ttainfo.DATALENGTH; g_info.duration = (double) ttainfo.LENGTH; g_info.sample_freq = ttainfo.SAMPLERATE; g_info.channels = ttainfo.NCH; g_info.filesize = ttainfo.FILESIZE; if (xAudioInit() < 0) { __end(); return -1; } if (xAudioSetFrequency(ttainfo.SAMPLERATE) < 0) { __end(); return -1; } xAudioSetChannelCallback(0, tta_audiocallback, NULL); generic_lock(); g_status = ST_LOADED; generic_unlock(); return 0; }
/** * 处理快进快退 * * @return * - -1 should exit * - 0 OK */ static int handle_seek(void) { if (g_status == ST_FFORWARD) { generic_lock(); g_status = ST_PLAYING; generic_unlock(); generic_set_playback(true); mp3_seek_seconds(g_play_time + g_seek_seconds); } else if (g_status == ST_FBACKWARD) { generic_lock(); g_status = ST_PLAYING; generic_unlock(); generic_set_playback(true); mp3_seek_seconds(g_play_time - g_seek_seconds); } return 0; }
int generic_pause(void) { generic_lock(); generic_set_playback(false); g_status = ST_PAUSED; generic_unlock(); return 0; }
int generic_play(void) { generic_lock(); generic_set_playback(true); g_status = ST_PLAYING; generic_unlock(); return 0; }
/** * 停止MP3音乐文件的播放,销毁资源等 * * @note 可以在播放线程中调用 * * @return 成功时返回0 */ static int __end(void) { xAudioEndPre(); g_play_time = 0.; generic_lock(); g_status = ST_STOPPED; generic_unlock(); return 0; }
int generic_resume(const char *spath, const char *lpath) { generic_lock(); g_status = g_suspend_status; if (g_status == ST_PLAYING) generic_set_playback(true); generic_unlock(); g_suspend_status = ST_LOADED; return 0; }
/** * 快退音乐文件 * * @param sec 秒数 * * @return 成功时返回0 */ int generic_fbackward(int sec) { generic_lock(); if (g_status == ST_PLAYING || g_status == ST_PAUSED || g_status == ST_FFORWARD) g_status = ST_FBACKWARD; g_seek_seconds = sec; xrRtcGetCurrentTick((u64 *) & g_last_seek_tick); g_last_seek_is_forward = false; g_seek_count++; generic_unlock(); return 0; }
/** * 初始化驱动变量资源等 * * @return 成功时返回0 */ static int __init(void) { generic_init(); generic_lock(); g_status = ST_UNKNOWN; generic_unlock(); g_buff_frame_size = g_buff_frame_start = 0; g_seek_seconds = 0; g_play_time = 0.; g_samples_decoded = g_tta_data_offset = 0; memset(&g_info, 0, sizeof(g_info)); return 0; }
/** * 初始化驱动变量资源等 * * @return 成功时返回0 */ static int __init(void) { generic_init(); generic_lock(); g_status = ST_UNKNOWN; generic_unlock(); memset(&g_inst_br, 0, sizeof(g_inst_br)); memset(g_input_buff, 0, sizeof(g_input_buff)); g_buff_frame_size = g_buff_frame_start = 0; g_seek_seconds = 0; g_play_time = 0.; memset(&mp3info, 0, sizeof(mp3info)); memset(&g_info, 0, sizeof(g_info)); return 0; }
static int mp3_load(const char *spath, const char *lpath) { int ret; __init(); dbg_printf(d, "%s: loading %s", __func__, spath); g_status = ST_UNKNOWN; mp3_data.use_buffer = true; mp3_data.fd = xrIoOpen(spath, PSP_O_RDONLY, 0777); if (mp3_data.fd < 0) return -1; g_info.filesize = xrIoLseek(mp3_data.fd, 0, PSP_SEEK_END); xrIoLseek(mp3_data.fd, 0, PSP_SEEK_SET); mp3_data.size = g_info.filesize; if (g_info.filesize < 0) return g_info.filesize; xrIoLseek(mp3_data.fd, 0, PSP_SEEK_SET); mad_stream_init(&stream); mad_frame_init(&frame); mad_synth_init(&synth); if (use_me) { if ((ret = me_init()) < 0) { dbg_printf(d, "me_init failed: %d", ret); use_me = false; } } mp3info.check_crc = check_crc; mp3info.have_crc = false; if (use_brute_method) { if (read_mp3_info_brute(&mp3info, &mp3_data) < 0) { __end(); return -1; } } else { if (read_mp3_info(&mp3info, &mp3_data) < 0) { __end(); return -1; } } g_info.channels = mp3info.channels; g_info.sample_freq = mp3info.sample_freq; g_info.avg_bps = mp3info.average_bitrate; g_info.samples = mp3info.frames; g_info.duration = mp3info.duration; generic_readtag(&g_info, spath); if (mp3_data.use_buffer) { SceOff cur = xrIoLseek(mp3_data.fd, 0, PSP_SEEK_CUR); xrIoClose(mp3_data.fd); mp3_data.fd = -1; mp3_data.r = buffered_reader_open(spath, g_io_buffer_size, 1); if (mp3_data.r == NULL) { __end(); return -1; } buffered_reader_seek(mp3_data.r, cur); } dbg_printf(d, "[%d channel(s), %d Hz, %.2f kbps, %02d:%02d%sframes %d%s]", g_info.channels, g_info.sample_freq, g_info.avg_bps / 1000, (int) (g_info.duration / 60), (int) g_info.duration % 60, mp3info.frameoff != NULL ? ", frame table, " : ", ", g_info.samples, mp3info.have_crc ? ", crc passed" : ""); #ifdef _DEBUG if (mp3info.lame_encoded) { char lame_method[80]; char encode_msg[80]; switch (mp3info.lame_mode) { case ABR: STRCPY_S(lame_method, "ABR"); break; case CBR: STRCPY_S(lame_method, "CBR"); break; case VBR: SPRINTF_S(lame_method, "VBR V%1d", mp3info.lame_vbr_quality); break; default: break; } if (mp3info.lame_str[strlen(mp3info.lame_str) - 1] == ' ') SPRINTF_S(encode_msg, "%s%s", mp3info.lame_str, lame_method); else SPRINTF_S(encode_msg, "%s %s", mp3info.lame_str, lame_method); dbg_printf(d, "[ %s ]", encode_msg); } #endif ret = xAudioInit(); if (ret < 0) { __end(); return -1; } ret = xAudioSetFrequency(g_info.sample_freq); if (ret < 0) { __end(); return -1; } g_buff = xAudioAlloc(0, BUFF_SIZE); if (g_buff == NULL) { __end(); return -1; } if (use_me) xAudioSetChannelCallback(0, memp3_audiocallback, NULL); else xAudioSetChannelCallback(0, mp3_audiocallback, NULL); generic_lock(); g_status = ST_LOADED; generic_unlock(); return 0; }
/* Get the host list from zeroconf */ int dcc_zeroconf_add_hosts(struct dcc_hostdef **ret_list, int *ret_nhosts, int n_slots, struct dcc_hostdef **ret_prev) { char host_file[PATH_MAX], lock_file[PATH_MAX], *s = NULL; int lock_fd = -1, host_fd = -1; int fork_daemon = 0; int r = -1; char *dir; struct stat st; if (get_zeroconf_dir(&dir) != 0) { rs_log_crit("failed to get zeroconf dir.\n"); goto finish; } snprintf(lock_file, sizeof(lock_file), "%s/lock", dir); snprintf(host_file, sizeof(host_file), "%s/hosts", dir); /* Open lock file */ if ((lock_fd = open(lock_file, O_RDWR|O_CREAT, 0666)) < 0) { rs_log_crit("open('%s') failed: %s\n", lock_file, strerror(errno)); goto finish; } /* Try to lock the lock file */ if (generic_lock(lock_fd, 1, 1, 0) >= 0) { /* The lock succeeded => there's no daemon running yet! */ fork_daemon = 1; generic_lock(lock_fd, 1, 0, 0); } close(lock_fd); /* Shall we fork a new daemon? */ if (fork_daemon) { pid_t pid; rs_log_info("Spawning zeroconf daemon.\n"); if ((pid = fork()) == -1) { rs_log_crit("fork() failed: %s\n", strerror(errno)); goto finish; } else if (pid == 0) { int fd; /* Child */ /* Close file descriptors and replace them by /dev/null */ close(0); close(1); close(2); fd = open("/dev/null", O_RDWR); assert(fd == 0); fd = dup(0); assert(fd == 1); fd = dup(0); assert(fd == 2); #ifdef HAVE_SETSID setsid(); #endif chdir("/"); rs_add_logger(rs_logger_syslog, RS_LOG_DEBUG, NULL, 0); _exit(daemon_proc(host_file, lock_file, n_slots)); } /* Parent */ /* Wait some time for initial host gathering */ usleep(1000000); /* 1000 ms */ } /* Open host list read-only */ if ((host_fd = open(host_file, O_RDONLY)) < 0) { rs_log_crit("open('%s') failed: %s\n", host_file, strerror(errno)); goto finish; } /* A read lock */ if (generic_lock(host_fd, 0, 1, 1) < 0) { rs_log_crit("lock failed: %s\n", strerror(errno)); goto finish; } /* Get file size */ if (fstat(host_fd, &st) < 0) { rs_log_crit("stat() failed: %s\n", strerror(errno)); goto finish; } if (st.st_size >= MAX_FILE_SIZE) { rs_log_crit("file too large.\n"); goto finish; } /* read file data */ s = malloc((size_t) st.st_size+1); assert(s); if (dcc_readx(host_fd, s, (size_t) st.st_size) != 0) { rs_log_crit("failed to read from file.\n"); goto finish; } s[st.st_size] = 0; /* Parse host data */ if (dcc_parse_hosts(s, host_file, ret_list, ret_nhosts, ret_prev) != 0) { rs_log_crit("failed to parse host file.\n"); goto finish; } r = 0; finish: if (host_fd >= 0) { generic_lock(host_fd, 0, 0, 1); close(host_fd); } free(s); return r; }
/* The main function of the background daemon */ static int daemon_proc(const char *host_file, const char *lock_file, int n_slots) { int ret = 1; int lock_fd = -1; struct daemon_data d; time_t clip_time; int error; char machine[64], version[64], stype[128]; rs_add_logger(rs_logger_syslog, RS_LOG_DEBUG, NULL, 0); /* Prepare daemon data structure */ d.fd = -1; d.hosts = NULL; d.n_slots = n_slots; d.simple_poll = NULL; d.browser = NULL; d.client = NULL; clip_time = time(NULL); rs_log_info("Zeroconf daemon running.\n"); /* Open daemon lock file and lock it */ if ((lock_fd = open(lock_file, O_RDWR|O_CREAT, 0666)) < 0) { rs_log_crit("open('%s') failed: %s\n", lock_file, strerror(errno)); goto finish; } if (generic_lock(lock_fd, 1, 1, 0) < 0) { /* lock failed, there's probably already another daemon running */ goto finish; } /* Open host file */ if ((d.fd = open(host_file, O_RDWR|O_CREAT, 0666)) < 0) { rs_log_crit("open('%s') failed: %s\n", host_file, strerror(errno)); goto finish; } /* Clear host file */ write_hosts(&d); if (!(d.simple_poll = avahi_simple_poll_new())) { rs_log_crit("Failed to create simple poll object.\n"); goto finish; } if (!(d.client = avahi_client_new( avahi_simple_poll_get(d.simple_poll), 0, client_callback, &d, &error))) { rs_log_crit("Failed to create Avahi client object: %s\n", avahi_strerror(error)); goto finish; } if (dcc_get_gcc_version(version, sizeof(version)) && dcc_get_gcc_machine(machine, sizeof(machine))) { dcc_make_dnssd_subtype(stype, sizeof(stype), version, machine); } else { rs_log_warning("Warning, failed to get CC version and machine type.\n"); strncpy(stype, DCC_DNS_SERVICE_TYPE, sizeof(stype)); stype[sizeof(stype)-1] = 0; } rs_log_info("Browsing for '%s'.\n", stype); if (!(d.browser = avahi_service_browser_new( d.client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, stype, NULL, 0, browse_reply, &d))) { rs_log_crit("Failed to create service browser object: %s\n", avahi_strerror(avahi_client_errno(d.client))); goto finish; } /* Check whether the host file has been used recently */ while (fd_last_used(d.fd, clip_time) <= MAX_IDLE_TIME) { /* Iterate the main loop for 5s */ if (avahi_simple_poll_iterate(d.simple_poll, 5000) != 0) { rs_log_crit("Event loop exited abnormaly.\n"); goto finish; } } /* Wer are idle */ rs_log_info("Zeroconf daemon unused.\n"); ret = 0; finish: /* Cleanup */ if (lock_fd >= 0) { generic_lock(lock_fd, 1, 0, 0); close(lock_fd); } if (d.fd >= 0) close(d.fd); while (d.hosts) { struct host *h = d.hosts; d.hosts = d.hosts->next; free_host(h); } if (d.client) avahi_client_free(d.client); if (d.simple_poll) avahi_simple_poll_free(d.simple_poll); rs_log_info("zeroconf daemon ended.\n"); return ret; }
/** * TTA音乐播放回调函数, * 负责将解码数据填充声音缓存区 * * @note 声音缓存区的格式为双声道,16位低字序 * * @param buf 声音缓冲区指针 * @param reqn 缓冲区帧大小 * @param pdata 用户数据,无用 */ static int tta_audiocallback(void *buf, unsigned int reqn, void *pdata) { int avail_frame; int snd_buf_frame_size = (int) reqn; int ret; double incr; signed short *audio_buf = buf; UNUSED(pdata); if (g_status != ST_PLAYING) { if (g_status == ST_FFORWARD) { g_play_time += g_seek_seconds; if (g_play_time >= g_info.duration) { __end(); return -1; } generic_lock(); g_status = ST_PLAYING; generic_set_playback(true); generic_unlock(); tta_seek_seconds(g_play_time); } else if (g_status == ST_FBACKWARD) { g_play_time -= g_seek_seconds; if (g_play_time < 0.) { g_play_time = 0.; } generic_lock(); g_status = ST_PLAYING; generic_set_playback(true); generic_unlock(); tta_seek_seconds(g_play_time); } xAudioClearSndBuf(buf, snd_buf_frame_size); xrKernelDelayThread(100000); return 0; } while (snd_buf_frame_size > 0) { avail_frame = g_buff_frame_size - g_buff_frame_start; if (avail_frame >= snd_buf_frame_size) { send_to_sndbuf(audio_buf, &g_buff[g_buff_frame_start * g_info.channels], snd_buf_frame_size, g_info.channels); g_buff_frame_start += snd_buf_frame_size; audio_buf += snd_buf_frame_size * 2; snd_buf_frame_size = 0; } else { send_to_sndbuf(audio_buf, &g_buff[g_buff_frame_start * g_info.channels], avail_frame, g_info.channels); snd_buf_frame_size -= avail_frame; audio_buf += avail_frame * 2; if (g_samples_decoded >= g_info.samples) { __end(); return -1; } ret = get_samples((byte *) g_buff); if (ret <= 0) { __end(); return -1; } g_buff_frame_size = ret; g_buff_frame_start = 0; incr = (double) (g_buff_frame_size) / g_info.sample_freq; g_play_time += incr; g_samples_decoded += g_buff_frame_size; } } return 0; }