/*------------------------------------------------------------------------------- * * * * TODO: Fill me in' * * * LWVMOBILE * 2023-10 DSD-FME Florida Man Edition *-----------------------------------------------------------------------------*/ #include "dsd.h" #include //NOTE: Tones produce ringing sound when put through the hpf_d, may want to look into tweaking it, //or looking for a way to store is_tone by glancing at ambe_d values and not running hpf_d on them //TODO: WAV File saving (works fine on shorts, but on float, writing short to wav is not auto-gained, //so super quiet, either convert to float wav files, or run processAudio AFTER memcpy of the temp_buf) //simple method -- produces cleaner results, but can be muted (or very loud) at times //has manual control only, no auto gain // void agf (dsd_opts * opts, dsd_state * state, float samp[160], int slot) // { // int i; // float mmax = 0.75f; // float mmin = -0.75f; // float df = 3276.7f; // //Default gain value of 1.0f on audio_gain == 0 // float gain = 1.0f; // //make it so that gain is 1.0f on 25.0f aout, and 2.0f on 50 aout // if (opts->audio_gain != 0 && slot == 0) // gain = state->aout_gain / 25.0f; // if (opts->audio_gain != 0 && slot == 1) // gain = state->aout_gainR / 25.0f; // //mono output handles slightly different, need to further decimate // if (opts->pulse_digi_out_channels == 1) // df *= 4.0f; // for (i = 0; i < 160; i++) // { // //simple decimation // samp[i] /= df; // samp[i] *= 0.65f; // //simple clipping // if (samp[i] > mmax) // samp[i] = mmax; // if (samp[i] < mmin) // samp[i] = mmin; // //user gain factor // samp[i] *= gain; // } // } //float stereo mix 3v2 DMR void playSynthesizedVoiceFS3 (dsd_opts * opts, dsd_state * state) { //NOTE: This runs once for every two timeslots, if we are in the BS voice loop //it doesn't matter if both slots have voice, or if one does, the slot without voice //will play silence while this runs if no voice present int i; uint8_t encL, encR; float stereo_samp1[320]; //8k 2-channel stereo interleave mix float stereo_samp2[320]; //8k 2-channel stereo interleave mix float stereo_samp3[320]; //8k 2-channel stereo interleave mix memset (stereo_samp1, 0.0f, sizeof(stereo_samp1)); memset (stereo_samp2, 0.0f, sizeof(stereo_samp2)); memset (stereo_samp3, 0.0f, sizeof(stereo_samp3)); //dmr enc checkdown for whether or not to fill the stereo sample or not for playback or writing encL = encR = 0; encL = (state->dmr_so >> 6) & 0x1; encR = (state->dmr_soR >> 6) & 0x1; //checkdown to see if we can lift the 'mute' if a key is available if (encL) { if (state->payload_algid == 0) { if (state->K != 0 || state->K1 != 0) { encL = 0; } else if (state->vtx_key_loaded == 1) { encL = 0; } } else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22) { if (state->R != 0) { encL = 0; } } else if (state->payload_algid == 0x24 || state->payload_algid == 0x25 || state->payload_algid == 0x36 || state->payload_algid == 0x37) { //going to need a better check for this later on, or seperated keys or something if (state->aes_key_loaded[0] == 1) { encL = 0; } } else if (state->payload_algid == 0x26) { if (state->straight_ks == 0x26) { encL = 0; } } } if (encR) { if (state->payload_algidR == 0) { if (state->K != 0 || state->K1 != 0) { encR = 0; } else if (state->vtx_key_loaded == 1) { encR = 0; } } else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22) { if (state->RR != 0) { encR = 0; } } else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37) { //going to need a better check for this later on, or seperated keys or something if (state->aes_key_loaded[1] == 1) { encR = 0; } } else if (state->payload_algidR == 0x26) { if (state->straight_ks == 0x26) { encL = 0; } } } //debug disable enc check for AES testing // encL = 0; // encR = 0; //TODO: add option to bypass enc with a toggle as well //CHEAT: Using the slot on/off, use that to set encL or encR back on //as a simple way to turn off voice synthesis in a particular slot //its not really 'disabled', we just aren't playing it if (opts->slot1_on == 0) encL = 1; if (opts->slot2_on == 0) encR = 1; //WIP: Mute if on B list (or not W list) char modeL[8]; sprintf (modeL, "%s", ""); char modeR[8]; sprintf (modeR, "%s", ""); int TGL = state->lasttg; int TGR = state->lasttgR; //if we are using allow/whitelist mode, then write 'B' to mode for block //comparison below will look for an 'A' to write to mode if it is allowed if (opts->trunk_use_allow_list == 1) { sprintf (modeL, "%s", "B"); sprintf (modeR, "%s", "B"); } for (i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == TGL) { strcpy (modeL, state->group_array[i].groupMode); // break; //need to keep going to check other potential slot group } if (state->group_array[i].groupNumber == TGR) { strcpy (modeR, state->group_array[i].groupMode); // break; //need to keep going to check other potential slot group } } //flag either left or right as 'enc' to mute if B if (strcmp(modeL, "B") == 0) encL = 1; if (strcmp(modeR, "B") == 0) encR = 1; //if TG Hold in place, mute anything but that TG #132 if (state->tg_hold != 0 && state->tg_hold != TGL) encL = 1; if (state->tg_hold != 0 && state->tg_hold != TGR) encR = 1; //likewise, override and unmute if TG hold matches TG if (state->tg_hold != 0 && state->tg_hold == TGL) encL = 0; if (state->tg_hold != 0 && state->tg_hold == TGR) encR = 0; //run autogain on the f_ buffers agf (opts, state, state->f_l4[0],0); agf (opts, state, state->f_r4[0],1); agf (opts, state, state->f_l4[1],0); agf (opts, state, state->f_r4[1],1); agf (opts, state, state->f_l4[2],0); agf (opts, state, state->f_r4[2],1); //interleave left and right channels from the temp (float) buffer with makeshift 'volume' decimation for (i = 0; i < 160; i++) { if (!encL) stereo_samp1[i*2+0] = state->f_l4[0][i]; if (!encR) stereo_samp1[i*2+1] = state->f_r4[0][i]; } for (i = 0; i < 160; i++) { if (!encL) stereo_samp2[i*2+0] = state->f_l4[1][i]; if (!encR) stereo_samp2[i*2+1] = state->f_r4[1][i]; } for (i = 0; i < 160; i++) { if (!encL) stereo_samp3[i*2+0] = state->f_l4[2][i]; if (!encR) stereo_samp3[i*2+1] = state->f_r4[2][i]; } //at this point, if both channels are still flagged as enc, then we can skip all playback/writing functions if (encL && encR) goto FS3_END; if (opts->audio_out_type == 0) //Pulse Audio { pa_simple_write(opts->pulse_digi_dev_out, stereo_samp1, 320*4, NULL); //switch to sizeof(stereo_samp1) * 2? pa_simple_write(opts->pulse_digi_dev_out, stereo_samp2, 320*4, NULL); pa_simple_write(opts->pulse_digi_dev_out, stereo_samp3, 320*4, NULL); } if (opts->audio_out_type == 8) //UDP Audio { udp_socket_blaster (opts, state, 320*4, stereo_samp1); udp_socket_blaster (opts, state, 320*4, stereo_samp2); udp_socket_blaster (opts, state, 320*4, stereo_samp3); } //No OSS, since we can't use float output, but STDOUT can with play, aplay, etc if (opts->audio_out_type == 1) //STDOUT (still need these seperated? or not really?) { write (opts->audio_out_fd, stereo_samp1, 320*4); write (opts->audio_out_fd, stereo_samp2, 320*4); write (opts->audio_out_fd, stereo_samp3, 320*4); } FS3_END: memset (state->audio_out_temp_buf, 0.0f, sizeof(state->audio_out_temp_buf)); memset (state->audio_out_temp_bufR, 0.0f, sizeof(state->audio_out_temp_bufR)); //set float temp buffer to baseline memset (state->f_l4, 0.0f, sizeof(state->f_l4)); memset (state->f_r4, 0.0f, sizeof(state->f_r4)); //if we don't run processAudio, then I don't think we really need any of the items below active state->audio_out_idx = 0; state->audio_out_idxR = 0; if (state->audio_out_idx2 >= 800000) { state->audio_out_float_buf_p = state->audio_out_float_buf + 100; state->audio_out_buf_p = state->audio_out_buf + 100; memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); memset (state->audio_out_buf, 0, 100 * sizeof (short)); state->audio_out_idx2 = 0; } if (state->audio_out_idx2R >= 800000) { state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100; state->audio_out_buf_pR = state->audio_out_bufR + 100; memset (state->audio_out_float_bufR, 0, 100 * sizeof (float)); memset (state->audio_out_bufR, 0, 100 * sizeof (short)); state->audio_out_idx2R = 0; } } //NOTE: On FS4 and SS4 voice, the longer the transmission, the more the function will start to lag //the entire DSD-FME loop due to the skipping of playback on SACCH frames (causes noticeable skip when it does play them), //this isn't a major problem, since the buffer can handle it, but it does delay return to CC until the end //of the call on busy systems where both VCH slots are constantly busy with voice //the longer the call, the more delayed until returning to the control channel //NOTE: Disabling voice synthesis clears up the delay issue (obviosly since we aren't having to wait on it to play) //disabling voice in only one slot will also fix most random stutter from the 4v in one slot, and 2v in the other slot //NOTE: The same skip may be occurring on the main and v2.1b branches of DSD-FME as well, so that may be due to the 4v/2v and //playing back immediately instead of buffering x number of samples or 4v/2v to get a smoother playback //NOTE: When using capture bins for playback, this issue is not as observable compared to real time reception due to how fast //we can blow through pure data on bin files compared to waiting for the real time reception //its usually a lot more noticeable on dual voices than single (probably due to various arrangements of dual 4v/2v in each superframe) //float stereo mix 4v2 P25p2 void playSynthesizedVoiceFS4 (dsd_opts * opts, dsd_state * state) { //NOTE: This will run for every TS % 2, except on SACCH inverted slots (10 and 11) //WIP: Get the real TS number out of the P25p2 frame, and not our ts_counter values int i, encL, encR; float stereo_samp1[320]; //8k 2-channel stereo interleave mix float stereo_samp2[320]; //8k 2-channel stereo interleave mix float stereo_samp3[320]; //8k 2-channel stereo interleave mix float stereo_samp4[320]; //8k 2-channel stereo interleave mix float empty[320]; //this is used to see if we want to play a single 2v or double 2v or not //p25p2 enc checkdown for whether or not to fill the stereo sample or not for playback or writing encL = encR = 1; if (state->payload_algid == 0 || state->payload_algid == 0x80) encL = 0; if (state->payload_algidR == 0 || state->payload_algidR == 0x80) encR = 0; //checkdown to see if we can lift the 'mute' if a key is available if (encL) { if (state->payload_algid == 0xAA) { if (state->R != 0) { encL = 0; } } else if (state->payload_algid == 0x81) { if (state->R != 0) { encL = 0; } } else if (state->payload_algid == 0x84 || state->payload_algid == 0x89) { if (state->aes_key_loaded[0] == 1) { encL = 0; } } } if (encR) { if (state->payload_algidR == 0xAA) { if (state->RR != 0) { encR = 0; } } else if (state->payload_algidR == 0x81) { if (state->R != 0) { encR = 0; } } else if (state->payload_algidR == 0x84 || state->payload_algidR == 0x89) { if (state->aes_key_loaded[1] == 1) { encR = 0; } } } //CHEAT: Using the slot on/off, use that to set encL or encR back on //as a simple way to turn off voice synthesis in a particular slot //its not really 'disabled', we just aren't playing it if (opts->slot1_on == 0) encL = 1; if (opts->slot2_on == 0) encR = 1; //WIP: Mute if on B list (or not W list) char modeL[8]; sprintf (modeL, "%s", ""); char modeR[8]; sprintf (modeR, "%s", ""); int TGL = state->lasttg; int TGR = state->lasttgR; //if we are using allow/whitelist mode, then write 'B' to mode for block //comparison below will look for an 'A' to write to mode if it is allowed if (opts->trunk_use_allow_list == 1) { sprintf (modeL, "%s", "B"); sprintf (modeR, "%s", "B"); } for (i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == TGL) { strcpy (modeL, state->group_array[i].groupMode); // break; //need to keep going to check other potential slot group } if (state->group_array[i].groupNumber == TGR) { strcpy (modeR, state->group_array[i].groupMode); // break; //need to keep going to check other potential slot group } } //flag either left or right as 'enc' to mute if B if (strcmp(modeL, "B") == 0) encL = 1; if (strcmp(modeR, "B") == 0) encR = 1; //if TG Hold in place, mute anything but that TG #132 if (state->tg_hold != 0 && state->tg_hold != TGL) encL = 1; if (state->tg_hold != 0 && state->tg_hold != TGR) encR = 1; //likewise, override and unmute if TG hold matches TG if (state->tg_hold != 0 && state->tg_hold == TGL) encL = 0; if (state->tg_hold != 0 && state->tg_hold == TGR) encR = 0; memset (stereo_samp1, 0.0f, sizeof(stereo_samp1)); memset (stereo_samp2, 0.0f, sizeof(stereo_samp2)); memset (stereo_samp3, 0.0f, sizeof(stereo_samp3)); memset (stereo_samp4, 0.0f, sizeof(stereo_samp4)); memset (empty, 0.0f, sizeof(empty)); //run autogain on the f_ buffers agf (opts, state, state->f_l4[0],0); agf (opts, state, state->f_r4[0],1); agf (opts, state, state->f_l4[1],0); agf (opts, state, state->f_r4[1],1); agf (opts, state, state->f_l4[2],0); //doing 2 and 3 here will break the memcmp below, so we also do it on empty agf (opts, state, state->f_r4[2],1); agf (opts, state, state->f_l4[3],0); agf (opts, state, state->f_r4[3],1); agf (opts, state, empty,2); //running empty may invalidate the memcmp, ideally both this empty and an empty f_ should come out with the same result anyways //interleave left and right channels from the temp (float) buffer with makeshift 'volume' decimation for (i = 0; i < 160; i++) { if (!encL) stereo_samp1[i*2+0] = state->f_l4[0][i]; // / ((float) 480.0f*10.0f) if (!encR) stereo_samp1[i*2+1] = state->f_r4[0][i]; } for (i = 0; i < 160; i++) { if (!encL) stereo_samp2[i*2+0] = state->f_l4[1][i]; if (!encR) stereo_samp2[i*2+1] = state->f_r4[1][i]; } for (i = 0; i < 160; i++) { if (!encL) stereo_samp3[i*2+0] = state->f_l4[2][i]; if (!encR) stereo_samp3[i*2+1] = state->f_r4[2][i]; } for (i = 0; i < 160; i++) { if (!encL) stereo_samp4[i*2+0] = state->f_l4[3][i]; if (!encR) stereo_samp4[i*2+1] = state->f_r4[3][i]; } if (encL && encR) goto END_FS4; if (opts->audio_out_type == 0) //Pulse Audio { pa_simple_write(opts->pulse_digi_dev_out, stereo_samp1, 320*4, NULL); //switch to sizeof(stereo_samp1) * 2? pa_simple_write(opts->pulse_digi_dev_out, stereo_samp2, 320*4, NULL); //only play these two if not a single 2v or double 2v (minor skip can still occur on a 4v and 2v combo, but will probably only be perceivable if one is a tone) if (memcmp(empty, stereo_samp3, sizeof(empty)) != 0) pa_simple_write(opts->pulse_digi_dev_out, stereo_samp3, 320*4, NULL); if (memcmp(empty, stereo_samp4, sizeof(empty)) != 0) pa_simple_write(opts->pulse_digi_dev_out, stereo_samp4, 320*4, NULL); } if (opts->audio_out_type == 8) //UDP Audio { udp_socket_blaster (opts, state, 320*4, stereo_samp1); udp_socket_blaster (opts, state, 320*4, stereo_samp2); //only play these two if not a single 2v or double 2v (minor skip can still occur on a 4v and 2v combo, but will probably only be perceivable if one is a tone) if (memcmp(empty, stereo_samp3, sizeof(empty)) != 0) udp_socket_blaster (opts, state, 320*4, stereo_samp3); if (memcmp(empty, stereo_samp4, sizeof(empty)) != 0) udp_socket_blaster (opts, state, 320*4, stereo_samp4); } //No OSS, since we can't use float output, but the STDOUT can with play, aplay, etc if (opts->audio_out_type == 1) //STDOUT { write (opts->audio_out_fd, stereo_samp1, 320*4); write (opts->audio_out_fd, stereo_samp2, 320*4); //only play these two if not a single 2v or double 2v (minor skip can still occur on a 4v and 2v combo, but will probably only be perceivable if one is a tone) if (memcmp(empty, stereo_samp3, sizeof(empty)) != 0) write (opts->audio_out_fd, stereo_samp3, 320*4); if (memcmp(empty, stereo_samp4, sizeof(empty)) != 0) write (opts->audio_out_fd, stereo_samp4, 320*4); } END_FS4: memset (state->audio_out_temp_buf, 0.0f, sizeof(state->audio_out_temp_buf)); memset (state->audio_out_temp_bufR, 0.0f, sizeof(state->audio_out_temp_bufR)); //set float temp buffer to baseline memset (state->f_l4, 0.0f, sizeof(state->f_l4)); memset (state->f_r4, 0.0f, sizeof(state->f_r4)); //if we don't run processAudio, then I don't think we really need any of the items below active state->audio_out_idx = 0; state->audio_out_idxR = 0; if (state->audio_out_idx2 >= 800000) { state->audio_out_float_buf_p = state->audio_out_float_buf + 100; state->audio_out_buf_p = state->audio_out_buf + 100; memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); memset (state->audio_out_buf, 0, 100 * sizeof (short)); state->audio_out_idx2 = 0; } if (state->audio_out_idx2R >= 800000) { state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100; state->audio_out_buf_pR = state->audio_out_bufR + 100; memset (state->audio_out_float_bufR, 0, 100 * sizeof (float)); memset (state->audio_out_bufR, 0, 100 * sizeof (short)); state->audio_out_idx2R = 0; } } //float stereo mix -- when using Float Stereo Output, we need to send P25p1, DMR MS/Simplex, DStar, and YSF here void playSynthesizedVoiceFS (dsd_opts * opts, dsd_state * state) { int i; int encL; float stereo_samp1[320]; //8k 2-channel stereo interleave mix memset (stereo_samp1, 0.0f, sizeof(stereo_samp1)); //TODO: ENC Check on P25p1, DMR MS, etc encL = 0; //Enc Checkdown -- P25p1 when run with -ft -y switch if (state->synctype == 0 || state->synctype == 1) { if (state->payload_algid != 0 && state->payload_algid != 0x80) { encL = 1; } } //checkdown to see if we can lift the 'mute' if a key is available if (encL) { if (state->payload_algid == 0xAA || state->payload_algid == 0x81 || state->payload_algid == 0x9F) { if (state->R != 0) { encL = 0; } } else if (state->payload_algid == 0x84 || state->payload_algid == 0x89 || state->payload_algid == 0x83) { if (state->aes_key_loaded[0] == 1) { encL = 0; } } } //TODO: add option to bypass enc with a toggle as well if (opts->slot1_on == 0) encL = 1; //WIP: Mute if on B list (or not W list) char modeL[8]; sprintf (modeL, "%s", ""); int TGL = state->lasttg; //if we are using allow/whitelist mode, then write 'B' to mode for block //comparison below will look for an 'A' to write to mode if it is allowed if (opts->trunk_use_allow_list == 1) sprintf (modeL, "%s", "B"); for (int i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == TGL) { strcpy (modeL, state->group_array[i].groupMode); break; } } //flag either left or right as 'enc' to mute if B if (strcmp(modeL, "B") == 0) encL = 1; //if TG Hold in place, mute anything but that TG #132 if (state->tg_hold != 0 && state->tg_hold != TGL) encL = 1; //likewise, override and unmute if TG hold matches TG if (state->tg_hold != 0 && state->tg_hold == TGL) encL = 0; //run autogain on the f_ buffers agf (opts, state, state->f_l,0); //at this point, if both channels are still flagged as enc, then we can skip all playback/writing functions if (encL) goto FS_END; //interleave left and right channels from the temp (float) buffer with makeshift 'volume' decimation for (i = 0; i < 160; i++) { if (!encL) stereo_samp1[i*2+0] = state->f_l[i] * 0.5f; //test loading right side with same if (!encL) stereo_samp1[i*2+1] = state->f_l[i] * 0.5f; } if (opts->audio_out_type == 0) //Pulse Audio pa_simple_write(opts->pulse_digi_dev_out, stereo_samp1, 320*4, NULL); //switch to sizeof(stereo_samp1) * 2? if (opts->audio_out_type == 8) //UDP Audio udp_socket_blaster (opts, state, 320*4, stereo_samp1); //No OSS, since we can't use float output, but STDOUT can with play, aplay, etc if (opts->audio_out_type == 1) write (opts->audio_out_fd, stereo_samp1, 320*4); FS_END: memset (state->audio_out_temp_buf, 0.0f, sizeof(state->audio_out_temp_buf)); memset (state->audio_out_temp_bufR, 0.0f, sizeof(state->audio_out_temp_bufR)); //set float temp buffer to baseline memset (state->f_l4, 0.0f, sizeof(state->f_l4)); memset (state->f_r4, 0.0f, sizeof(state->f_r4)); //if we don't run processAudio, then I don't think we really need any of the items below active state->audio_out_idx = 0; state->audio_out_idxR = 0; if (state->audio_out_idx2 >= 800000) { state->audio_out_float_buf_p = state->audio_out_float_buf + 100; state->audio_out_buf_p = state->audio_out_buf + 100; memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); memset (state->audio_out_buf, 0, 100 * sizeof (short)); state->audio_out_idx2 = 0; } if (state->audio_out_idx2R >= 800000) { state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100; state->audio_out_buf_pR = state->audio_out_bufR + 100; memset (state->audio_out_float_bufR, 0, 100 * sizeof (float)); memset (state->audio_out_bufR, 0, 100 * sizeof (short)); state->audio_out_idx2R = 0; } } void playSynthesizedVoiceFM (dsd_opts * opts, dsd_state * state) { agf(opts, state, state->f_l,0); //mono may need an additional decimation // for (int i = 0; i < 160; i++) // state->f_l[i] *= 0.5f; int encL = 0; //Enc Checkdown -- P25p1 when run with -ft -y switch if (state->synctype == 0 || state->synctype == 1) { if (state->payload_algid != 0 && state->payload_algid != 0x80) { encL = 1; } } //NXDN if (state->nxdn_cipher_type != 0) { encL = 1; } //checkdown to see if we can lift the 'mute' if a key is available if (encL) { if (state->payload_algid == 0xAA || state->payload_algid == 0x81 || state->payload_algid == 0x83 || state->payload_algid == 0x9F || state->nxdn_cipher_type == 0x1 || state->nxdn_cipher_type == 0x2) { if (state->R != 0) { encL = 0; } } else if (state->payload_algid == 0x84 || state->payload_algid == 0x89 || state->nxdn_cipher_type == 0x3 || state->payload_algid == 0x36 || state->payload_algid == 0x37) { if (state->aes_key_loaded[0] == 1) { encL = 0; } } } //WIP: Mute if on B list (or not W list) char modeL[8]; sprintf (modeL, "%s", ""); int TGL = state->lasttg; if (opts->frame_nxdn48 == 1 || opts->frame_nxdn96 == 1) TGL = state->nxdn_last_tg; //if we are using allow/whitelist mode, then write 'B' to mode for block //comparison below will look for an 'A' to write to mode if it is allowed if (opts->trunk_use_allow_list == 1) sprintf (modeL, "%s", "B"); for (int i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == TGL) { strcpy (modeL, state->group_array[i].groupMode); break; } } //flag either left or right as 'enc' to mute if B if (strcmp(modeL, "B") == 0) encL = 1; //if TG Hold in place, mute anything but that TG #132 if (state->tg_hold != 0 && state->tg_hold != TGL) encL = 1; //likewise, override and unmute if TG hold matches TG if (state->tg_hold != 0 && state->tg_hold == TGL) encL = 0; if (encL == 1) goto vfm_end; if (opts->slot1_on == 0) goto vfm_end; if (opts->audio_out_type == 0) pa_simple_write(opts->pulse_digi_dev_out, state->f_l, 160*4, NULL); if (opts->audio_out_type == 8) //UDP Audio udp_socket_blaster (opts, state, 160*4, state->f_l); if (opts->audio_out_type == 1 || opts->audio_out_type == 5) write(opts->audio_out_fd, state->f_l, 160*4); vfm_end: if (state->audio_out_idx2 >= 800000) { state->audio_out_float_buf_p = state->audio_out_float_buf + 100; state->audio_out_buf_p = state->audio_out_buf + 100; memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); memset (state->audio_out_buf, 0, 100 * sizeof (short)); state->audio_out_idx2 = 0; } memset (state->f_l, 0.0f, sizeof(state->f_l)); memset (state->audio_out_temp_buf, 0.0f, sizeof(state->audio_out_temp_buf)); } //Mono - Short (SB16LE) - Drop-in replacement for playSyntesizedVoice, but easier to manipulate void playSynthesizedVoiceMS (dsd_opts * opts, dsd_state * state) { int i; size_t len = state->audio_out_idx; //debug // fprintf (stderr, " L LEN: %d", len); short mono_samp[len]; memset (mono_samp, 0, len*sizeof(short)); if (opts->slot1_on == 0) goto MS_END; if (len == 160) { for (i = 0; i < len; i++) mono_samp[i] = state->s_l[i]; } else if (len == 960) { state->audio_out_buf_p -= 960; //rewind first for (i = 0; i < len; i++) { mono_samp[i] = *state->audio_out_buf_p; state->audio_out_buf_p++; } } if (opts->use_hpf_d == 1) hpf_dL(state, state->s_l, len); if (opts->audio_out_type == 0) //Pulse Audio pa_simple_write(opts->pulse_digi_dev_out, mono_samp, len*2, NULL); if (opts->audio_out_type == 8) //UDP Audio udp_socket_blaster (opts, state, len*2, mono_samp); if (opts->audio_out_type == 1 || opts->audio_out_type == 2 || opts->audio_out_type == 5) //STDOUT or OSS write (opts->audio_out_fd, mono_samp, len*2); //this one needs testing w/ 48000 OSS output when audio is not split if (opts->wav_out_f != NULL && opts->static_wav_file == 1) { //convert to stereo for new static wav file setup short ss[320]; memset (ss, 0, sizeof(ss)); if (len == 160) { for (i = 0; i < 160; i++) { ss[(i*2)+0] = mono_samp[i]; ss[(i*2)+1] = mono_samp[i]; } } else if (len == 960) { for (i = 0; i < 160; i++) { ss[(i*2)+0] = mono_samp[i*6]; //grab every 6th sample to downsample ss[(i*2)+1] = mono_samp[i*6]; //grab every 6th sample to downsample } } sf_write_short(opts->wav_out_f, ss, 320); } MS_END: //run cleanup since we pulled stuff from processAudio state->audio_out_idx = 0; //set short temp buffer to baseline memset (state->s_l, 0, sizeof(state->s_l)); if (state->audio_out_idx2 >= 800000) { state->audio_out_float_buf_p = state->audio_out_float_buf + 100; state->audio_out_buf_p = state->audio_out_buf + 100; memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); memset (state->audio_out_buf, 0, 100 * sizeof (short)); state->audio_out_idx2 = 0; } } //Mono - Short (SB16LE) - Drop-in replacement for playSyntesizedVoiceR, but easier to manipulate void playSynthesizedVoiceMSR (dsd_opts * opts, dsd_state * state) { int i; size_t len = state->audio_out_idxR; //debug // fprintf (stderr, " R LEN: %d", len); short mono_samp[len]; memset (mono_samp, 0, len*sizeof(short)); if (opts->slot2_on == 0) goto MS_ENDR; if (len == 160) { for (i = 0; i < len; i++) mono_samp[i] = state->s_r[i]; } else if (len == 960) { state->audio_out_buf_pR -= 960; //rewind first for (i = 0; i < len; i++) { mono_samp[i] = *state->audio_out_buf_pR; state->audio_out_buf_pR++; } } if (opts->use_hpf_d == 1) hpf_dR(state, mono_samp, len); if (opts->audio_out_type == 0) //Pulse Audio pa_simple_write(opts->pulse_digi_dev_out, mono_samp, len*2, NULL); if (opts->audio_out_type == 8) //UDP Audio udp_socket_blaster (opts, state, len*2, mono_samp); if (opts->audio_out_type == 1 || opts->audio_out_type == 2 || opts->audio_out_type == 5) //STDOUT or OSS write (opts->audio_out_fd, mono_samp, len*2); //this one needs testing w/ 48000 OSS output when audio is not split if (opts->wav_out_f != NULL && opts->static_wav_file == 1) { //convert to stereo for new static wav file setup short ss[320]; memset (ss, 0, sizeof(ss)); if (len == 160) { for (i = 0; i < 160; i++) { ss[(i*2)+0] = mono_samp[i]; ss[(i*2)+1] = mono_samp[i]; } } else if (len == 960) { for (i = 0; i < 160; i++) { ss[(i*2)+0] = mono_samp[i*6]; //grab every 6th sample to downsample ss[(i*2)+1] = mono_samp[i*6]; //grab every 6th sample to downsample } } sf_write_short(opts->wav_out_f, ss, 320); } MS_ENDR: //run cleanup since we pulled stuff from processAudioR state->audio_out_idxR = 0; //set short temp buffer to baseline memset (state->s_r, 0, sizeof(state->s_r)); if (state->audio_out_idx2R >= 800000) { state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100; state->audio_out_buf_pR = state->audio_out_bufR + 100; memset (state->audio_out_float_bufR, 0, 100 * sizeof (float)); memset (state->audio_out_bufR, 0, 100 * sizeof (short)); state->audio_out_idx2R = 0; } } //Stereo Mix - Short (SB16LE) -- When Playing Short FDMA samples when setup for stereo output void playSynthesizedVoiceSS (dsd_opts * opts, dsd_state * state) { int i; int encL; short stereo_samp1[320]; //8k 2-channel stereo interleave mix memset (stereo_samp1, 0, sizeof(stereo_samp1)); //enc checkdown for whether or not to fill the stereo sample or not for playback or writing encL = 0; //Enc Checkdown -- P25p1 when run with -ft switch if (state->synctype == 0 || state->synctype == 1) { if (state->payload_algid != 0 && state->payload_algid != 0x80) { encL = 1; } } //checkdown to see if we can lift the 'mute' if a key is available if (encL) { if (state->payload_algid == 0xAA || state->payload_algid == 0x81 || state->payload_algid == 0x9F || state->payload_algid == 0x83) { if (state->R != 0) { encL = 0; } } else if (state->payload_algid == 0x84 || state->payload_algid == 0x89) { if (state->aes_key_loaded[0] == 1) { encL = 0; } } } //TODO: add option to bypass enc with a toggle as well if (opts->slot1_on == 0) encL = 1; //WIP: Mute if on B list (or not W list) char modeL[8]; sprintf (modeL, "%s", ""); int TGL = state->lasttg; //if we are using allow/whitelist mode, then write 'B' to mode for block //comparison below will look for an 'A' to write to mode if it is allowed if (opts->trunk_use_allow_list == 1) sprintf (modeL, "%s", "B"); for (int i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == TGL) { strcpy (modeL, state->group_array[i].groupMode); break; } } //flag either left or right as 'enc' to mute if B if (strcmp(modeL, "B") == 0) encL = 1; //if TG Hold in place, mute anything but that TG #132 if (state->tg_hold != 0 && state->tg_hold != TGL) encL = 1; //likewise, override and unmute if TG hold matches TG if (state->tg_hold != 0 && state->tg_hold == TGL) encL = 0; //test hpf if (opts->use_hpf_d == 1) hpf_dL(state, state->s_l, 160); //interleave left and right channels from the short storage area for (i = 0; i < 160; i++) { if (!encL) stereo_samp1[i*2+0] = state->s_l[i] / 1; //testing double left and right channel if (!encL) stereo_samp1[i*2+1] = state->s_l[i] / 1; } //at this point, if still flagged as enc, then we can skip all playback/writing functions if (encL) goto SSM_END; if (opts->audio_out_type == 0) //Pulse Audio pa_simple_write(opts->pulse_digi_dev_out, stereo_samp1, 320*2, NULL); if (opts->audio_out_type == 8) //UDP Audio udp_socket_blaster (opts, state, 320*2, stereo_samp1); if (opts->audio_out_type == 1 || opts->audio_out_type == 2) //STDOUT or OSS 8k/2 write (opts->audio_out_fd, stereo_samp1, 320*2); if (opts->wav_out_f != NULL && opts->static_wav_file == 1) sf_write_short(opts->wav_out_f, stereo_samp1, 320); SSM_END: //run cleanup since we pulled stuff from processAudio state->audio_out_idx = 0; state->audio_out_idxR = 0; //set short temp buffer to baseline memset (state->s_l, 0, sizeof(state->s_l)); memset (state->s_r, 0, sizeof(state->s_r)); if (state->audio_out_idx2 >= 800000) { state->audio_out_float_buf_p = state->audio_out_float_buf + 100; state->audio_out_buf_p = state->audio_out_buf + 100; memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); memset (state->audio_out_buf, 0, 100 * sizeof (short)); state->audio_out_idx2 = 0; } if (state->audio_out_idx2R >= 800000) { state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100; state->audio_out_buf_pR = state->audio_out_bufR + 100; memset (state->audio_out_float_bufR, 0, 100 * sizeof (float)); memset (state->audio_out_bufR, 0, 100 * sizeof (short)); state->audio_out_idx2R = 0; } } //short stereo mix 3v2 DMR void playSynthesizedVoiceSS3 (dsd_opts * opts, dsd_state * state) { //NOTE: This runs once for every two timeslots, if we are in the BS voice loop //it doesn't matter if both slots have voice, or if one does, the slot without voice //will play silence while this runs if no voice present int i; uint8_t encL, encR; short stereo_samp1[320]; //8k 2-channel stereo interleave mix short stereo_samp2[320]; //8k 2-channel stereo interleave mix short stereo_samp3[320]; //8k 2-channel stereo interleave mix memset (stereo_samp1, 0, sizeof(stereo_samp1)); memset (stereo_samp2, 0, sizeof(stereo_samp2)); memset (stereo_samp3, 0, sizeof(stereo_samp3)); //dmr enc checkdown for whether or not to fill the stereo sample or not for playback or writing encL = encR = 0; encL = (state->dmr_so >> 6) & 0x1; encR = (state->dmr_soR >> 6) & 0x1; //checkdown to see if we can lift the 'mute' if a key is available if (encL) { if (state->payload_algid == 0) { if (state->K != 0 || state->K1 != 0) { encL = 0; } else if (state->vtx_key_loaded == 1) { encL = 0; } } else if (state->payload_algid == 0x02 || state->payload_algid == 0x21 || state->payload_algid == 0x22) { if (state->R != 0) { encL = 0; } } else if (state->payload_algid == 0x24 || state->payload_algid == 0x25 || state->payload_algid == 0x36 || state->payload_algid == 0x37) { //going to need a better check for this later on, or seperated keys or something if (state->aes_key_loaded[0] == 1) { encL = 0; } } else if (state->payload_algid == 0x26) { if (state->straight_ks == 0x26) { encL = 0; } } } if (encR) { if (state->payload_algidR == 0) { if (state->K != 0 || state->K1 != 0) { encR = 0; } else if (state->vtx_key_loaded == 1) { encR = 0; } } else if (state->payload_algidR == 0x02 ||state->payload_algidR == 0x21 || state->payload_algidR == 0x22) { if (state->RR != 0) { encR = 0; } } else if (state->payload_algidR == 0x24 || state->payload_algidR == 0x25 || state->payload_algidR == 0x36 || state->payload_algidR == 0x37) { //going to need a better check for this later on, or seperated keys or something if (state->aes_key_loaded[1] == 1) { encR = 0; } } else if (state->payload_algidR == 0x26) { if (state->straight_ks == 0x26) { encR = 0; } } } //debug for AES // encL = encR = 0; //TODO: add option to bypass enc with a toggle as well //CHEAT: Using the slot on/off, use that to set encL or encR back on //as a simple way to turn off voice synthesis in a particular slot //its not really 'disabled', we just aren't playing it // if (opts->slot1_on == 0) encL = 1; // if (opts->slot2_on == 0) encR = 1; //WIP: Mute if on B list (or not W list) char modeL[8]; sprintf (modeL, "%s", ""); char modeR[8]; sprintf (modeR, "%s", ""); int TGL = state->lasttg; int TGR = state->lasttgR; //if we are using allow/whitelist mode, then write 'B' to mode for block //comparison below will look for an 'A' to write to mode if it is allowed if (opts->trunk_use_allow_list == 1) { sprintf (modeL, "%s", "B"); sprintf (modeR, "%s", "B"); } for (i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == TGL) { strcpy (modeL, state->group_array[i].groupMode); // break; //need to keep going to check other potential slot group } if (state->group_array[i].groupNumber == TGR) { strcpy (modeR, state->group_array[i].groupMode); // break; //need to keep going to check other potential slot group } } //flag either left or right as 'enc' to mute if B if (strcmp(modeL, "B") == 0) encL = 1; if (strcmp(modeR, "B") == 0) encR = 1; //check to see if we need to enable slot and toggle slot preference here //this method will always favor slot 2 (this is a patch anyways, so....meh) // if (strcmp(modeL, "A") == 0) // { // opts->slot1_on = 1; // opts->slot_preference = 0; // } // if (strcmp(modeR, "A") == 0) // { // opts->slot2_on = 1; // opts->slot_preference = 1; // } //check to see if we need to enable slot and toggle slot preference here //if both groups allowed, then give no preference to either one (not sure this is needed now) // if ( (strcmp(modeL, "A") == 0) && (strcmp(modeR, "A") == 0) ) // { // opts->slot1_on = 1; // opts->slot2_on = 1; // opts->slot_preference = 2; // } // else if (strcmp(modeL, "A") == 0) // { // opts->slot1_on = 1; // opts->slot_preference = 0; // } // else if (strcmp(modeR, "A") == 0) // { // opts->slot2_on = 1; // opts->slot_preference = 1; // } // else //if any other condition, then give no preference to either one // { // opts->slot1_on = 1; // opts->slot2_on = 1; // opts->slot_preference = 2; // } //if TG Hold in place, mute anything but that TG #132 if (state->tg_hold != 0 && state->tg_hold != TGL) encL = 1; if (state->tg_hold != 0 && state->tg_hold != TGR) encR = 1; //likewise, override and unmute if TG hold matches TG (and turn on slot and set preference) if (state->tg_hold != 0 && state->tg_hold == TGL) { encL = 0; opts->slot1_on = 1; opts->slot_preference = 0; } else if (state->tg_hold != 0 && state->tg_hold == TGR) { encR = 0; opts->slot2_on = 1; opts->slot_preference = 1; } else //otherwise, reset slot preference to either or (both slots enabled) { opts->slot_preference = 2; } //test hpf if (opts->use_hpf_d == 1) { hpf_dL(state, state->s_l4[0], 160); hpf_dL(state, state->s_l4[1], 160); hpf_dL(state, state->s_l4[2], 160); hpf_dR(state, state->s_r4[0], 160); hpf_dR(state, state->s_r4[1], 160); hpf_dR(state, state->s_r4[2], 160); } //convert the left or right channel to both channels if single voice under certain conditions, if defined to do so #define DMR_STEREO_OUTPUT #ifdef DMR_STEREO_OUTPUT if (encL) memset (state->s_l4, 0, sizeof(state->s_l4)); if (encR) memset (state->s_r4, 0, sizeof(state->s_r4)); //this is for playing single voice over both channels, or when to keep them seperated if (opts->slot1_on == 0 && opts->slot2_on == 1 && encR == 0) //slot 1 is hard off and slot 2 is on memcpy (state->s_l4, state->s_r4, sizeof(state->s_l4)); //copy right to left else if (opts->slot1_on == 1 && opts->slot2_on == 0 && encL == 0) //slot 2 is hard off and slot 1 is on memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4)); //copy left to right else if (opts->slot_preference == 0 && state->dmrburstL == 16 && encL == 0) //slot 1 is preferred, and voice in slot 1 memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4)); //copy left to right else if (opts->slot_preference == 1 && state->dmrburstR == 16 && encR == 0) //slot 2 is preferred, and voice in slot 2 memcpy (state->s_l4, state->s_r4, sizeof(state->s_l4)); //copy right to left else if (state->dmrburstL == 16 && state->dmrburstR != 16 && encL == 0) //voice in left, no voice in right memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4)); //copy left to right else if (state->dmrburstR == 16 && state->dmrburstL != 16 && encR == 0) //voice in right, no voice in left memcpy (state->s_l4, state->s_r4, sizeof(state->s_l4)); //copy right to left //else if voice in both, and both slots on, and no preference on slot, then regular stereo interleave (left and right channels) //if both slots are the same now, then let's decimate the audio to keep the audio level consistent // if (memcmp (state->s_l4, state->s_r4, sizeof(state->s_l4)) == 0) // { // for (int j = 0; j < 3; j++) // { // for (i = 0; i < 160; i++) // { // state->s_l4[j][i] *= 0.85; // state->s_r4[j][i] *= 0.85; // } // } // } #endif //check this last if (opts->slot1_on == 0 && opts->slot2_on == 0) //both slots are hard off, disable playback { encL = 1; encR = 1; } //at this point, if both channels are still flagged as enc, then we can skip all playback/writing functions if (encL && encR) goto SS3_END; //test hpf // if (opts->use_hpf_d == 1) // { // hpf_dL(state, state->s_l4[0], 160); // hpf_dL(state, state->s_l4[1], 160); // hpf_dL(state, state->s_l4[2], 160); // hpf_dR(state, state->s_r4[0], 160); // hpf_dR(state, state->s_r4[1], 160); // hpf_dR(state, state->s_r4[2], 160); // } //interleave left and right channels from the short storage area for (i = 0; i < 160; i++) { #ifdef DMR_STEREO_OUTPUT #else if (!encL) #endif stereo_samp1[i*2+0] = state->s_l4[0][i]; #ifdef DMR_STEREO_OUTPUT #else if (!encR) #endif stereo_samp1[i*2+1] = state->s_r4[0][i]; } for (i = 0; i < 160; i++) { #ifdef DMR_STEREO_OUTPUT #else if (!encL) #endif stereo_samp2[i*2+0] = state->s_l4[1][i]; #ifdef DMR_STEREO_OUTPUT #else if (!encR) #endif stereo_samp2[i*2+1] = state->s_r4[1][i]; } for (i = 0; i < 160; i++) { #ifdef DMR_STEREO_OUTPUT #else if (!encL) #endif stereo_samp3[i*2+0] = state->s_l4[2][i]; #ifdef DMR_STEREO_OUTPUT #else if (!encR) #endif stereo_samp3[i*2+1] = state->s_r4[2][i]; } if (opts->audio_out_type == 0) //Pulse Audio { pa_simple_write(opts->pulse_digi_dev_out, stereo_samp1, 320*2, NULL); pa_simple_write(opts->pulse_digi_dev_out, stereo_samp2, 320*2, NULL); pa_simple_write(opts->pulse_digi_dev_out, stereo_samp3, 320*2, NULL); } if (opts->audio_out_type == 8) //UDP Audio { udp_socket_blaster (opts, state, 320*2, stereo_samp1); udp_socket_blaster (opts, state, 320*2, stereo_samp2); udp_socket_blaster (opts, state, 320*2, stereo_samp3); } if (opts->audio_out_type == 1 || opts->audio_out_type == 2) //STDOUT or OSS 8k/2channel { write (opts->audio_out_fd, stereo_samp1, 320*2); write (opts->audio_out_fd, stereo_samp2, 320*2); write (opts->audio_out_fd, stereo_samp3, 320*2); } if (opts->wav_out_f != NULL && opts->static_wav_file == 1) { sf_write_short(opts->wav_out_f, stereo_samp1, 320); sf_write_short(opts->wav_out_f, stereo_samp2, 320); sf_write_short(opts->wav_out_f, stereo_samp3, 320); } SS3_END: //run cleanup since we pulled stuff from processAudio state->audio_out_idx = 0; state->audio_out_idxR = 0; //set short temp buffer to baseline memset (state->s_l4, 0, sizeof(state->s_l4)); memset (state->s_r4, 0, sizeof(state->s_r4)); if (state->audio_out_idx2 >= 800000) { state->audio_out_float_buf_p = state->audio_out_float_buf + 100; state->audio_out_buf_p = state->audio_out_buf + 100; memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); memset (state->audio_out_buf, 0, 100 * sizeof (short)); state->audio_out_idx2 = 0; } if (state->audio_out_idx2R >= 800000) { state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100; state->audio_out_buf_pR = state->audio_out_bufR + 100; memset (state->audio_out_float_bufR, 0, 100 * sizeof (float)); memset (state->audio_out_bufR, 0, 100 * sizeof (short)); state->audio_out_idx2R = 0; } } //short stereo mix 4v2 P25p2 void playSynthesizedVoiceSS4 (dsd_opts * opts, dsd_state * state) { //NOTE: This runs once for every two timeslots, if we are in the BS voice loop //it doesn't matter if both slots have voice, or if one does, the slot without voice //will play silence while this runs if no voice present int i; uint8_t encL, encR; short stereo_samp1[320]; //8k 2-channel stereo interleave mix short stereo_samp2[320]; //8k 2-channel stereo interleave mix short stereo_samp3[320]; //8k 2-channel stereo interleave mix short stereo_samp4[320]; short empss[160]; //this is used to see if we want to run HPF on an empty set short empty[320]; //this is used to see if we want to play a single 2v or double 2v or not memset (stereo_samp1, 0, sizeof(stereo_samp1)); memset (stereo_samp2, 0, sizeof(stereo_samp2)); memset (stereo_samp3, 0, sizeof(stereo_samp3)); memset (stereo_samp4, 0, sizeof(stereo_samp4)); memset (empty, 0, sizeof(empty)); memset (empss, 0, sizeof(empss)); //p25p2 enc checkdown for whether or not to fill the stereo sample or not for playback or writing encL = encR = 1; if (state->payload_algid == 0 || state->payload_algid == 0x80) encL = 0; if (state->payload_algidR == 0 || state->payload_algidR == 0x80) encR = 0; //checkdown to see if we can lift the 'mute' if a key is available if (encL) { if (state->payload_algid == 0xAA) { if (state->R != 0) { encL = 0; } } else if (state->payload_algid == 0x81) { if (state->R != 0) { encL = 0; } } else if (state->payload_algid == 0x84 || state->payload_algid == 0x89) { if (state->aes_key_loaded[0] == 1) { encL = 0; } } } if (encR) { if (state->payload_algidR == 0xAA) { if (state->RR != 0) { encR = 0; } } else if (state->payload_algidR == 0x81) { if (state->R != 0) { encR = 0; } } else if (state->payload_algidR == 0x84 || state->payload_algidR == 0x89) { if (state->aes_key_loaded[1] == 1) { encR = 0; } } } //TODO: add option to bypass enc with a toggle as well //CHEAT: Using the slot on/off, use that to set encL or encR back on //as a simple way to turn off voice synthesis in a particular slot //its not really 'disabled', we just aren't playing it if (opts->slot1_on == 0) encL = 1; if (opts->slot2_on == 0) encR = 1; //WIP: Mute if on B list (or not W list) char modeL[8]; sprintf (modeL, "%s", ""); char modeR[8]; sprintf (modeR, "%s", ""); int TGL = state->lasttg; int TGR = state->lasttgR; //if we are using allow/whitelist mode, then write 'B' to mode for block //comparison below will look for an 'A' to write to mode if it is allowed if (opts->trunk_use_allow_list == 1) { sprintf (modeL, "%s", "B"); sprintf (modeR, "%s", "B"); } for (i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == TGL) { strcpy (modeL, state->group_array[i].groupMode); // break; //need to keep going to check other potential slot group } if (state->group_array[i].groupNumber == TGR) { strcpy (modeR, state->group_array[i].groupMode); // break; //need to keep going to check other potential slot group } } //flag either left or right as 'enc' to mute if B if (strcmp(modeL, "B") == 0) encL = 1; if (strcmp(modeR, "B") == 0) encR = 1; //if TG Hold in place, mute anything but that TG #132 if (state->tg_hold != 0 && state->tg_hold != TGL) encL = 1; if (state->tg_hold != 0 && state->tg_hold != TGR) encR = 1; //likewise, override and unmute if TG hold matches TG if (state->tg_hold != 0 && state->tg_hold == TGL) encL = 0; if (state->tg_hold != 0 && state->tg_hold == TGR) encR = 0; //test hpf if (opts->use_hpf_d == 1) { hpf_dL(state, state->s_l4[0], 160); hpf_dL(state, state->s_l4[1], 160); if (memcmp(empss, state->s_l4[2], sizeof(empss)) != 0) hpf_dL(state, state->s_l4[2], 160); if (memcmp(empss, state->s_l4[3], sizeof(empss)) != 0) hpf_dL(state, state->s_l4[3], 160); hpf_dR(state, state->s_r4[0], 160); hpf_dR(state, state->s_r4[1], 160); if (memcmp(empss, state->s_r4[2], sizeof(empss)) != 0) hpf_dR(state, state->s_r4[2], 160); if (memcmp(empss, state->s_r4[3], sizeof(empss)) != 0) hpf_dR(state, state->s_r4[3], 160); } //interleave left and right channels from the short storage area for (i = 0; i < 160; i++) { if (!encL) stereo_samp1[i*2+0] = state->s_l4[0][i]; if (!encR) stereo_samp1[i*2+1] = state->s_r4[0][i]; } for (i = 0; i < 160; i++) { if (!encL) stereo_samp2[i*2+0] = state->s_l4[1][i]; if (!encR) stereo_samp2[i*2+1] = state->s_r4[1][i]; } for (i = 0; i < 160; i++) { if (!encL) stereo_samp3[i*2+0] = state->s_l4[2][i]; if (!encR) stereo_samp3[i*2+1] = state->s_r4[2][i]; } for (i = 0; i < 160; i++) { if (!encL) stereo_samp4[i*2+0] = state->s_l4[3][i]; if (!encR) stereo_samp4[i*2+1] = state->s_r4[3][i]; } //at this point, if both channels are still flagged as enc, then we can skip all playback/writing functions if (encL && encR) goto SS4_END; if (opts->audio_out_type == 0) //Pulse Audio { pa_simple_write(opts->pulse_digi_dev_out, stereo_samp1, 320*2, NULL); pa_simple_write(opts->pulse_digi_dev_out, stereo_samp2, 320*2, NULL); //only play these two if not a single 2v or double 2v (minor skip can still occur on a 4v and 2v combo, but will probably only be perceivable if one is a tone) if (memcmp(empty, stereo_samp3, sizeof(empty)) != 0) pa_simple_write(opts->pulse_digi_dev_out, stereo_samp3, 320*2, NULL); if (memcmp(empty, stereo_samp4, sizeof(empty)) != 0) pa_simple_write(opts->pulse_digi_dev_out, stereo_samp4, 320*2, NULL); } if (opts->audio_out_type == 8) //UDP Audio { udp_socket_blaster (opts, state, 320*2, stereo_samp1); udp_socket_blaster (opts, state, 320*2, stereo_samp2); //only play these two if not a single 2v or double 2v (minor skip can still occur on a 4v and 2v combo, but will probably only be perceivable if one is a tone) if (memcmp(empty, stereo_samp3, sizeof(empty)) != 0) udp_socket_blaster (opts, state, 320*2, stereo_samp3); if (memcmp(empty, stereo_samp4, sizeof(empty)) != 0) udp_socket_blaster (opts, state, 320*2, stereo_samp4); } if (opts->audio_out_type == 1 || opts->audio_out_type == 2) //STDOUT or OSS 8k/2channel { write (opts->audio_out_fd, stereo_samp1, 320*2); write (opts->audio_out_fd, stereo_samp2, 320*2); //only play these two if not a single 2v or double 2v if (memcmp(empty, stereo_samp3, sizeof(empty)) != 0) write (opts->audio_out_fd, stereo_samp3, 320*2); if (memcmp(empty, stereo_samp4, sizeof(empty)) != 0) write (opts->audio_out_fd, stereo_samp4, 320*2); } if (opts->wav_out_f != NULL && opts->static_wav_file == 1) { sf_write_short(opts->wav_out_f, stereo_samp1, 320); sf_write_short(opts->wav_out_f, stereo_samp2, 320); //only write these two if not a single 2v or double 2v if (memcmp(empty, stereo_samp3, sizeof(empty)) != 0) sf_write_short(opts->wav_out_f, stereo_samp3, 320); if (memcmp(empty, stereo_samp4, sizeof(empty)) != 0) sf_write_short(opts->wav_out_f, stereo_samp4, 320); } SS4_END: //run cleanup since we pulled stuff from processAudio state->audio_out_idx = 0; state->audio_out_idxR = 0; //set short temp buffer to baseline memset (state->s_l4, 0, sizeof(state->s_l4)); memset (state->s_r4, 0, sizeof(state->s_r4)); if (state->audio_out_idx2 >= 800000) { state->audio_out_float_buf_p = state->audio_out_float_buf + 100; state->audio_out_buf_p = state->audio_out_buf + 100; memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); memset (state->audio_out_buf, 0, 100 * sizeof (short)); state->audio_out_idx2 = 0; } if (state->audio_out_idx2R >= 800000) { state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100; state->audio_out_buf_pR = state->audio_out_bufR + 100; memset (state->audio_out_float_bufR, 0, 100 * sizeof (float)); memset (state->audio_out_bufR, 0, 100 * sizeof (short)); state->audio_out_idx2R = 0; } } //short stereo mix 18v superframe void playSynthesizedVoiceSS18 (dsd_opts * opts, dsd_state * state) { //NOTE: This will run once every superframe during a sacch field //exact implementation to be determined int i, j; uint8_t encL, encR; short stereo_sf[18][320]; //8k 2-channel stereo interleave mix for full superframe // memset (stereo_sf, 1, 18*sizeof(short)); //I don't think 18*sizeof(short) was large enough, should probably be 18*320*sizeof(short) memset (stereo_sf, 0, sizeof(stereo_sf)); short empty[320]; memset (empty, 0, sizeof(empty)); //p25p2 enc checkdown for whether or not to fill the stereo sample or not for playback or writing encL = encR = 1; if (state->payload_algid == 0 || state->payload_algid == 0x80) encL = 0; if (state->payload_algidR == 0 || state->payload_algidR == 0x80) encR = 0; //checkdown to see if we can lift the 'mute' if a key is available if (encL) { //RC4 or DES if (state->payload_algid == 0xAA || state->payload_algid == 0x81) { if (state->R != 0) { encL = 0; } } //AES if (state->payload_algid == 0x84 || state->payload_algid == 0x89) { if (state->aes_key_loaded[0] == 1) { encL = 0; } } } if (encR) { //RC4 or DES if (state->payload_algidR == 0xAA || state->payload_algidR == 0x81) { if (state->RR != 0) { encR = 0; } } //AES if (state->payload_algidR == 0x84 || state->payload_algidR == 0x89) { if (state->aes_key_loaded[1] == 1) { encR = 0; } } } //debug, unmute both slots // encL = 0; // encR = 0; //WIP: Mute if on B list (or not W list) char modeL[8]; sprintf (modeL, "%s", ""); char modeR[8]; sprintf (modeR, "%s", ""); int TGL = state->lasttg; int TGR = state->lasttgR; //if we are using allow/whitelist mode, then write 'B' to mode for block //comparison below will look for an 'A' to write to mode if it is allowed if (opts->trunk_use_allow_list == 1) { sprintf (modeL, "%s", "B"); sprintf (modeR, "%s", "B"); } for (i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == TGL) { strcpy (modeL, state->group_array[i].groupMode); // break; //need to keep going to check other potential slot group } if (state->group_array[i].groupNumber == TGR) { strcpy (modeR, state->group_array[i].groupMode); // break; //need to keep going to check other potential slot group } } //flag either left or right as 'enc' to mute if B if (strcmp(modeL, "B") == 0) encL = 1; if (strcmp(modeR, "B") == 0) encR = 1; //check to see if we need to enable slot and toggle slot preference here //this method will always favor slot 2 (this is a patch anyways, so....meh) // if (strcmp(modeL, "A") == 0) // { // opts->slot1_on = 1; // opts->slot_preference = 0; // } // if (strcmp(modeR, "A") == 0) // { // opts->slot2_on = 1; // opts->slot_preference = 1; // } //check to see if we need to enable slot and toggle slot preference here //if both groups allowed, then give no preference to either one (not sure this is needed now) // if ( (strcmp(modeL, "A") == 0) && (strcmp(modeR, "A") == 0) ) // { // opts->slot1_on = 1; // opts->slot2_on = 1; // opts->slot_preference = 2; // } // else if (strcmp(modeL, "A") == 0) // { // opts->slot1_on = 1; // opts->slot_preference = 0; // } // else if (strcmp(modeR, "A") == 0) // { // opts->slot2_on = 1; // opts->slot_preference = 1; // } // else //if any other condition, then give no preference to either one // { // opts->slot1_on = 1; // opts->slot2_on = 1; // opts->slot_preference = 2; // } //if TG Hold in place, mute anything but that TG #132 if (state->tg_hold != 0 && state->tg_hold != TGL) encL = 1; if (state->tg_hold != 0 && state->tg_hold != TGR) encR = 1; //likewise, override and unmute if TG hold matches TG (and turn on slot and set preference) if (state->tg_hold != 0 && state->tg_hold == TGL) { encL = 0; opts->slot1_on = 1; opts->slot_preference = 0; } else if (state->tg_hold != 0 && state->tg_hold == TGR) { encR = 0; opts->slot2_on = 1; opts->slot_preference = 1; } else //otherwise, reset slot preference to either or (both slots enabled) { opts->slot_preference = 2; } //run hpf_d filter, if enabled if (opts->use_hpf_d == 1) { for (j = 0; j < 18; j++) { hpf_dL(state, state->s_l4[j], 160); hpf_dR(state, state->s_r4[j], 160); } } //convert the left or right channel to both channels if single voice under certain conditions, if defined to do so #define P2_STEREO_OUTPUT #ifdef P2_STEREO_OUTPUT if (encL) memset (state->s_l4, 0, sizeof(state->s_l4)); if (encR) memset (state->s_r4, 0, sizeof(state->s_r4)); //this is for playing single voice over both channels, or when to keep them seperated if (opts->slot1_on == 0 && opts->slot2_on == 1 && encR == 0) //slot 1 is hard off and slot 2 is on memcpy (state->s_l4, state->s_r4, sizeof(state->s_l4)); //copy right to left else if (opts->slot1_on == 1 && opts->slot2_on == 0 && encL == 0) //slot 2 is hard off and slot 1 is on memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4)); //copy left to right else if (opts->slot_preference == 0 && state->dmrburstL == 21 && encL == 0) //slot 1 is preferred, and voice in slot 1 memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4)); //copy left to right else if (opts->slot_preference == 1 && state->dmrburstR == 21 && encR == 0) //slot 2 is preferred, and voice in slot 2 memcpy (state->s_l4, state->s_r4, sizeof(state->s_l4)); //copy right to left else if (state->dmrburstL == 21 && state->dmrburstR != 21 && encL == 0) //voice in left, no voice in right memcpy (state->s_r4, state->s_l4, sizeof(state->s_r4)); //copy left to right else if (state->dmrburstR == 21 && state->dmrburstL != 21 && encR == 0) //voice in right, no voice in left memcpy (state->s_l4, state->s_r4, sizeof(state->s_l4)); //copy right to left //else if voice in both, and both slots on, and no preference on slot, then regular stereo interleave (left and right channels) //if both slots are the same now, then let's decimate the audio to keep the audio level consistent // if (memcmp (state->s_l4, state->s_r4, sizeof(state->s_l4)) == 0) // { // for (j = 0; j < 18; j++) // { // for (i = 0; i < 160; i++) // { // state->s_l4[j][i] *= 0.85; // state->s_r4[j][i] *= 0.85; // } // } // } #endif //check this last if (opts->slot1_on == 0 && opts->slot2_on == 0) //both slots are hard off, disable playback { encL = 1; encR = 1; } //at this point, if both channels are still flagged as enc, then we can skip all playback/writing functions if (encL && encR) goto SS18_END; //interleave left and right channels from the short storage area for (j = 0; j < 18; j++) { for (i = 0; i < 160; i++) { #ifdef P2_STEREO_OUTPUT #else if (!encL) #endif stereo_sf[j][i*2+0] = state->s_l4[j][i]; #ifdef P2_STEREO_OUTPUT #else if (!encR) #endif stereo_sf[j][i*2+1] = state->s_r4[j][i]; } } if (opts->audio_out_type == 0) //Pulse Audio { for (j = 0; j < 18; j++) { if (memcmp(empty, stereo_sf[j], sizeof(empty)) != 0) //may not work as intended because its stereo and one will have something in it most likely pa_simple_write(opts->pulse_digi_dev_out, stereo_sf[j], 320*2, NULL); } } if (opts->audio_out_type == 8) //UDP Audio { for (j = 0; j < 18; j++) { if (memcmp(empty, stereo_sf[j], sizeof(empty)) != 0) //may not work as intended because its stereo and one will have something in it most likely udp_socket_blaster (opts, state, 320*2, stereo_sf[j]); } } if (opts->audio_out_type == 1 || opts->audio_out_type == 2) //STDOUT or OSS 8k/2channel { for (j = 0; j < 18; j++) { if (memcmp(empty, stereo_sf[j], sizeof(empty)) != 0) //may not work as intended because its stereo and one will have something in it most likely write (opts->audio_out_fd, stereo_sf[j], 320*2); } } if (opts->wav_out_f != NULL && opts->static_wav_file == 1) { for (j = 0; j < 18; j++) { if (memcmp(empty, stereo_sf[j], sizeof(empty)) != 0) sf_write_short(opts->wav_out_f, stereo_sf[j], 320); } } SS18_END: //run cleanup since we pulled stuff from processAudio state->audio_out_idx = 0; state->audio_out_idxR = 0; //set float temp buffer to baseline memset (state->s_l4, 0, sizeof(state->s_l4)); memset (state->s_r4, 0, sizeof(state->s_r4)); if (state->audio_out_idx2 >= 800000) { state->audio_out_float_buf_p = state->audio_out_float_buf + 100; state->audio_out_buf_p = state->audio_out_buf + 100; memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); memset (state->audio_out_buf, 0, 100 * sizeof (short)); state->audio_out_idx2 = 0; } if (state->audio_out_idx2R >= 800000) { state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100; state->audio_out_buf_pR = state->audio_out_bufR + 100; memset (state->audio_out_float_bufR, 0, 100 * sizeof (float)); memset (state->audio_out_bufR, 0, 100 * sizeof (short)); state->audio_out_idx2R = 0; } } //largely borrowed from Boatbod OP25 (simplified single tone ID version) void soft_tonef (float samp[160], int n, int ID, int AD) { int i; float step1, step2, amplitude, freq1, freq2; float gain = 1.0f; //needs opts and state to work....don't feel like doing the changes // if (opts->audio_gain != 0 && slot == 0) // gain = state->aout_gain / 25.0f; // Synthesize tones freq1 = 31.25 * ID; freq2 = freq1; step1 = 2 * M_PI * freq1 / 8000.0f; step2 = 2 * M_PI * freq2 / 8000.0f; amplitude = AD * 75.0f; for (i = 0; i < 160; i++) { samp[i] = (float) ( amplitude * (sin((n) * step1)/2 + sin((n) * step2)/2) ); samp[i] /= 8000.0f; samp[i] *= gain; n++; } } //older version, does better at normalizing audio, but also sounds 'flatter' and 'muddier' //probably too much compression and adjustments on the samples (tones have a slight tremolo effect) //Remus, enable this one and disable the one above if you prefer void agf (dsd_opts * opts, dsd_state * state, float samp[160], int slot) { int i, j, run; run = 1; float empty[160]; memset (empty, 0.0f, sizeof(empty)); float mmax = 0.90f; float mmin = -0.90f; float aavg = 0.0f; //average of the absolute value float df; //decimation value df = 3277.0f; //test value //trying things float gain = 1.0f; //test increasing gain on DMR EP samples with degraded AMBE samples if (state->payload_algid == 0x21 || state->payload_algidR == 0x21) gain = 1.75f; if (opts->audio_gain != 0) gain = opts->audio_gain / 25.0f; //this comparison is to determine whether or not to run gain on 'empty' samples (2v last 2, silent frames, etc) if (memcmp(empty, samp, sizeof(empty)) == 0) run = 0; if (run == 0) goto AGF_END; for (j = 0; j < 8; j++) { if (slot == 0) df = 384.0f * (50.0f - state->aout_gain); if (slot == 1) df = 384.0f * (50.0f - state->aout_gainR); for (i = 0; i < 20; i++) { samp[(j*20)+i] = samp[(j*20)+i] / df; // samp[(j*20)+i] *= gain; // aavg += fabsf(samp[i]); //simple clipping if (samp[(j*20)+i] > mmax) samp[(j*20)+i] = mmax; if (samp[(j*20)+i] < mmin) samp[(j*20)+i] = mmin; aavg += fabsf(samp[i]); samp[(j*20)+i] *= gain * 0.8f; } //i loop aavg /= 20.0f; //average of the 20 samples //debug // fprintf (stderr, "\nS%d - DF = %f AAVG = %f", slot, df, aavg); if (slot == 0) { if (aavg < 0.075f && state->aout_gain < 46.0f) state->aout_gain += 0.5f; if (aavg >= 0.075f && state->aout_gain > 1.0f) state->aout_gain -= 0.5f; } if (slot == 1) { if (aavg < 0.075f && state->aout_gainR < 46.0f) state->aout_gainR += 0.5f; if (aavg >= 0.075f && state->aout_gainR > 1.0f) state->aout_gainR -= 0.5f; } aavg = 0.0f; //reset } //j loop AGF_END: ; //do nothing } //automatic gain short mono for analog audio and some digital mono (WIP) void agsm (dsd_opts * opts, dsd_state * state, short * input, int len) { int i; UNUSED(opts); //NOTE: This seems to be doing better now that I got it worked out properly //This may produce a mild buzz sound though on the low end float avg = 0.0f; //average of 20 samples float coeff = 0.0f; //gain coeffiecient float max = 0.0f; //the highest sample value float nom = 4800.0f; //nominator value for 48k float samp[960]; memset (samp, 0.0f, 960*sizeof(float)); //assign internal float from short input for (i = 0; i < len; i++) samp[i] = (float)input[i]; for (i = 0; i < len; i++) { if ( fabsf (samp[i]) > max) { max = fabsf (samp[i]); } } for (i = 0; i < len; i++) avg += (float)samp[i]; avg /= (float)len; coeff = fabsf (nom / max); //keep coefficient with tolerable range when silence to prevent crackle/buzz if (coeff > 3.0f) coeff = 3.0f; //apply the coefficient to bring the max value to our desired maximum value for (i = 0; i < 20; i++) samp[i] *= coeff; //debug // fprintf (stderr, "\n M: %f; C: %f; A: %f; ", max, coeff, avg); // debug // for (i = 0; i < len; i++) // { // fprintf (stderr, " in: %d", input[i]); // fprintf (stderr, " out: %f", samp[i]); // } //return new smaple values post agc for (i = 0; i < len; i++) input[i] = (short)samp[i]; state->aout_gainA = coeff; //store for internal use UNUSED(avg); } //until analog agc is fixed, going to use a manual gain control on this void analog_gain (dsd_opts * opts, dsd_state * state, short * input, int len) { int i; UNUSED(state); float gain = (opts->audio_gainA / 100.0f) * 5.0f; //scale 0x - 5x for (i = 0; i < len; i++) input[i] *= gain; } void beeper (dsd_opts * opts, dsd_state * state, int lr, int id, int ad, int len) { UNUSED(state); int i, j, n; //use lr as left or right channel designation in stereo config float samp_f[160]; //mono float sample float samp_fs[320]; //stereo float sample short samp_s[160]; //mono short sample short samp_ss[320]; //stereo short sample short samp_su[960]; //mono short upsample short outbuf[6]; //temp storage for upsample n = 0; //rolling sine wave 'degree' //double len if not using Pulse Audio, //anything over UDP or using OSS may //not clear the buffer at the shorter len if (opts->audio_out_type != 0) len *= 2; //each j increment is 20 ms at 160 samples / 8 kHz for (j = 0; j < len; j++) { //'zero' out stereo mix samples memset (samp_fs, 0, sizeof(samp_fs)); memset (samp_ss, 0, sizeof(samp_ss)); //generate a tone with supplied tone ID and AD value soft_tonef(samp_f, n, id, ad); //convert float to short if required if (opts->floating_point == 0) { mbe_floattoshort(samp_f, samp_s); for (i = 0; i < 160; i++) { samp_s[i] *= 4000; //apply gain samp_ss[(i*2)+lr] = samp_s[i]; } } //load returned tone sample into appropriate channel -- left = +0; right = +1; for (i = 0; i < 160; i++) samp_fs[(i*2)+lr] = samp_f[i]; //play sample 3 times (20ms x 3 = 60ms) if (opts->audio_out_type == 0) //Pulse Audio { if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1) pa_simple_write(opts->pulse_digi_dev_out, samp_fs, 320*4, NULL); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1) pa_simple_write(opts->pulse_digi_dev_out, samp_f, 160*4, NULL); if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0) pa_simple_write(opts->pulse_digi_dev_out, samp_ss, 320*2, NULL); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) pa_simple_write(opts->pulse_digi_dev_out, samp_s, 160*2, NULL); } else if (opts->audio_out_type == 8) //UDP Audio { if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1) udp_socket_blaster (opts, state, 320*4, samp_fs); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1) udp_socket_blaster (opts, state, 160*4, samp_f); if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0) udp_socket_blaster (opts, state, 320*2, samp_ss); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) udp_socket_blaster (opts, state, 160*2, samp_s); } else if (opts->audio_out_type == 1) //STDOUT { if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1) write(opts->audio_out_fd, samp_fs, 320*4); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1) write(opts->audio_out_fd, samp_f, 160*4); if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0) write(opts->audio_out_fd, samp_ss, 320*2); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) write(opts->audio_out_fd, samp_s, 160*2); } else if (opts->audio_out_type == 2) //OSS Variable Output (no float) { if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0) write(opts->audio_out_fd, samp_ss, 320*2); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) write(opts->audio_out_fd, samp_s, 160*2); } else if (opts->audio_out_type == 5) //OSS 48k/1 configuration with upsample { short prev = 0; for (i = 0; i < 160; i++) { upsampleS (samp_s[i], prev, outbuf); samp_su[(i*6)+0] = outbuf[0]; samp_su[(i*6)+1] = outbuf[1]; samp_su[(i*6)+2] = outbuf[2]; samp_su[(i*6)+3] = outbuf[3]; samp_su[(i*6)+4] = outbuf[4]; samp_su[(i*6)+5] = outbuf[5]; } write (opts->audio_out_fd, samp_su, 960*2); } } }