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