Example #1
0
int quisk_read_sound(void)	// Called from sound thread
{  // called in an infinite loop by the main program
	int i, nSamples, mic_count, mic_interp, retval, is_cw, mic_sample_rate;
	complex double tx_mic_phase;
	static double cwEnvelope=0;
	static double cwCount=0;
	static complex double tuneVector = (double)CLIP32 / CLIP16;	// Convert 16-bit to 32-bit samples
	static struct quisk_cFilter filtInterp={NULL};
        key_state = quisk_is_key_down(); //reading this once is important for predicable bevavior on cork/flush
        
#if DEBUG_MIC == 1
	complex double tmpSamples[SAMP_BUFFER_SIZE];
#endif

	quisk_sound_state.interupts++;
#if DEBUG_IO > 1
	QuiskPrintTime("Start read_sound", 0);
#endif
	if (pt_sample_read) {			// read samples from SDR-IQ or UDP
		nSamples = (*pt_sample_read)(cSamples);
	}
	else if (Capture.handle) {		// blocking read from soundcard
                if (Capture.driver == DEV_DRIVER_PULSEAUDIO && quisk_sound_state.IQ_server[0]) {
                	if (key_state == 1 && !(Capture.cork_status)) 
                	   	quisk_cork_pulseaudio(&Capture, 1);
                	else if (key_state == 0 && Capture.cork_status) {
                     		quisk_flush_pulseaudio(&Capture);
                      		quisk_cork_pulseaudio(&Capture, 0);

                   	}
                }

		nSamples = read_sound_interface(&Capture, cSamples);
		if (Capture.channel_Delay >= 0)	// delay the I or Q channel by one sample
			delay_sample(&Capture, (double *)cSamples, nSamples);
		if (Capture.doAmplPhase)		// amplitude and phase corrections
			correct_sample(&Capture, cSamples, nSamples);
	}
	else {
		nSamples = 0;
	}
	retval = nSamples;		// retval remains the number of samples read
#if DEBUG_IO
	debug_timer += nSamples;
	if (debug_timer >= quisk_sound_state.sample_rate)		// one second
		debug_timer = 0;
#endif
#if DEBUG_IO > 2
	ptimer (nSamples);
#endif
	quisk_sound_state.latencyCapt = nSamples;	// samples available
#if DEBUG_IO > 1
	QuiskPrintTime("  read samples", 0);
#endif
	// Perhaps record the samples to a file
	if (want_record) {
		if (is_recording_samples) {
			record_samples(cSamples, nSamples);   // Record samples
		}
		else if (file_name_samples[0]) {
			if (record_samples(NULL, -1))	 // Open file
				is_recording_samples = 1;
		}
	}
	else if (is_recording_samples) {
		record_samples(NULL, -2);		 // Close file
		is_recording_samples = 0;
	}
	// Perhaps write samples to a loopback device for use by another program
	if (RawSamplePlayback.handle)
		play_sound_interface(&RawSamplePlayback, nSamples, cSamples, 0, 1.0);
#if ! DEBUG_MIC
	nSamples = quisk_process_samples(cSamples, nSamples);
#endif
#if DEBUG_IO > 1
	QuiskPrintTime("  process samples", 0);
#endif

	if (quisk_record_state == PLAYBACK)
		quisk_tmp_playback(cSamples, nSamples, 1.0);		// replace radio sound
	else if (quisk_record_state == PLAY_FILE)
		quisk_file_playback(cSamples, nSamples, 1.0);		// replace radio sound
   
	// Play the demodulated audio
	play_sound_interface(&Playback, nSamples, cSamples, 1, quisk_audioVolume);
   
	// Play digital if required
	if (rxMode == 7 || rxMode == 8 || rxMode == 9)
		play_sound_interface(&DigitalOutput, nSamples, cSamples, 0, digital_output_level);
   
	// Perhaps record the speaker audio to a file
	if (want_record) {
		if (is_recording_audio) {
			record_audio(cSamples, nSamples);   // Record samples
		}
		else if (file_name_audio[0]) {
			if (record_audio(NULL, -1))	 // Open file
				is_recording_audio = 1;
		}
	}
	else if (is_recording_audio) {
		record_audio(NULL, -2);		 // Close file
		is_recording_audio = 0;
	}

#if DEBUG_IO > 1
	QuiskPrintTime("  play samples", 0);
#endif
	// Read and process the microphone
	mic_count = 0;
	mic_sample_rate = quisk_sound_state.mic_sample_rate;
    if (MicCapture.handle)
		mic_count = read_sound_interface(&MicCapture, cSamples);
	if (quisk_record_state == PLAYBACK)			// Discard previous samples and replace with saved sound
		quisk_tmp_microphone(cSamples, mic_count);
	else if (quisk_record_state == PLAY_FILE)	// Discard previous samples and replace with saved sound
		quisk_file_microphone(cSamples, mic_count);
	if (rxMode == 7 || rxMode == 8 || rxMode == 9) {		// Discard previous samples and use digital samples
		if (DigitalInput.handle) {
                   if (DigitalInput.driver == DEV_DRIVER_PULSEAUDIO && quisk_sound_state.IQ_server[0]) {
                      if (key_state == 0 && !(DigitalInput.cork_status)) {//key is up, cork stream
                         quisk_cork_pulseaudio(&DigitalInput, 1);
                      }
                      else if (key_state == 1 && DigitalInput.cork_status) { //key is down, uncork and flush
                         quisk_flush_pulseaudio(&DigitalInput);
                         if (MicPlayback.handle)
                             quisk_flush_pulseaudio(&MicPlayback);
                         quisk_cork_pulseaudio(&DigitalInput, 0);
                      }
                   }
		   mic_sample_rate = DigitalInput.sample_rate;
		   mic_count = read_sound_interface(&DigitalInput, cSamples);
		}
		else {
			mic_count = 0;
		}
	}
	if (mic_count > 0) {
#if DEBUG_IO > 1
		QuiskPrintTime("  mic-read", 0);
#endif
#if DEBUG_MIC == 3
		quisk_process_samples(cSamples, mic_count);
#endif
		// quisk_process_microphone returns samples at the sample rate MIC_OUT_RATE
		mic_count = quisk_process_microphone(mic_sample_rate, cSamples, mic_count);
#if DEBUG_MIC == 1
		for (i = 0; i < mic_count; i++)
			tmpSamples[i] = cSamples[i] * (double)CLIP32 / CLIP16;	// convert 16-bit samples to 32 bits
		quisk_process_samples(tmpSamples, mic_count);
#endif
#if DEBUG_IO > 1
		QuiskPrintTime("  mic-proc", 0);
#endif
	}
	// Mic playback without a mic is needed for CW
	if (MicPlayback.handle) {		// Mic playback: send mic I/Q samples to a sound card
		if (rxMode == 0 || rxMode == 1) {	// Transmit CW
			is_cw = 1;
            if (MicCapture.driver == DEV_DRIVER_PULSEAUDIO && MicCapture.cork_status) {
                //uncork and flush streams. Don't cork on key-up while in CW mode.
                quisk_flush_pulseaudio(&MicCapture);
                quisk_cork_pulseaudio(&MicCapture, 0);
                quisk_flush_pulseaudio(&MicPlayback);
            }
		}
		else {
			is_cw = 0;
			cwCount = 0;
			cwEnvelope = 0.0;
            //Not in CW mode, cork/uncork capture streams as PTT is used.
            if (MicCapture.driver == DEV_DRIVER_PULSEAUDIO && MicCapture.handle && quisk_sound_state.IQ_server[0]) {
                if (key_state == 0 && !(MicCapture.cork_status)) {//key is up, cork stream
                    quisk_cork_pulseaudio(&MicCapture, 1);
                }
                else if (key_state == 1 && DigitalInput.cork_status) { //key is down, uncork and flush
                    quisk_flush_pulseaudio(&MicCapture);
                    quisk_flush_pulseaudio(&MicPlayback);
                    quisk_cork_pulseaudio(&MicCapture, 0);
                }
            }
		}
		tx_mic_phase = cexp(( -I * 2.0 * M_PI * quisk_tx_tune_freq) / MicPlayback.sample_rate);
		if (is_cw) {	// Transmit CW; use capture device for timing, not microphone
			cwCount += (double)retval * MicPlayback.sample_rate / quisk_sound_state.sample_rate;
			mic_count = 0;
			if (quisk_is_key_down()) {
				while (cwCount >= 1.0) {
					if (cwEnvelope < 1.0) {
						cwEnvelope += 1. / (MicPlayback.sample_rate * 5e-3);	// 5 milliseconds
						if (cwEnvelope > 1.0)
							cwEnvelope = 1.0;
					}
					if (quiskSpotLevel >= 0)
						cSamples[mic_count++] = (CLIP16 - 1) * cwEnvelope * quiskSpotLevel / 1000.0 * tuneVector * quisk_sound_state.mic_out_volume;
					else
						cSamples[mic_count++] = (CLIP16 - 1) * cwEnvelope * tuneVector * quisk_sound_state.mic_out_volume;
					tuneVector *= tx_mic_phase;
					cwCount -= 1;
				}
			}
			else {		// key is up
				while (cwCount >= 1.0) {
					if (cwEnvelope > 0.0) {
						cwEnvelope -= 1.0 / (MicPlayback.sample_rate * 5e-3);	// 5 milliseconds
						if (cwEnvelope < 0.0)
							cwEnvelope = 0.0;
					}
					cSamples[mic_count++] = (CLIP16 - 1) * cwEnvelope * tuneVector * quisk_sound_state.mic_out_volume;
					tuneVector *= tx_mic_phase;
					cwCount -= 1;
				}
			}
		}
		else if( ! DEBUG_MIC && ! quisk_is_key_down()) {		// Transmit SSB
			for (i = 0; i < mic_count; i++)
				cSamples[i] = 0.0;
		}
		// Perhaps interpolate the mic samples back to the mic play rate
		mic_interp = MicPlayback.sample_rate / MIC_OUT_RATE;
		if ( ! is_cw && mic_interp > 1) {
			if (! filtInterp.dCoefs)
				quisk_filt_cInit(&filtInterp, quiskFilt12_19Coefs, sizeof(quiskFilt12_19Coefs)/sizeof(double));
			mic_count = quisk_cInterpolate(cSamples, mic_count, &filtInterp, mic_interp);
		}
		// Tune the samples to frequency
		if ( ! is_cw) {
			for (i = 0; i < mic_count; i++) {
				cSamples[i] = conj(cSamples[i]) * tuneVector * quisk_sound_state.mic_out_volume;
				tuneVector *= tx_mic_phase;
			}
		}
		// delay the I or Q channel by one sample
		if (MicPlayback.channel_Delay >= 0)
			delay_sample(&MicPlayback, (double *)cSamples, mic_count);
		// amplitude and phase corrections
		if (MicPlayback.doAmplPhase)
            correct_sample (&MicPlayback, cSamples, mic_count);

        // play mic samples
        play_sound_interface(&MicPlayback, mic_count, cSamples, 0, 1.0);

#if DEBUG_MIC == 2
		quisk_process_samples(cSamples, mic_count);
#endif
	}
        prev_key_state = key_state;
#if DEBUG_IO > 1
	QuiskPrintTime("  finished", 0);
#endif
	// Return negative number for error
	return retval;
}
Example #2
0
File: sound.c Project: n1ywb/quisk
int quisk_read_sound(void)	// Called from sound thread
{  // called in an infinite loop by the main program
	int i, nSamples, mic_count, mic_interp, retval, is_cw, mic_sample_rate;
	complex tx_mic_phase;
	static double cwEnvelope=0;
	static double cwCount=0;
	static complex tuneVector = (double)CLIP32 / CLIP16;	// Convert 16-bit to 32-bit samples
	static struct quisk_cFilter filtInterp={NULL};

	quisk_sound_state.interupts++;
#if DEBUG_IO > 1
	QuiskPrintTime("Start read_sound", 0);
#endif
	if (pt_sample_read) {			// read samples from SDR-IQ
		nSamples = (*pt_sample_read)(cSamples);
	}
	else if (quisk_using_udp) {	// read samples from UDP port
		nSamples = quisk_read_rx_udp(cSamples);
	}
	else if (Capture.handle) {							// blocking read from soundcard
		if (Capture.portaudio_index < 0)
			nSamples = quisk_read_alsa(&Capture, cSamples);
		else
			nSamples = quisk_read_portaudio(&Capture, cSamples);
		if (Capture.channel_Delay >= 0)	// delay the I or Q channel by one sample
			delay_sample(&Capture, (double *)cSamples, nSamples);
		if (Capture.doAmplPhase)		// amplitude and phase corrections
			correct_sample(&Capture, cSamples, nSamples);
	}
	else {
		nSamples = 0;
	}
	retval = nSamples;		// retval remains the number of samples read
#if DEBUG_IO
	debug_timer += nSamples;
	if (debug_timer >= quisk_sound_state.sample_rate)		// one second
		debug_timer = 0;
#endif
#if DEBUG_IO > 2
	ptimer (nSamples);
#endif
	quisk_sound_state.latencyCapt = nSamples;	// samples available
#if DEBUG_IO > 1
	QuiskPrintTime("  read samples", 0);
#endif
	// Perhaps record the samples to a file
	if (want_record) {
		if (is_recording_samples) {
			record_samples(cSamples, nSamples);   // Record samples
		}
		else if (file_name_samples[0]) {
			if (record_samples(NULL, -1))	 // Open file
				is_recording_samples = 1;
		}
	}
	else if (is_recording_samples) {
		record_samples(NULL, -2);		 // Close file
		is_recording_samples = 0;
	}
#if ! DEBUG_MIC
	nSamples = quisk_process_samples(cSamples, nSamples);
#endif
#if DEBUG_IO > 1
	QuiskPrintTime("  process samples", 0);
#endif
	if (Playback.portaudio_index < 0)
		quisk_play_alsa(&Playback, nSamples, cSamples, 1);
	else
		quisk_play_portaudio(&Playback, nSamples, cSamples, 1);
	if (rxMode == 7) {
		if (DigitalOutput.portaudio_index < 0)
			quisk_play_alsa(&DigitalOutput, nSamples, cSamples, 0);
		else
			quisk_play_portaudio(&DigitalOutput, nSamples, cSamples, 0);
	}
	// Perhaps record the speaker audio to a file
	if (want_record) {
		if (is_recording_audio) {
			record_audio(cSamples, nSamples);   // Record samples
		}
		else if (file_name_audio[0]) {
			if (record_audio(NULL, -1))	 // Open file
				is_recording_audio = 1;
		}
	}
	else if (is_recording_audio) {
		record_audio(NULL, -2);		 // Close file
		is_recording_audio = 0;
	}

#if DEBUG_IO > 1
	QuiskPrintTime("  play samples", 0);
#endif
	// Read and process the microphone
	mic_count = 0;
	mic_sample_rate = quisk_sound_state.mic_sample_rate;
	if (MicCapture.handle) {
		if (MicCapture.portaudio_index < 0)
			mic_count = quisk_read_alsa(&MicCapture, cSamples);
		else
			mic_count = quisk_read_portaudio(&MicCapture, cSamples);
	}
	if (quisk_record_state == PLAYBACK) {	// Discard previous samples and replace with saved sound
		quisk_tmp_microphone(cSamples, mic_count);
	}
	if (rxMode == 7) {		// Discard previous samples and use digital samples
		if (DigitalInput.handle) {
			mic_sample_rate = DigitalInput.sample_rate;
			if (DigitalInput.portaudio_index < 0)
				mic_count = quisk_read_alsa(&DigitalInput, cSamples);
			else
				mic_count = quisk_read_portaudio(&DigitalInput, cSamples);
		}
		else {
			mic_count = 0;
		}
	}
	if (mic_count > 0) {
#if DEBUG_IO > 1
		QuiskPrintTime("  mic-read", 0);
#endif
		// quisk_process_microphone returns samples at the sample rate MIC_OUT_RATE
		mic_count = quisk_process_microphone(mic_sample_rate, cSamples, mic_count);
#if DEBUG_MIC == 1
		if ( ! quisk_is_key_down())
			for (i = 0; i < mic_count; i++)
				cSamples[i] = 0;
		for (i = 0; i < mic_count; i++)
			cSamples[i] *= (double)CLIP32 / CLIP16;	// convert 16-bit samples to 32 bits
		quisk_process_samples(cSamples, mic_count);
#endif
#if DEBUG_IO > 1
		QuiskPrintTime("  mic-proc", 0);
#endif
	}
	// Mic playback without a mic is needed for CW
	if (MicPlayback.handle) {		// Mic playback: send mic I/Q samples to a sound card
		if (rxMode == 0 || rxMode == 1) {	// Transmit CW
			is_cw = 1;
		}
		else {
			is_cw = 0;
			cwCount = 0;
			cwEnvelope = 0.0;
		}
		tx_mic_phase = cexp(( -I * 2.0 * M_PI * quisk_tx_tune_freq) / MicPlayback.sample_rate);
		if (is_cw) {	// Transmit CW; use capture device for timing, not microphone
			cwCount += (double)retval * MicPlayback.sample_rate / quisk_sound_state.sample_rate;
			mic_count = 0;
			if (quisk_is_key_down()) {
				while (cwCount >= 1.0) {
					if (cwEnvelope < 1.0) {
						cwEnvelope += 1. / (MicPlayback.sample_rate * 5e-3);	// 5 milliseconds
						if (cwEnvelope > 1.0)
							cwEnvelope = 1.0;
					}
					cSamples[mic_count++] = (CLIP16 - 1) * cwEnvelope * tuneVector * quisk_sound_state.mic_out_volume;
					tuneVector *= tx_mic_phase;
					cwCount -= 1;
				}
			}
			else {		// key is up
				while (cwCount >= 1.0) {
					if (cwEnvelope > 0.0) {
						cwEnvelope -= 1.0 / (MicPlayback.sample_rate * 5e-3);	// 5 milliseconds
						if (cwEnvelope < 0.0)
							cwEnvelope = 0.0;
					}
					cSamples[mic_count++] = (CLIP16 - 1) * cwEnvelope * tuneVector * quisk_sound_state.mic_out_volume;
					tuneVector *= tx_mic_phase;
					cwCount -= 1;
				}
			}
		}
		else {		// Transmit SSB
			if ( ! quisk_is_key_down()) {
				for (i = 0; i < mic_count; i++)
					cSamples[i] = 0.0;
			}
		}
		// Perhaps interpolate the mic samples back to the mic play rate
		mic_interp = MicPlayback.sample_rate / MIC_OUT_RATE;
		if ( ! is_cw && mic_interp > 1) {
			if (! filtInterp.dCoefs)
				quisk_filt_cInit(&filtInterp, quiskFilt12_19Coefs, sizeof(quiskFilt12_19Coefs)/sizeof(double));
			mic_count = quisk_cInterpolate(cSamples, mic_count, &filtInterp, mic_interp);
		}
		// Tune the samples to frequency
		if ( ! is_cw) {
			for (i = 0; i < mic_count; i++) {
				cSamples[i] = conj(cSamples[i]) * tuneVector * quisk_sound_state.mic_out_volume;
				tuneVector *= tx_mic_phase;
			}
		}
		// delay the I or Q channel by one sample
		if (MicPlayback.channel_Delay >= 0)
			delay_sample(&MicPlayback, (double *)cSamples, mic_count);
		// amplitude and phase corrections
		if (MicPlayback.doAmplPhase)
			correct_sample (&MicPlayback, cSamples, mic_count);
		// play mic samples
		if (MicPlayback.portaudio_index < 0)
			quisk_play_alsa(&MicPlayback, mic_count, cSamples, 0);
		else
			quisk_play_portaudio(&MicPlayback, mic_count, cSamples, 0);
#if DEBUG_MIC == 2
		quisk_process_samples(cSamples, mic_count);
#endif
	}
#if DEBUG_IO > 1
	QuiskPrintTime("  finished", 0);
#endif
	// Return negative number for error
	return retval;
}