2 * Copyright (C) 2017 Konsulko Group
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
21 #include <pulse/pulseaudio.h>
23 #include "radio_output.h"
26 static pa_threaded_mainloop *mainloop;
27 static pa_context *context;
28 static pa_stream *stream;
30 static unsigned int extra;
31 static int16_t extra_buf[1];
32 static unsigned char *output_buf;
34 static void pa_context_state_cb(pa_context *c, void *data) {
38 switch (pa_context_get_state(c)) {
39 case PA_CONTEXT_CONNECTING:
40 case PA_CONTEXT_AUTHORIZING:
41 case PA_CONTEXT_SETTING_NAME:
42 case PA_CONTEXT_READY:
44 case PA_CONTEXT_TERMINATED:
45 pa_threaded_mainloop_stop(mainloop);
47 case PA_CONTEXT_FAILED:
49 fprintf(stderr, "PA connection failed: %s\n",
50 pa_strerror(pa_context_errno(c)));
51 pa_threaded_mainloop_stop(mainloop);
54 pa_threaded_mainloop_signal(mainloop, 0);
57 int radio_output_open(void)
60 pa_mainloop_api *mapi;
66 if (!(mainloop = pa_threaded_mainloop_new())) {
67 fprintf(stderr, "pa_mainloop_new() failed.\n");
71 pa_threaded_mainloop_set_name(mainloop, "pa_mainloop");
72 mapi = pa_threaded_mainloop_get_api(mainloop);
74 client = pa_xstrdup("radio");
75 if (!(c = pa_context_new(mapi, client))) {
76 fprintf(stderr, "pa_context_new() failed.\n");
80 pa_context_set_state_callback(c, pa_context_state_cb, NULL);
81 if (pa_context_connect(c, NULL, 0, NULL) < 0) {
82 fprintf(stderr, "pa_context_connect(): %s", pa_strerror(pa_context_errno(c)));
86 if (pa_threaded_mainloop_start(mainloop) < 0) {
87 fprintf(stderr, "pa_mainloop_run() failed.\n");
94 output_buf = malloc(sizeof(unsigned char) * RTL_FM_MAXIMUM_BUF_LENGTH);
103 pa_threaded_mainloop_free(mainloop);
109 int radio_output_start(void)
112 pa_sample_spec *spec;
118 error = radio_output_open();
123 while(pa_context_get_state(context) != PA_CONTEXT_READY)
124 pa_threaded_mainloop_wait(mainloop);
126 spec = (pa_sample_spec*) calloc(1, sizeof(pa_sample_spec));
127 spec->format = PA_SAMPLE_S16LE;
130 if (!pa_sample_spec_valid(spec)) {
131 fprintf(stderr, "%s\n",
132 pa_strerror(pa_context_errno(context)));
136 pa_threaded_mainloop_lock(mainloop);
137 pa_proplist *props = pa_proplist_new();
138 pa_proplist_sets(props, PA_PROP_MEDIA_ROLE, "radio");
139 stream = pa_stream_new_with_proplist(context, "radio-output", spec, 0, props);
141 fprintf(stderr, "Error creating stream %s\n",
142 pa_strerror(pa_context_errno(context)));
143 pa_proplist_free(props);
145 pa_threaded_mainloop_unlock(mainloop);
148 pa_proplist_free(props);
151 if(pa_stream_connect_playback(stream,
154 (pa_stream_flags_t) 0,
157 fprintf(stderr, "Error connecting to PulseAudio : %s\n",
158 pa_strerror(pa_context_errno(context)));
159 pa_stream_unref(stream);
161 pa_threaded_mainloop_unlock(mainloop);
165 pa_threaded_mainloop_unlock(mainloop);
167 while(pa_stream_get_state(stream) != PA_STREAM_READY)
168 pa_threaded_mainloop_wait(mainloop);
173 void radio_output_stop(void)
176 pa_threaded_mainloop_lock(mainloop);
178 pa_stream_set_state_callback(stream, 0, 0);
179 pa_stream_set_write_callback(stream, 0, 0);
180 pa_stream_set_underflow_callback(stream, 0, 0);
181 pa_stream_set_overflow_callback(stream, 0, 0);
182 pa_stream_set_latency_update_callback(stream, 0, 0);
184 pa_operation *o = pa_stream_flush(stream, NULL, NULL);
186 pa_operation_unref(o);
188 pa_stream_disconnect(stream);
189 pa_stream_unref(stream);
192 pa_threaded_mainloop_unlock(mainloop);
196 void radio_output_suspend(int state)
199 pa_stream_cork(stream, state, NULL, NULL);
203 void radio_output_close(void)
208 pa_context_disconnect(context);
209 pa_context_unref(context);
214 pa_threaded_mainloop_stop(mainloop);
215 pa_threaded_mainloop_free(mainloop);
223 int radio_output_write(void *buf, int len)
229 int samples = len / 2;
237 fprintf(stderr, "Error: buf == null!\n");
241 pa_threaded_mainloop_lock(mainloop);
243 avail = pa_stream_writable_size(stream);
246 * NOTE: Definitely room for improvement here,but for now just
247 * check for the no space case that happens when the
257 * Handle the rtl_fm code giving us an odd number of samples, which
258 * PA does not like. This extra buffer copying approach is not
259 * particularly efficient, but works for now. It looks feasible to
260 * hack in something in the demod and output thread routines in
261 * rtl_fm.c to handle it there if more performance is required.
265 memcpy(output_buf, extra_buf, sizeof(int16_t));
266 if((extra + samples) % 2) {
267 // We still have an extra sample, n remains the same, store the extra
268 memcpy(output_buf + sizeof(int16_t), buf, n - 2);
269 memcpy(extra_buf, ((unsigned char*) buf) + n - 2, sizeof(int16_t));
271 // We have an even number of samples, no extra
272 memcpy(output_buf + sizeof(int16_t), buf, n);
276 } else if(samples % 2) {
277 // We have an extra sample, store it, and decrease n
279 memcpy(output_buf + sizeof(int16_t), buf, n);
280 memcpy(extra_buf, ((unsigned char*) buf) + n, sizeof(int16_t));
286 if ((rc = pa_stream_write(stream, p, n, NULL, 0, PA_SEEK_RELATIVE)) < 0) {
287 fprintf(stderr, "Error writing %d bytes to PulseAudio : %s\n",
288 n, pa_strerror(pa_context_errno(context)));
291 pa_threaded_mainloop_unlock(mainloop);