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; }
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; }