/* * Copyright (C) 2010 DSD Author * GPG Key ID: 0x3F1D7FD0 (74EF 430D F7F2 0A48 FCE6 F630 FAA2 635D 3F1D 7FD0) * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "dsd.h" void processAudio (dsd_opts * opts, dsd_state * state) { int i, n; float aout_abs, max, gainfactor, gaindelta, maxbuf; if (opts->audio_gain == (float) 0) { // detect max level max = 0; state->audio_out_temp_buf_p = state->audio_out_temp_buf; for (n = 0; n < 160; n++) { aout_abs = fabsf (*state->audio_out_temp_buf_p); if (aout_abs > max) { max = aout_abs; } state->audio_out_temp_buf_p++; } *state->aout_max_buf_p = max; state->aout_max_buf_p++; state->aout_max_buf_idx++; if (state->aout_max_buf_idx > 24) { state->aout_max_buf_idx = 0; state->aout_max_buf_p = state->aout_max_buf; } // lookup max history for (i = 0; i < 25; i++) { maxbuf = state->aout_max_buf[i]; if (maxbuf > max) { max = maxbuf; } } // determine optimal gain level if (max > (float) 0) { gainfactor = ((float) 30000 / max); } else { gainfactor = (float) 50; } if (gainfactor < state->aout_gain) { state->aout_gain = gainfactor; gaindelta = (float) 0; } else { if (gainfactor > (float) 50) { gainfactor = (float) 50; } gaindelta = gainfactor - state->aout_gain; if (gaindelta > ((float) 0.05 * state->aout_gain)) { gaindelta = ((float) 0.05 * state->aout_gain); } } gaindelta /= (float) 160; } else { gaindelta = (float) 0; } if(opts->audio_gain >= 0) { // adjust output gain state->audio_out_temp_buf_p = state->audio_out_temp_buf; for (n = 0; n < 160; n++) { *state->audio_out_temp_buf_p = (state->aout_gain + ((float) n * gaindelta)) * (*state->audio_out_temp_buf_p); state->audio_out_temp_buf_p++; } state->aout_gain += ((float) 160 * gaindelta); } // copy audio data to output buffer and upsample if necessary state->audio_out_temp_buf_p = state->audio_out_temp_buf; if (opts->split == 0) { for (n = 0; n < 160; n++) { upsample (state, *state->audio_out_temp_buf_p); state->audio_out_temp_buf_p++; state->audio_out_float_buf_p += 6; state->audio_out_idx += 6; state->audio_out_idx2 += 6; } state->audio_out_float_buf_p -= (960 + opts->playoffset); // copy to output (short) buffer for (n = 0; n < 960; n++) { if (*state->audio_out_float_buf_p > 32767.0F) { *state->audio_out_float_buf_p = 32767.0F; } else if (*state->audio_out_float_buf_p < -32768.0F) { *state->audio_out_float_buf_p = -32768.0F; } *state->audio_out_buf_p = (short) *state->audio_out_float_buf_p; state->audio_out_buf_p++; state->audio_out_float_buf_p++; } state->audio_out_float_buf_p += opts->playoffset; } else { for (n = 0; n < 160; n++) { if (*state->audio_out_temp_buf_p > 32767.0F) { *state->audio_out_temp_buf_p = 32767.0F; } else if (*state->audio_out_temp_buf_p < -32768.0F) { *state->audio_out_temp_buf_p = -32768.0F; } *state->audio_out_buf_p = (short) *state->audio_out_temp_buf_p; state->audio_out_buf_p++; state->audio_out_temp_buf_p++; state->audio_out_idx++; state->audio_out_idx2++; } } } void writeSynthesizedVoice (dsd_opts * opts, dsd_state * state) { int n; short aout_buf[160]; short *aout_buf_p; // for(n=0; n<160; n++) // printf("%d ", ((short*)(state->audio_out_temp_buf))[n]); // printf("\n"); aout_buf_p = aout_buf; state->audio_out_temp_buf_p = state->audio_out_temp_buf; for (n = 0; n < 160; n++) { if (*state->audio_out_temp_buf_p > (float) 32767) { *state->audio_out_temp_buf_p = (float) 32767; } else if (*state->audio_out_temp_buf_p < (float) -32768) { *state->audio_out_temp_buf_p = (float) -32768; } *aout_buf_p = (short) *state->audio_out_temp_buf_p; aout_buf_p++; state->audio_out_temp_buf_p++; } sf_write_short(opts->wav_out_f, aout_buf, 160); /* int n; short aout_buf[160]; short *aout_buf_p; ssize_t result; aout_buf_p = aout_buf; state->audio_out_temp_buf_p = state->audio_out_temp_buf; for (n = 0; n < 160; n++) { if (*state->audio_out_temp_buf_p > (float) 32760) { *state->audio_out_temp_buf_p = (float) 32760; } else if (*state->audio_out_temp_buf_p < (float) -32760) { *state->audio_out_temp_buf_p = (float) -32760; } *aout_buf_p = (short) *state->audio_out_temp_buf_p; aout_buf_p++; state->audio_out_temp_buf_p++; } result = write (opts->wav_out_fd, aout_buf, 320); fflush (opts->wav_out_f); state->wav_out_bytes += 320; */ } //Test playing raw audio HERE //get_rtlsdr_sample(&sample); void playRawAudio (dsd_opts * opts, dsd_state * state) { ssize_t result; //if (state->audio_out_idx > opts->delay) if (opts->audio_in_type == 3) //if set to rtl_fm, maybe it shouldn't matter but for now, using this { // output synthesized speech to sound card if(opts->audio_out_type == 2) { #ifdef USE_PORTAUDIO PaError err = paNoError; do { long available = Pa_GetStreamWriteAvailable( opts->audio_out_pa_stream ); if(available < 0) err = available; //printf("Frames available: %d\n", available); if( err != paNoError ) break; if(available > SAMPLE_RATE_OUT * PA_LATENCY_OUT) { //It looks like this might not be needed for very small latencies. However, it's definitely needed for a bit larger ones. //When PA_LATENCY_OUT == 0.500 I get output buffer underruns if I don't use this. With PA_LATENCY_OUT <= 0.100 I don't see those happen. //But with PA_LATENCY_OUT < 0.100 I run the risk of choppy audio and stream errors. printf("\nSyncing voice output stream\n"); err = Pa_StopStream( opts->audio_out_pa_stream ); if( err != paNoError ) break; } err = Pa_IsStreamActive( opts->audio_out_pa_stream ); if(err == 0) { printf("Start voice output stream\n"); err = Pa_StartStream( opts->audio_out_pa_stream ); } else if(err == 1) { err = paNoError; } if( err != paNoError ) break; //printf("write stream %d\n", state->audio_out_idx); err = Pa_WriteStream( opts->audio_out_pa_stream, (state->audio_out_buf_p - state->audio_out_idx), state->audio_out_idx ); if( err != paNoError ) break; } while(0); if( err != paNoError ) { fprintf( stderr, "An error occured while using the portaudio output stream\n" ); fprintf( stderr, "Error number: %d\n", err ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); } #endif } else result = write (opts->audio_out_fd, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2)); state->audio_out_idx = 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; } } //end test raw audio void playSynthesizedVoice (dsd_opts * opts, dsd_state * state) { ssize_t result; if (state->audio_out_idx > opts->delay) { // output synthesized speech to sound card if(opts->audio_out_type == 2) { #ifdef USE_PORTAUDIO PaError err = paNoError; do { long available = Pa_GetStreamWriteAvailable( opts->audio_out_pa_stream ); if(available < 0) err = available; //printf("Frames available: %d\n", available); if( err != paNoError ) break; if(available > SAMPLE_RATE_OUT * PA_LATENCY_OUT) { //It looks like this might not be needed for very small latencies. However, it's definitely needed for a bit larger ones. //When PA_LATENCY_OUT == 0.500 I get output buffer underruns if I don't use this. With PA_LATENCY_OUT <= 0.100 I don't see those happen. //But with PA_LATENCY_OUT < 0.100 I run the risk of choppy audio and stream errors. printf("\nSyncing voice output stream\n"); err = Pa_StopStream( opts->audio_out_pa_stream ); if( err != paNoError ) break; } err = Pa_IsStreamActive( opts->audio_out_pa_stream ); if(err == 0) { printf("Start voice output stream\n"); err = Pa_StartStream( opts->audio_out_pa_stream ); } else if(err == 1) { err = paNoError; } if( err != paNoError ) break; //printf("write stream %d\n", state->audio_out_idx); err = Pa_WriteStream( opts->audio_out_pa_stream, (state->audio_out_buf_p - state->audio_out_idx), state->audio_out_idx ); if( err != paNoError ) break; } while(0); if( err != paNoError ) { fprintf( stderr, "An error occured while using the portaudio output stream\n" ); fprintf( stderr, "Error number: %d\n", err ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); } #endif } else result = write (opts->audio_out_fd, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2)); state->audio_out_idx = 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; } } #ifdef USE_PORTAUDIO int getPADevice(char* dev, int input, PaStream** stream) { int devnum = atoi(dev + 3); printf("Using portaudio device %d.\n", devnum); PaError err; int numDevices = Pa_GetDeviceCount(); if( numDevices < 0 ) { printf( "ERROR: Pa_GetDeviceCount returned 0x%x\n", numDevices ); err = numDevices; goto error; } if( devnum >= numDevices) { printf( "ERROR: Requested device %d is larger than number of devices.\n", devnum ); return(1); } const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo( devnum ); /* print device name */ #ifdef WIN32 { /* Use wide char on windows, so we can show UTF-8 encoded device names */ wchar_t wideName[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, deviceInfo->name, -1, wideName, MAX_PATH-1); wprintf( L"Name = %s\n", wideName ); } #else printf( "Name = %s\n", deviceInfo->name ); #endif if((input == 1) && (deviceInfo->maxInputChannels == 0)) { printf( "ERROR: Requested device %d is not an input device.\n", devnum ); return(1); } if((input == 0) && (deviceInfo->maxOutputChannels == 0)) { printf( "ERROR: Requested device %d is not an output device.\n", devnum ); return(1); } //Create stream parameters PaStreamParameters parameters; parameters.device = devnum; parameters.channelCount = 1; /* mono */ parameters.sampleFormat = paInt16; //Shorts parameters.suggestedLatency = (input == 1) ? PA_LATENCY_IN : PA_LATENCY_OUT; parameters.hostApiSpecificStreamInfo = NULL; //Open stream err = Pa_OpenStream( stream, (input == 1) ? ¶meters : NULL, (input == 0) ? ¶meters : NULL, (input == 1) ? SAMPLE_RATE_IN : SAMPLE_RATE_OUT, PA_FRAMES_PER_BUFFER, paClipOff, NULL /*callback*/, NULL ); if( err != paNoError ) goto error; return 0; error: fprintf( stderr, "An error occured while initializing a portaudio stream\n" ); fprintf( stderr, "Error number: %d\n", err ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); return err; } #endif void openAudioOutDevice (dsd_opts * opts, int speed) { // get info of device/file if(strncmp(opts->audio_out_dev, "pa:", 3) == 0) { opts->audio_out_type = 2; #ifdef USE_PORTAUDIO int err = getPADevice(opts->audio_out_dev, 0, &opts->audio_out_pa_stream); if(err != 0) exit(err); #else printf("Error, Portaudio support not compiled.\n"); exit(1); #endif } else { struct stat stat_buf; if(stat(opts->audio_out_dev, &stat_buf) != 0) { printf("Error, couldn't open %s\n", opts->audio_out_dev); exit(1); } if( !(S_ISCHR(stat_buf.st_mode) || S_ISBLK(stat_buf.st_mode))) { // this is not a device printf("Error, %s is not a device. use -w filename for wav output.\n", opts->audio_out_dev); exit(1); } #ifdef SOLARIS sample_info_t aset, aget; opts->audio_out_fd = open (opts->audio_out_dev, O_WRONLY); if (opts->audio_out_fd == -1) { printf ("Error, couldn't open %s\n", opts->audio_out_dev); exit (1); } // get current ioctl (opts->audio_out_fd, AUDIO_GETINFO, &aset); aset.record.sample_rate = speed; aset.play.sample_rate = speed; aset.record.channels = 1; aset.play.channels = 1; aset.record.precision = 16; aset.play.precision = 16; aset.record.encoding = AUDIO_ENCODING_LINEAR; aset.play.encoding = AUDIO_ENCODING_LINEAR; if (ioctl (opts->audio_out_fd, AUDIO_SETINFO, &aset) == -1) { printf ("Error setting sample device parameters\n"); exit (1); } #endif #if defined(BSD) && !defined(__APPLE__) int fmt; opts->audio_out_fd = open (opts->audio_out_dev, O_WRONLY); if (opts->audio_out_fd == -1) { printf ("Error, couldn't open %s\n", opts->audio_out_dev); opts->audio_out = 0; exit(1); } fmt = 0; if (ioctl (opts->audio_out_fd, SNDCTL_DSP_RESET) < 0) { printf ("ioctl reset error \n"); } fmt = speed; if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SPEED, &fmt) < 0) { printf ("ioctl speed error \n"); } fmt = 0; if (ioctl (opts->audio_out_fd, SNDCTL_DSP_STEREO, &fmt) < 0) { printf ("ioctl stereo error \n"); } fmt = AFMT_S16_LE; if (ioctl (opts->audio_out_fd, SNDCTL_DSP_SETFMT, &fmt) < 0) { printf ("ioctl setfmt error \n"); } #endif } printf ("Audio Out Device: %s\n", opts->audio_out_dev); } void openAudioInDevice (dsd_opts * opts) { // get info of device/file if(strncmp(opts->audio_in_dev, "-", 1) == 0) { opts->audio_in_type = 1; opts->audio_in_file_info = calloc(1, sizeof(SF_INFO)); opts->audio_in_file_info->samplerate=48000; opts->audio_in_file_info->channels=1; opts->audio_in_file_info->seekable=0; opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE; opts->audio_in_file = sf_open_fd(fileno(stdin), SFM_READ, opts->audio_in_file_info, 0); if(opts->audio_in_file == NULL) { printf ("Error, couldn't open stdin with libsndfile: %s\n", sf_strerror(NULL)); exit(1); } } else if(strncmp(opts->audio_in_dev, "pa:", 2) == 0) { opts->audio_in_type = 2; #ifdef USE_PORTAUDIO int err = getPADevice(opts->audio_in_dev, 1, &opts->audio_in_pa_stream); if(err != 0) exit(err); if (opts->split == 0) { int err = getPADevice(opts->audio_in_dev, 0, &opts->audio_out_pa_stream); if(err != 0) exit(err); } #else printf("Error, Portaudio support not compiled.\n"); exit(1); #endif } else if(strncmp(opts->audio_in_dev, "rtl", 3) == 0) { opts->audio_in_type = 3; } else { struct stat stat_buf; if (stat(opts->audio_in_dev, &stat_buf) != 0) { printf("Error, couldn't open %s\n", opts->audio_in_dev); exit(1); } if (S_ISREG(stat_buf.st_mode)) { // is this a regular file? then process with libsndfile. opts->audio_in_type = 1; opts->audio_in_file_info = calloc(1, sizeof(SF_INFO)); opts->audio_in_file_info->channels = 1; opts->audio_in_file = sf_open(opts->audio_in_dev, SFM_READ, opts->audio_in_file_info); if(opts->audio_in_file == NULL) { printf ("Error, couldn't open file %s\n", opts->audio_in_dev); exit(1); } } else { // this is a device, use old handling opts->audio_in_type = 0; #ifdef SOLARIS sample_info_t aset, aget; int rgain; rgain = 64; if (opts->split == 1) { opts->audio_in_fd = open (opts->audio_in_dev, O_RDONLY); } else { opts->audio_in_fd = open (opts->audio_in_dev, O_RDWR); } if (opts->audio_in_fd == -1) { printf ("Error, couldn't open %s\n", opts->audio_in_dev); exit(1); } // get current ioctl (opts->audio_in_fd, AUDIO_GETINFO, &aset); aset.record.sample_rate = SAMPLE_RATE_IN; aset.play.sample_rate = SAMPLE_RATE_IN; aset.record.channels = 1; aset.play.channels = 1; aset.record.precision = 16; aset.play.precision = 16; aset.record.encoding = AUDIO_ENCODING_LINEAR; aset.play.encoding = AUDIO_ENCODING_LINEAR; aset.record.port = AUDIO_LINE_IN; aset.record.gain = rgain; if (ioctl (opts->audio_in_fd, AUDIO_SETINFO, &aset) == -1) { printf ("Error setting sample device parameters\n"); exit (1); } #endif #if defined(BSD) && !defined(__APPLE__) int fmt; if (opts->split == 1) { opts->audio_in_fd = open (opts->audio_in_dev, O_RDONLY); } else { opts->audio_in_fd = open (opts->audio_in_dev, O_RDWR); } if (opts->audio_in_fd == -1) { printf ("Error, couldn't open %s\n", opts->audio_in_dev); opts->audio_out = 0; } fmt = 0; if (ioctl (opts->audio_in_fd, SNDCTL_DSP_RESET) < 0) { printf ("ioctl reset error \n"); } fmt = SAMPLE_RATE_IN; if (ioctl (opts->audio_in_fd, SNDCTL_DSP_SPEED, &fmt) < 0) { printf ("ioctl speed error \n"); } fmt = 0; if (ioctl (opts->audio_in_fd, SNDCTL_DSP_STEREO, &fmt) < 0) { printf ("ioctl stereo error \n"); } fmt = AFMT_S16_LE; if (ioctl (opts->audio_in_fd, SNDCTL_DSP_SETFMT, &fmt) < 0) { printf ("ioctl setfmt error \n"); } #endif } } if (opts->split == 1) { printf ("Audio In Device: %s\n", opts->audio_in_dev); } else { printf ("Audio In/Out Device: %s\n", opts->audio_in_dev); } }