Ejemplo n.º 1
0
static void pa_stream_finished(void *userdata) {
	if (running) {
		LOG_INFO("stream finished");
		LOCK;
		output.pa_reopen = true;
		wake_controller();
		UNLOCK;
	}
}
Ejemplo n.º 2
0
static void *ir_thread() {
	char *code;
	
	while (fd > 0 && LIRC(i, nextcode, &code) == 0) {
		
		u32_t now = gettime_ms();
		u32_t ir_code = 0;
		
		if (code == NULL) continue;
		
		if (config) {
			// allow lirc_client to decode then lookup cmd in our table
			// we can only send one IR event to slimproto so break after first one
			char *c;
			while (LIRC(i, code2char, config, code, &c) == 0 && c != NULL) {
				ir_code = ir_cmd_map(c);
				if (ir_code) {
					LOG_DEBUG("ir cmd: %s -> %x", c, ir_code);
				}
			}
		}

		if (!ir_code) {
			// try to match on lirc button name if it is from the standard namespace
			// this allows use of non slim remotes without a specific entry in .lircrc
			char *b, *r;
			strtok(code, " \n");     // discard
			r = strtok(NULL, " \n"); // repeat count
			b = strtok(NULL, " \n"); // key name
			if (r && b) {
				ir_code = ir_key_map(b, r);
				LOG_DEBUG("ir lirc: %s [%s] -> %x", b, r, ir_code);
			}
		}

		if (ir_code) {
			LOCK_I;
			if (ir.code) {
				LOG_DEBUG("code dropped");
			}
			ir.code = ir_code;
			ir.ts = now;
			UNLOCK_I;
			wake_controller();
		}
		
		free(code);
	}
	
	return 0;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
static void process_cont(u8_t *pkt, int len) {
	struct cont_packet *cont = (struct cont_packet *)pkt;
	cont->metaint = unpackN(&cont->metaint);

	LOG_INFO("cont metaint: %u loop: %u", cont->metaint, cont->loop);

	if (autostart > 1) {
		autostart -= 2;
		LOCK_S;
		if (stream.state == STREAMING_WAIT) {
			stream.state = STREAMING_BUFFERING;
			stream.meta_interval = stream.meta_next = cont->metaint;
		}
		UNLOCK_S;
		wake_controller();
	}
}
Ejemplo n.º 5
0
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) {
Ejemplo n.º 6
0
static int pa_device_id(const char *device) {
	int len = strlen(device);
	int i;

	if (!strncmp(device, "default", 7)) {
#ifndef PA18API
		return Pa_GetDefaultOutputDevice();
#else
		return Pa_GetDefaultOutputDeviceID();
#endif
	}
	if (len >= 1 && len <= 2 && device[0] >= '0' && device[0] <= '9') {
		return atoi(device);
	}

#ifndef PA18API
#define DEVICE_ID_MAXLEN 256
	for (i = 0; i < Pa_GetDeviceCount(); ++i) {
		char tmp[DEVICE_ID_MAXLEN];
		snprintf(tmp, DEVICE_ID_MAXLEN, "%s [%s]", Pa_GetDeviceInfo(i)->name, Pa_GetHostApiInfo(Pa_GetDeviceInfo(i)->hostApi)->name);
		if (!strncmp(tmp, device, len)) {
#else
	for (i = 0; i < Pa_CountDevices(); ++i) {
		if (!strncmp(Pa_GetDeviceInfo(i)->name, device, len)) {
#endif
			return i;
		}
	}

	return -1;
}

#ifndef PA18API
static int pa_callback(const void *pa_input, void *pa_output, unsigned long pa_frames_wanted, 
					   const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData);

#else
static int pa_callback(void *pa_input, void *pa_output, unsigned long pa_frames_wanted, 
			   PaTimestamp outTime, void *userData);
#endif
bool test_open(const char *device, unsigned rates[]) {
	PaStreamParameters outputParameters;
	PaError err;
	unsigned ref[] TEST_RATES;
	int device_id, i, ind;

	if ((device_id = pa_device_id(device)) == -1) {
		LOG_INFO("device %s not found", device);
		return false;
	}

	outputParameters.device = device_id;
	outputParameters.channelCount = 2;
	outputParameters.sampleFormat = paInt32;
#ifndef PA18API
	outputParameters.suggestedLatency =
		output.latency ? (double)output.latency/(double)1000 : Pa_GetDeviceInfo(outputParameters.device)->defaultHighOutputLatency;
	outputParameters.hostApiSpecificStreamInfo = NULL;
#endif

	// check supported sample rates
	// Note use Pa_OpenStream as it appears more reliable than Pa_IsFormatSupported on some windows apis
	for (i = 0, ind = 0; ref[i]; ++i) {
#ifndef PA18API
		err = Pa_OpenStream(&pa.stream, NULL, &outputParameters, (double)ref[i], paFramesPerBufferUnspecified, paNoFlag, 
							pa_callback, NULL);
#else
		err = Pa_OpenStream(&pa.stream, paNoDevice, 0, 0, NULL, outputParameters.device,
			outputParameters.channelCount, outputParameters.sampleFormat, NULL, (double)ref[i],
			paFramesPerBuffer, paNumberOfBuffers, paNoFlag, pa_callback, NULL);
#endif
		if (err == paNoError) {
			Pa_CloseStream(pa.stream);
			rates[ind++] = ref[i];
		}
	}

	if (!rates[0]) {
		LOG_WARN("no available rate found");
		return false;
	}

	pa.stream = NULL;
	return true;
}

static void pa_stream_finished(void *userdata) {
	if (running) {
		LOG_INFO("stream finished");
		LOCK;
		output.pa_reopen = true;
		wake_controller();
		UNLOCK;
	}
}

static thread_type monitor_thread;
bool monitor_thread_running = false;

static void *pa_monitor() {
	bool output_off;

	LOCK;

	if (monitor_thread_running) {
		LOG_DEBUG("monitor thread already running");
		UNLOCK;
		return 0;
	}

	LOG_DEBUG("start monitor thread");

	monitor_thread_running = true;
	output_off = (output.state == OUTPUT_OFF);

	while (monitor_thread_running) {
		if (output_off) {
			if (output.state != OUTPUT_OFF) {
				LOG_INFO("output on");
				break;
			}
		} else {
			// this is a hack to partially support hot plugging of devices
			// we rely on terminating and reinitalising PA to get an updated list of devices and use name for output.device
			LOG_INFO("probing device %s", output.device);
			Pa_Terminate();
			Pa_Initialize();
			pa.stream = NULL;
			if (pa_device_id(output.device) != -1) {
				LOG_INFO("device reopen");
				break;
			}
		}

		UNLOCK;
		sleep(output_off ? 1 : 5);
		LOCK;
	}

	LOG_DEBUG("end monitor thread");

	monitor_thread_running = false;
	pa.stream = NULL;

	_pa_open();

	UNLOCK;

	return 0;
}