1 /*****************************************************************
2  * gmerlin - a general purpose multimedia framework and applications
3  *
4  * Copyright (c) 2001 - 2011 Members of the Gmerlin project
5  * gmerlin-general@lists.sourceforge.net
6  * http://gmerlin.sourceforge.net
7  *
8  * This program 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 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
20  * *****************************************************************/
21 
22 #include <stdio.h>
23 #include <string.h>
24 
25 #include <unistd.h>
26 #include <stdlib.h>
27 
28 #include "jack_common.h"
29 
30 #include <gmerlin/utils.h>
31 
32 
33 #define LOG_DOMAIN "oa_jack"
34 
mute_port(jack_t * priv,jack_nframes_t nframes,int port)35 static void mute_port(jack_t * priv, jack_nframes_t nframes, int port)
36   {
37   char *out;
38   out = jack_port_get_buffer(priv->ports[port].int_port, nframes);
39   memset(out, 0, nframes * sizeof(float));
40   }
41 
find_port(jack_t * priv,gavl_channel_id_t id)42 static int find_port(jack_t * priv, gavl_channel_id_t id)
43   {
44   int i;
45   for(i = 0; i < priv->num_ports; i++)
46     {
47     if(priv->ports[i].channel_id == id)
48       return i;
49     }
50   return -1;
51   }
52 
jack_process(jack_nframes_t nframes,void * arg)53 static int jack_process(jack_nframes_t nframes, void *arg)
54   {
55   int i;
56   jack_t * priv = arg;
57   char *out;
58   int bytes_read, bytes_to_read, result;
59   gavl_time_t delay_time;
60 
61   //  fprintf(stderr, "jack_process %d\n", nframes);
62 
63   bytes_to_read = nframes * sizeof(float);
64 
65   pthread_mutex_lock(&priv->active_mutex);
66 
67   if(!priv->active)
68     {
69     for(i = 0; i < priv->num_ports; i++)
70       mute_port(priv, nframes, i);
71     pthread_mutex_unlock(&priv->active_mutex);
72     return 0;
73     }
74 
75   pthread_mutex_unlock(&priv->active_mutex);
76 
77   for(i = 0; i < priv->num_ports; i++)
78     {
79     if(priv->ports[i].active)
80       {
81       bytes_read = 0;
82       out = jack_port_get_buffer(priv->ports[i].int_port, nframes);
83 
84       while(bytes_read < bytes_to_read)
85         {
86         result = jack_ringbuffer_read(priv->ports[i].buffer, out + bytes_read,
87                                       bytes_to_read - bytes_read);
88 
89         if(result < bytes_to_read - bytes_read)
90           {
91           fprintf(stderr, "Underflow\n");
92 
93           delay_time = gavl_time_unscale(priv->format.samplerate,
94                                          bytes_to_read - bytes_read - result) / 2;
95           gavl_time_delay(&delay_time);
96           }
97         bytes_read += result;
98         }
99       }
100     else
101       {
102       mute_port(priv, nframes, i);
103       }
104     }
105 
106   return 0;
107   }
108 
109 
setup_channel(jack_t * priv,gavl_channel_id_t id)110 static void setup_channel(jack_t * priv, gavl_channel_id_t id)
111   {
112   int index;
113 
114   index = find_port(priv, id);
115   if(index < 0)
116     return;
117 
118   priv->ports[index].index = priv->format.num_channels;
119   priv->format.channel_locations[priv->format.num_channels] = id;
120   priv->ports[index].active = 1;
121   priv->format.num_channels++;
122 
123   jack_ringbuffer_reset(priv->ports[index].buffer);
124 
125   }
126 
open_jack(void * data,gavl_audio_format_t * format)127 static int open_jack(void * data, gavl_audio_format_t * format)
128   {
129   int i;
130   int num_front_channels;
131   int num_rear_channels;
132   int num_lfe_channels;
133 
134   jack_t * priv = data;
135 
136   if(!priv->client)
137     bg_jack_open_client(priv, 1, jack_process);
138 
139   /* Copy format */
140   gavl_audio_format_copy(&priv->format, format);
141 
142   priv->format.samplerate = priv->samplerate;
143   priv->format.sample_format = GAVL_SAMPLE_FLOAT;
144   priv->format.interleave_mode = GAVL_INTERLEAVE_NONE;
145   priv->format.samples_per_frame = priv->samples_per_frame;
146 
147   /* Clear ports */
148   for(i = 0; i < priv->num_ports; i++)
149     priv->ports[i].active = 0;
150 
151   /* Setup ports */
152 
153   num_front_channels = gavl_front_channels(format);
154   num_rear_channels = gavl_rear_channels(format);
155   num_lfe_channels = gavl_lfe_channels(format);
156 
157   priv->format.num_channels = 0;
158 
159   switch(num_front_channels)
160     {
161     case 2:
162       setup_channel(priv, GAVL_CHID_FRONT_LEFT);
163       setup_channel(priv, GAVL_CHID_FRONT_RIGHT);
164       break;
165     case 1:
166     case 3:
167       setup_channel(priv, GAVL_CHID_FRONT_CENTER);
168       setup_channel(priv, GAVL_CHID_FRONT_LEFT);
169       setup_channel(priv, GAVL_CHID_FRONT_RIGHT);
170       break;
171     }
172 
173   if(num_rear_channels)
174     {
175     setup_channel(priv, GAVL_CHID_REAR_LEFT);
176     setup_channel(priv, GAVL_CHID_REAR_RIGHT);
177     }
178   if(num_lfe_channels)
179     setup_channel(priv, GAVL_CHID_LFE);
180 
181   gavl_audio_format_copy(format, &priv->format);
182 
183   return 1;
184   }
185 
close_jack(void * p)186 static void close_jack(void * p)
187   {
188   }
189 
write_frame_jack(void * p,gavl_audio_frame_t * f)190 static void write_frame_jack(void * p, gavl_audio_frame_t * f)
191   {
192   int i;
193   int samples_written, result;
194   gavl_time_t delay_time;
195   jack_t * priv = p;
196   int write_space;
197 
198   //  fprintf(stderr, "Write jack %d\n", f->valid_samples);
199 
200   for(i = 0; i < priv->num_ports; i++)
201     {
202     if(!priv->ports[i].active)
203       continue;
204 
205     samples_written = 0;
206 
207     while(1)
208       {
209       write_space = jack_ringbuffer_write_space(priv->ports[i].buffer);
210       if(write_space >= f->valid_samples * sizeof(float))
211         break;
212 
213       delay_time = gavl_time_unscale(priv->format.samplerate,
214                                      f->valid_samples - write_space / sizeof(float)) / 2;
215       gavl_time_delay(&delay_time);
216       }
217 
218     while(samples_written < f->valid_samples)
219       {
220       result =
221         jack_ringbuffer_write(priv->ports[i].buffer,
222                               (const char*)(f->channels.f[priv->ports[i].index] +
223                                             samples_written),
224                               (f->valid_samples - samples_written) *
225                               sizeof(float));
226 #if 0
227       if(i)
228         fprintf(stderr, "Write %d %d %ld\n", samples_written,
229                 f->valid_samples - samples_written, result);
230 
231 #endif
232       result /= sizeof(float);
233 
234       samples_written += result;
235 
236       }
237 
238     }
239 
240   }
241 
get_delay_jack(void * p)242 static int get_delay_jack(void * p)
243   {
244   jack_t * priv;
245   priv = p;
246 
247   return jack_port_get_latency(priv->ports[0].int_port);
248   }
249 
250 const bg_oa_plugin_t the_plugin =
251   {
252     .common =
253     {
254       BG_LOCALE,
255       .name =          "oa_jack",
256       .long_name =     TRS("Jack"),
257       .description =   TRS("Jack output plugin"),
258       .type =          BG_PLUGIN_OUTPUT_AUDIO,
259       .flags =         BG_PLUGIN_PLAYBACK,
260       .priority =      BG_PLUGIN_PRIORITY_MAX-1,
261       .create =        bg_jack_create,
262       .destroy =       bg_jack_destroy,
263 
264       .get_parameters = bg_jack_get_parameters,
265       .set_parameter =  bg_jack_set_parameter,
266     },
267 
268     .open =          open_jack,
269     .start =         bg_jack_start,
270     .write_audio =   write_frame_jack,
271     .stop =          bg_jack_stop,
272     .close =         close_jack,
273     .get_delay =     get_delay_jack,
274   };
275 
276 /* Include this into all plugin modules exactly once
277    to let the plugin loader obtain the API version */
278 BG_GET_PLUGIN_API_VERSION;
279