1 /* -*- c++ -*- */
2 /*
3  * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
4  *           https://gqrx.dk/
5  *
6  * Copyright 2016 Alexandru Csete OZ9AEC.
7  *
8  * Gqrx is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3, or (at your option)
11  * any later version.
12  *
13  * Gqrx is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Gqrx; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street,
21  * Boston, MA 02110-1301, USA.
22  */
23 #include <gnuradio/io_signature.h>
24 #include <stdio.h>
25 
26 #include "device_list.h"
27 #include "portaudio_sink.h"
28 
29 /**
30  * Create a new portaudio sink object.
31  * @param device_name The name of the audio device, or NULL for default.
32  * @param audio_rate The sample rate of the audio stream.
33  * @param app_name Application name.
34  * @param stream_name The audio stream name.
35  */
make_portaudio_sink(const string device_name,int audio_rate,const string app_name,const string stream_name)36 portaudio_sink_sptr make_portaudio_sink(const string device_name,
37                                         int audio_rate,
38                                         const string app_name,
39                                         const string stream_name)
40 {
41     return gnuradio::get_initial_sptr(new portaudio_sink(device_name,
42                                                          audio_rate,
43                                                          app_name,
44                                                          stream_name));
45 }
46 
portaudio_sink(const string device_name,int audio_rate,const string app_name,const string stream_name)47 portaudio_sink::portaudio_sink(const string device_name, int audio_rate,
48                  const string app_name, const string stream_name)
49   : gr::sync_block ("portaudio_sink",
50         gr::io_signature::make (1, 2, sizeof(float)),
51         gr::io_signature::make (0, 0, 0)),
52     d_stream_name(stream_name),
53     d_app_name(app_name),
54     d_audio_rate(audio_rate)
55 {
56 
57     // find device index
58     PaDeviceIndex           idx;
59     portaudio_device_list   devices;
60 
61     idx = devices.get_output_device_index(device_name);
62     if (idx == -1)
63     {
64         fprintf(stderr, "Using default audio device\n");
65         idx = Pa_GetDefaultOutputDevice();
66     }
67     else
68     {
69         fprintf(stderr, "Audio device '%s' has index %d\n",
70                 device_name.data(), idx);
71     }
72 
73     // Initialize stream parameters
74     d_out_params.device = idx;
75     d_out_params.channelCount = 2;
76     d_out_params.sampleFormat = paFloat32;
77     d_out_params.suggestedLatency =
78             Pa_GetDeviceInfo(d_out_params.device)->defaultHighOutputLatency;
79     d_out_params.hostApiSpecificStreamInfo = NULL;
80 
81     if (Pa_IsFormatSupported(NULL, &d_out_params, d_audio_rate) != paFormatIsSupported)
82         fprintf(stderr, "portaudio_sink(): Audio output device does not support requested format.\n");
83 }
84 
~portaudio_sink()85 portaudio_sink::~portaudio_sink()
86 {
87     // nothing to do
88 }
89 
90 /* open and start audio stream */
start()91 bool portaudio_sink::start()
92 {
93     PaError     err;
94 
95     err = Pa_OpenStream(&d_stream,
96                         NULL,           // inputParameters
97                         &d_out_params,
98                         d_audio_rate,
99                         paFramesPerBufferUnspecified,
100                         paClipOff,
101                         NULL,           // no callback, use blocking API
102                         NULL);
103 
104     if (err != paNoError)
105     {
106         fprintf(stderr,
107                 "portaudio_sink::start(): Failed to open audio stream: %s\n",
108                 Pa_GetErrorText(err));
109         return false;
110     }
111 
112     err = Pa_StartStream(d_stream);
113     if (err != paNoError)
114     {
115         fprintf(stderr,
116                 "portaudio_sink::start(): Failed to start audio stream: %s\n",
117                 Pa_GetErrorText(err));
118         return false;
119     }
120 
121     return true;
122 }
123 
124 /* Stop and close audio stream */
stop()125 bool portaudio_sink::stop()
126 {
127     PaError     err;
128     bool        retval = true;
129 
130     err = Pa_StopStream(d_stream);
131     if (err != paNoError)
132     {
133         retval = false;
134         fprintf(stderr,
135                 "portaudio_sink::stop(): Error stopping audio stream: %s\n",
136                 Pa_GetErrorText(err));
137     }
138 
139     err = Pa_CloseStream(d_stream);
140     if (err != paNoError)
141     {
142         retval = false;
143         fprintf(stderr,
144                 "portaudio_sink::stop(): Error closing audio stream: %s\n",
145                 Pa_GetErrorText(err));
146     }
147 
148     return retval;
149 }
150 
select_device(string device_name)151 void portaudio_sink::select_device(string device_name)
152 {
153 
154 }
155 
156 #define BUFFER_SIZE 100000
work(int noutput_items,gr_vector_const_void_star & input_items,gr_vector_void_star & output_items)157 int portaudio_sink::work(int noutput_items,
158                          gr_vector_const_void_star &input_items,
159                          gr_vector_void_star &output_items)
160 {
161     PaError     err;
162 
163     static float    audio_buffer[BUFFER_SIZE];
164     float      *ptr = &audio_buffer[0];
165     int         i;
166 
167     (void) output_items;
168 
169     if (noutput_items > BUFFER_SIZE/2)
170         noutput_items = BUFFER_SIZE/2;
171 
172     // two channels (stereo)
173     const float *data_l = (const float*) input_items[0];
174     const float *data_r = (const float*) input_items[1];
175     for (i = noutput_items; i > 0; i--)
176     {
177         *ptr++ = *data_l++;
178         *ptr++ = *data_r++;
179     }
180 
181     err = Pa_WriteStream(d_stream, audio_buffer, noutput_items);
182     if (err)
183         fprintf(stderr,
184                 "portaudio_sink::work(): Error writing to audio device: %s\n",
185                 Pa_GetErrorText(err));
186 
187     return noutput_items;
188 
189 // code below supports 1 or 2 channels
190 #if 0
191     if (input_items.size() == 2)
192     {
193         // two channels (stereo)
194         const float *data_l = (const float*) input_items[0]; // left channel
195         const float *data_r = (const float*) input_items[1]; // right channel
196         for (i = noutput_items; i > 0; i--)
197         {
198             *ptr++ = *data_l++;
199             *ptr++ = *data_r++;
200         }
201     }
202     else
203     {
204         // assume 1 channel (mono)
205         const float *data = (const float*) input_items[0];
206         for (i = noutput_items; i > 0; i--)
207         {
208             float a = *data++;
209             *ptr++ = a; // same data in left and right channel
210             *ptr++ = a;
211         }
212     }
213 #endif
214 
215 }
216