void resample_samples(struct processstate *process) { size_t idone, odone; size_t clip_cnt; soxr_error_t error = SOXR(r, process, r->resampler, process->inbuf, process->in_frames, &idone, process->outbuf, process->max_out_frames, &odone); if (error) { LOG_INFO("soxr_process error: %s", soxr_strerror(error)); return; } if (idone != process->in_frames) { // should not get here if buffers are big enough... LOG_ERROR("should not get here - partial sox process: %u of %u processed %u of %u out", (unsigned)idone, process->in_frames, (unsigned)odone, process->max_out_frames); } process->out_frames = odone; process->total_in += idone; process->total_out += odone; clip_cnt = *(SOXR(r, num_clips, r->resampler)); if (clip_cnt - r->old_clips) { LOG_SDEBUG("resampling clips: %u", (unsigned)(clip_cnt - r->old_clips)); r->old_clips = clip_cnt; } }
/*---------------------------------------------------------------------------*/ static void *decode_thread(struct thread_ctx_s *ctx) { while (ctx->decode_running) { size_t bytes, space, min_space; bool toend; bool ran = false; LOCK_S; bytes = _buf_used(ctx->streambuf); toend = (ctx->stream.state <= DISCONNECT); UNLOCK_S; LOCK_O; space = _buf_space(ctx->outputbuf); UNLOCK_O; LOCK_D; if (ctx->decode.state == DECODE_RUNNING && ctx->codec) { LOG_SDEBUG("streambuf bytes: %u outputbuf space: %u", bytes, space); IF_DIRECT( min_space = ctx->codec->min_space; ); IF_PROCESS( min_space = ctx->process.max_out_frames * BYTES_PER_FRAME; );
void resample_samples(struct thread_ctx_s *ctx) { struct soxr *r = ctx->decode.process_handle; size_t idone, odone; size_t clip_cnt; soxr_error_t error = SOXR(&gr, process, r->resampler, ctx->process.inbuf, ctx->process.in_frames, &idone, ctx->process.outbuf, ctx->process.max_out_frames, &odone); if (error) { LOG_INFO("[%p]: soxr_process error: %s", ctx, soxr_strerror(error)); return; } if (idone != ctx->process.in_frames) { // should not get here if buffers are big enough... LOG_ERROR("[%p]: should not get here - partial sox process: %u of %u processed %u of %u out", ctx, (unsigned)idone, ctx->process.in_frames, (unsigned)odone, ctx->process.max_out_frames); } ctx->process.out_frames = odone; ctx->process.total_in += idone; ctx->process.total_out += odone; clip_cnt = *(SOXR(&gr, num_clips, r->resampler)); if (clip_cnt - r->old_clips) { LOG_SDEBUG("[%p]: resampling clips: %u", ctx, (unsigned)(clip_cnt - r->old_clips)); r->old_clips = clip_cnt; } }
static void *decode_thread() { while (running) { size_t bytes, space; bool toend; bool ran = false; LOCK_S; bytes = _buf_used(streambuf); toend = (stream.state <= DISCONNECT); UNLOCK_S; LOCK_O; space = _buf_space(outputbuf); UNLOCK_O; LOCK_D; if (decode.state == DECODE_RUNNING && codec) { LOG_SDEBUG("streambuf bytes: %u outputbuf space: %u", bytes, space); if (space > codec->min_space && (bytes > codec->min_read_bytes || toend)) { decode.state = codec->decode(); if (decode.state != DECODE_RUNNING) { LOG_INFO("decode %s", decode.state == DECODE_COMPLETE ? "complete" : "error"); LOCK_O; if (output.fade_mode) _checkfade(false); UNLOCK_O; wake_controller(); } ran = true; } } UNLOCK_D; if (!ran) { usleep(100000); } } return 0; }
static void sendSTAT(const char *event, u32_t server_timestamp) { struct STAT_packet pkt; u32_t now = gettime_ms(); u32_t ms_played; if (status.current_sample_rate && status.frames_played && status.frames_played > status.device_frames) { ms_played = (u32_t)(((u64_t)(status.frames_played - status.device_frames) * (u64_t)1000) / (u64_t)status.current_sample_rate); if (now > status.updated) ms_played += (now - status.updated); } else { ms_played = 0; } memset(&pkt, 0, sizeof(struct STAT_packet)); memcpy(&pkt.opcode, "STAT", 4); pkt.length = htonl(sizeof(struct STAT_packet) - 8); memcpy(&pkt.event, event, 4); // num_crlf // mas_initialized; mas_mode; packN(&pkt.stream_buffer_fullness, status.stream_full); packN(&pkt.stream_buffer_size, status.stream_size); packN(&pkt.bytes_received_H, (u64_t)status.stream_bytes >> 32); packN(&pkt.bytes_received_L, (u64_t)status.stream_bytes & 0xffffffff); pkt.signal_strength = 0xffff; packN(&pkt.jiffies, now); packN(&pkt.output_buffer_size, status.output_size); packN(&pkt.output_buffer_fullness, status.output_full); packN(&pkt.elapsed_seconds, ms_played / 1000); // voltage; packN(&pkt.elapsed_milliseconds, ms_played); pkt.server_timestamp = server_timestamp; // keep this is server format - don't unpack/pack // error_code; LOG_INFO("STAT: %s", event); if (loglevel == lSDEBUG) { LOG_SDEBUG("received bytesL: %u streambuf: %u outputbuf: %u calc elapsed: %u real elapsed: %u (diff: %u) device: %u delay: %d", (u32_t)status.stream_bytes, status.stream_full, status.output_full, ms_played, now - status.stream_start, ms_played - now + status.stream_start, status.device_frames * 1000 / status.current_sample_rate, now - status.updated); } send_packet((u8_t *)&pkt, sizeof(pkt)); }
frames_t _output_frames(frames_t avail) { frames_t frames, size; bool silence; s32_t cross_gain_in = 0, cross_gain_out = 0; s32_t *cross_ptr = NULL; s32_t gainL = output.current_replay_gain ? gain(output.gainL, output.current_replay_gain) : output.gainL; s32_t gainR = output.current_replay_gain ? gain(output.gainR, output.current_replay_gain) : output.gainR; if (output.invert) { gainL = -gainL; gainR = -gainR; } frames = _buf_used(outputbuf) / BYTES_PER_FRAME; silence = false; // start when threshold met if (output.state == OUTPUT_BUFFER && frames > output.threshold * output.next_sample_rate / 100 && frames > output.start_frames) { output.state = OUTPUT_RUNNING; LOG_INFO("start buffer frames: %u", frames); wake_controller(); } // skip ahead - consume outputbuf but play nothing if (output.state == OUTPUT_SKIP_FRAMES) { if (frames > 0) { frames_t skip = min(frames, output.skip_frames); LOG_INFO("skip %u of %u frames", skip, output.skip_frames); frames -= skip; output.frames_played += skip; while (skip > 0) { frames_t cont_frames = min(skip, _buf_cont_read(outputbuf) / BYTES_PER_FRAME); skip -= cont_frames; _buf_inc_readp(outputbuf, cont_frames * BYTES_PER_FRAME); } } output.state = OUTPUT_RUNNING; } // pause frames - play silence for required frames if (output.state == OUTPUT_PAUSE_FRAMES) { LOG_INFO("pause %u frames", output.pause_frames); if (output.pause_frames == 0) { output.state = OUTPUT_RUNNING; } else { silence = true; frames = min(avail, output.pause_frames); frames = min(frames, MAX_SILENCE_FRAMES); output.pause_frames -= frames; } } // start at - play silence until jiffies reached if (output.state == OUTPUT_START_AT) { u32_t now = gettime_ms(); if (now >= output.start_at || output.start_at > now + 10000) { output.state = OUTPUT_RUNNING; } else { u32_t delta_frames = (output.start_at - now) * output.current_sample_rate / 1000; silence = true; frames = min(avail, delta_frames); frames = min(frames, MAX_SILENCE_FRAMES); } } // play silence if buffering or no frames if (output.state <= OUTPUT_BUFFER || frames == 0) { silence = true; frames = min(avail, MAX_SILENCE_FRAMES); } LOG_SDEBUG("avail: %d frames: %d silence: %d", avail, frames, silence); frames = min(frames, avail); size = frames; while (size > 0) { frames_t out_frames; frames_t cont_frames = _buf_cont_read(outputbuf) / BYTES_PER_FRAME; int wrote; if (output.track_start && !silence) { if (output.track_start == outputbuf->readp) { unsigned delay = 0; if (output.current_sample_rate != output.next_sample_rate) { delay = output.rate_delay; } IF_DSD( if (output.dop != output.next_dop) { delay = output.dop_delay; } ) frames -= size; // add silence delay in two halves, before and after track start on rate or pcm-dop change if (delay) { output.state = OUTPUT_PAUSE_FRAMES; if (!output.delay_active) { output.pause_frames = output.current_sample_rate * delay / 2000; output.delay_active = true; // first delay - don't process track start break; } else { output.pause_frames = output.next_sample_rate * delay / 2000; output.delay_active = false; // second delay - process track start } } LOG_INFO("track start sample rate: %u replay_gain: %u", output.next_sample_rate, output.next_replay_gain); output.frames_played = 0; output.track_started = true; output.track_start_time = gettime_ms(); output.current_sample_rate = output.next_sample_rate; IF_DSD( output.dop = output.next_dop; ) if (output.fade != FADE_ACTIVE || output.fade_mode != FADE_CROSSFADE) { output.current_replay_gain = output.next_replay_gain; } output.track_start = NULL; break; } else if (output.track_start > outputbuf->readp) {
static decode_state mpg_decode(void) { size_t bytes, space, size; int ret; LOCK_S; LOCK_O; bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); space = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)); bytes = min(bytes, READ_SIZE); space = min(space, WRITE_SIZE); if (stream.state <= DISCONNECT && bytes == 0) { UNLOCK_O; UNLOCK_S; return DECODE_COMPLETE; } if (m->use16bit) { space = (space / BYTES_PER_FRAME) * 4; } ret = m->mpg123_decode(m->h, streambuf->readp, bytes, outputbuf->writep, space, &size); if (ret == MPG123_NEW_FORMAT) { if (decode.new_stream) { long rate; int channels, enc; m->mpg123_getformat(m->h, &rate, &channels, &enc); LOG_INFO("setting track_start"); output.next_sample_rate = rate; output.track_start = outputbuf->writep; if (output.fade_mode) _checkfade(true); decode.new_stream = false; } else { LOG_WARN("format change mid stream - not supported"); } } // expand 16bit output to 32bit samples if (m->use16bit) { s16_t *iptr; s32_t *optr; size_t count = size / 2; size = count * 4; iptr = (s16_t *)outputbuf->writep + count; optr = (s32_t *)outputbuf->writep + count; while (count--) { *--optr = *--iptr << 16; } } _buf_inc_readp(streambuf, bytes); _buf_inc_writep(outputbuf, size); UNLOCK_O; UNLOCK_S; LOG_SDEBUG("write %u frames", size / BYTES_PER_FRAME); if (ret == MPG123_DONE) { LOG_INFO("stream complete"); return DECODE_COMPLETE; } if (ret == MPG123_ERR) { LOG_WARN("Error"); return DECODE_COMPLETE; } // OK and NEED_MORE keep running return DECODE_RUNNING; }
static decode_state faad_decode(void) { size_t bytes_total; size_t bytes_wrap; NeAACDecFrameInfo info; s32_t *iptr; bool endstream; frames_t frames; LOCK_S; bytes_total = _buf_used(streambuf); bytes_wrap = min(bytes_total, _buf_cont_read(streambuf)); if (stream.state <= DISCONNECT && !bytes_total) { UNLOCK_S; return DECODE_COMPLETE; } if (a->consume) { u32_t consume = min(a->consume, bytes_wrap); LOG_DEBUG("consume: %u of %u", consume, a->consume); _buf_inc_readp(streambuf, consume); a->pos += consume; a->consume -= consume; UNLOCK_S; return DECODE_RUNNING; } if (decode.new_stream) { int found = 0; static unsigned char channels; static unsigned long samplerate; if (a->type == '2') { // adts stream - seek for header while (bytes_wrap >= 2 && (*(streambuf->readp) != 0xFF || (*(streambuf->readp + 1) & 0xF6) != 0xF0)) { _buf_inc_readp(streambuf, 1); bytes_total--; bytes_wrap--; } if (bytes_wrap >= 2) { long n = NEAAC(a, Init, a->hAac, streambuf->readp, bytes_wrap, &samplerate, &channels); if (n < 0) { found = -1; } else { _buf_inc_readp(streambuf, n); found = 1; } } } else { // mp4 - read header found = read_mp4_header(&samplerate, &channels); } if (found == 1) { LOG_INFO("samplerate: %u channels: %u", samplerate, channels); bytes_total = _buf_used(streambuf); bytes_wrap = min(bytes_total, _buf_cont_read(streambuf)); LOCK_O; LOG_INFO("setting track_start"); output.next_sample_rate = decode_newstream(samplerate, output.max_sample_rate); output.track_start = outputbuf->writep; if (output.fade_mode) _checkfade(true); decode.new_stream = false; UNLOCK_O; } else if (found == -1) { LOG_WARN("error reading stream header"); UNLOCK_S; return DECODE_ERROR; } else { // not finished header parsing come back next time UNLOCK_S; return DECODE_RUNNING; } } if (bytes_wrap < WRAPBUF_LEN && bytes_total > WRAPBUF_LEN) { // make a local copy of frames which may have wrapped round the end of streambuf u8_t buf[WRAPBUF_LEN]; memcpy(buf, streambuf->readp, bytes_wrap); memcpy(buf + bytes_wrap, streambuf->buf, WRAPBUF_LEN - bytes_wrap); iptr = NEAAC(a, Decode, a->hAac, &info, buf, WRAPBUF_LEN); } else { iptr = NEAAC(a, Decode, a->hAac, &info, streambuf->readp, bytes_wrap); } if (info.error) { LOG_WARN("error: %u %s", info.error, NEAAC(a, GetErrorMessage, info.error)); } endstream = false; // mp4 end of chunk - skip to next offset if (a->chunkinfo && a->chunkinfo[a->nextchunk].offset && a->sample++ == a->chunkinfo[a->nextchunk].sample) { if (a->chunkinfo[a->nextchunk].offset > a->pos) { u32_t skip = a->chunkinfo[a->nextchunk].offset - a->pos; if (skip != info.bytesconsumed) { LOG_DEBUG("skipping to next chunk pos: %u consumed: %u != skip: %u", a->pos, info.bytesconsumed, skip); } if (bytes_total >= skip) { _buf_inc_readp(streambuf, skip); a->pos += skip; } else { a->consume = skip; } a->nextchunk++; } else { LOG_ERROR("error: need to skip backwards!"); endstream = true; } // adts and mp4 when not at end of chunk } else if (info.bytesconsumed != 0) { _buf_inc_readp(streambuf, info.bytesconsumed); a->pos += info.bytesconsumed; // error which doesn't advance streambuf - end } else { endstream = true; } UNLOCK_S; if (endstream) { LOG_WARN("unable to decode further"); return DECODE_ERROR; } if (!info.samples) { a->empty = true; return DECODE_RUNNING; } frames = info.samples / info.channels; if (a->skip) { u32_t skip; if (a->empty) { a->empty = false; a->skip -= frames; LOG_DEBUG("gapless: first frame empty, skipped %u frames at start", frames); } skip = min(frames, a->skip); LOG_DEBUG("gapless: skipping %u frames at start", skip); frames -= skip; a->skip -= skip; iptr += skip * info.channels; } if (a->samples) { if (a->samples < frames) { LOG_DEBUG("gapless: trimming %u frames from end", frames - a->samples); frames = (frames_t)a->samples; } a->samples -= frames; } LOG_SDEBUG("write %u frames", frames); LOCK_O_direct; while (frames > 0) { frames_t f; frames_t count; s32_t *optr; IF_DIRECT( f = _buf_cont_write(outputbuf) / BYTES_PER_FRAME; optr = (s32_t *)outputbuf->writep; ); IF_PROCESS( f = process.max_in_frames; optr = (s32_t *)process.inbuf; );