]> git.ipfire.org Git - thirdparty/shairport-sync.git/blob - audio_soundio.c
Merge "openbsd corrections #1357" after resolving one conflict.
[thirdparty/shairport-sync.git] / audio_soundio.c
1 #include "audio.h"
2 #include "common.h"
3 #include <memory.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7
8 #include <soundio/soundio.h>
9
10 struct SoundIoOutStream *outstream;
11 struct SoundIo *soundio;
12 struct SoundIoDevice *device;
13 struct SoundIoRingBuffer *ring_buffer = NULL;
14
15 static int min_int(int a, int b) { return (a < b) ? a : b; }
16
17 static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min,
18 int frame_count_max) {
19 struct SoundIoChannelArea *areas;
20 // int frame_count;
21 int err;
22
23 char *read_ptr = soundio_ring_buffer_read_ptr(ring_buffer);
24 int fill_bytes = soundio_ring_buffer_fill_count(ring_buffer);
25 if (outstream->bytes_per_frame == 0)
26 die("soundio: outstream->bytes_per_frame is zero.");
27 int fill_count = fill_bytes / outstream->bytes_per_frame;
28
29 debug(3,
30 "[--->>] frame_count_min: %d , frame_count_max: %d , fill_bytes: %d , fill_count: %d , "
31 "outstream->bytes_per_frame: %d",
32 frame_count_min, frame_count_max, fill_bytes, fill_count, outstream->bytes_per_frame);
33
34 if (frame_count_min > fill_count) {
35 int frame_count = frame_count_min;
36 if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count))) {
37 debug(0, "[--->>] begin write error: %s", soundio_strerror(err));
38 }
39 for (int frame = 0; frame < frame_count; frame += 1) {
40 for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
41 memset(areas[ch].ptr, 0, outstream->bytes_per_sample);
42 areas[ch].ptr += areas[ch].step;
43 }
44 }
45 if ((err = soundio_outstream_end_write(outstream)))
46 debug(0, "[--->>] end write error: %s", soundio_strerror(err));
47 return;
48 }
49
50 int read_count = min_int(frame_count_max, fill_count);
51 int frames_left = read_count;
52
53 while (frames_left > 0) {
54 int frame_count = frames_left;
55
56 if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
57 debug(0, "[--->>] begin write error: %s", soundio_strerror(err));
58
59 if (frame_count <= 0)
60 break;
61
62 for (int frame = 0; frame < frame_count; frame += 1) {
63 for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) {
64 memcpy(areas[ch].ptr, read_ptr, outstream->bytes_per_sample);
65 areas[ch].ptr += areas[ch].step;
66 read_ptr += outstream->bytes_per_sample;
67 }
68 }
69
70 if ((err = soundio_outstream_end_write(outstream)))
71 debug(0, "[--->>] end write error: %s", soundio_strerror(err));
72
73 frames_left -= frame_count;
74 }
75
76 debug(3, "[--->>] Wrote: %d", read_count * outstream->bytes_per_frame);
77 soundio_ring_buffer_advance_read_ptr(ring_buffer, read_count * outstream->bytes_per_frame);
78 }
79
80 static void underflow_callback(__attribute__((unused)) struct SoundIoOutStream *outstream) {
81 static int count = 0;
82 debug(0, "underflow %d\n", ++count);
83 }
84
85 static int init(__attribute__((unused)) int argc, __attribute__((unused)) char **argv) {
86 int err;
87
88 config.audio_backend_buffer_desired_length = 2.0;
89 config.audio_backend_latency_offset = 0;
90
91 // get settings from settings file
92
93 // do the "general" audio options. Note, these options are in the "general" stanza!
94 parse_general_audio_options();
95
96 // get the specific settings
97
98 soundio = soundio_create();
99 if (!soundio) {
100 debug(0, "out of memory\n");
101 return 1;
102 }
103 if ((err = soundio_connect_backend(soundio, SoundIoBackendCoreAudio))) {
104 debug(0, "error connecting: %s", soundio_strerror(err));
105 return 1;
106 }
107 soundio_flush_events(soundio);
108
109 int default_out_device_index = soundio_default_output_device_index(soundio);
110 if (default_out_device_index < 0) {
111 debug(0, "no output device found");
112 return 1;
113 }
114
115 device = soundio_get_output_device(soundio, default_out_device_index);
116 if (!device) {
117 debug(0, "out of memory");
118 return 1;
119 }
120 debug(0, "Output device: %s\n", device->name);
121 return 0;
122 }
123
124 static void deinit(void) {
125 soundio_ring_buffer_destroy(ring_buffer);
126 soundio_device_unref(device);
127 soundio_destroy(soundio);
128 debug(0, "soundio audio deinit\n");
129 }
130
131 static void start(int sample_rate, int sample_format) {
132 int err;
133
134 debug(1, "soundion rate: %d, format: %d", sample_rate, sample_format);
135
136 // soundio_device_sort_channel_layouts(device);
137
138 outstream = soundio_outstream_create(device);
139 outstream->format = SoundIoFormatS16NE;
140 outstream->sample_rate = sample_rate;
141 outstream->layout.channel_count = 2;
142 outstream->write_callback = write_callback;
143 outstream->underflow_callback = underflow_callback;
144 // outstream->software_latency = 0;
145
146 if ((err = soundio_outstream_open(outstream))) {
147 debug(0, "unable to open device: %s", soundio_strerror(err));
148 }
149 if (outstream->layout_error)
150 debug(0, "unable to set channel layout: %s\n", soundio_strerror(outstream->layout_error));
151
152 int capacity = outstream->sample_rate * outstream->bytes_per_frame;
153 ring_buffer = soundio_ring_buffer_create(soundio, capacity);
154 if (!ring_buffer)
155 debug(0, "unable to create ring buffer: out of memory");
156 char *buf = soundio_ring_buffer_write_ptr(ring_buffer);
157 memset(buf, 0, capacity);
158 soundio_ring_buffer_advance_write_ptr(ring_buffer, capacity);
159
160 if ((err = soundio_outstream_start(outstream))) {
161 debug(0, "unable to start outstream: %s", soundio_strerror(err));
162 }
163
164 debug(1, "libsoundio output started\n");
165 }
166
167 static int play(void *buf, int samples) {
168 // int err;
169 int free_bytes = soundio_ring_buffer_free_count(ring_buffer);
170 int written_bytes = 0;
171 int write_bytes = 0;
172 int left_bytes = samples * outstream->bytes_per_frame;
173 char *write_ptr = soundio_ring_buffer_write_ptr(ring_buffer);
174
175 debug(3, "[<<---] samples: %d , size: %d", samples, left_bytes);
176 write_bytes = min_int(left_bytes, free_bytes);
177 debug(3, "[<<---] left_bytes: %d, write_bytes: %d, free_bytes: %d\n", left_bytes, write_bytes,
178 free_bytes);
179
180 if (write_bytes) {
181 memcpy(write_ptr, buf, write_bytes);
182 written_bytes += write_bytes;
183 soundio_ring_buffer_advance_write_ptr(ring_buffer, write_bytes);
184 debug(3, "[<<---] Written to buffer : %d\n", written_bytes);
185 }
186 return 0;
187 }
188
189 static void parameters(audio_parameters *info) {
190 info->minimum_volume_dB = -30.0;
191 info->maximum_volume_dB = 0.0;
192 debug(2, "Parameters\n");
193 debug(2, "Current Volume dB: %f\n", info->current_volume_dB);
194 debug(2, "Minimum Volume dB: %d\n", info->minimum_volume_dB);
195 debug(2, "Maximum Volume dB: %d\n", info->maximum_volume_dB);
196 }
197
198 static void stop(void) {
199 soundio_outstream_destroy(outstream);
200 soundio_ring_buffer_clear(ring_buffer);
201 debug(1, "libsoundio output stopped\n");
202 }
203
204 static void flush(void) {
205 soundio_ring_buffer_clear(ring_buffer);
206 debug(1, "libsoundio output flushed\n");
207 }
208
209 static void help(void) { printf(" There are no options for libsoundio.\n"); }
210
211 audio_output audio_soundio = {.name = "soundio",
212 .help = &help,
213 .init = &init,
214 .deinit = &deinit,
215 .prepare = NULL,
216 .start = &start,
217 .stop = &stop,
218 .is_running = NULL,
219 .flush = &flush,
220 .delay = NULL,
221 .stats = NULL,
222 .play = &play,
223 .volume = NULL,
224 .parameters = &parameters,
225 .mute = NULL};