root/branches/libfreebob-2.0/src/libstreaming/Port.cpp

Revision 228, 11.9 kB (checked in by pieterpalmers, 18 years ago)

- generic rate control added for ringbuffer based ports.

Line 
1 /* $Id$ */
2
3 /*
4  *   FreeBob Streaming API
5  *   FreeBob = Firewire (pro-)audio for linux
6  *
7  *   http://freebob.sf.net
8  *
9  *   Copyright (C) 2005,2006 Pieter Palmers <pieterpalmers@users.sourceforge.net>
10  *
11  *   This program is free software {} you can redistribute it and/or modify
12  *   it under the terms of the GNU General Public License as published by
13  *   the Free Software Foundation {} either version 2 of the License, or
14  *   (at your option) any later version.
15  *
16  *   This program is distributed in the hope that it will be useful,
17  *   but WITHOUT ANY WARRANTY {} without even the implied warranty of
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *   GNU General Public License for more details.
20  *
21  *   You should have received a copy of the GNU General Public License
22  *   along with this program {} if not, write to the Free Software
23  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  *
25  *
26  *
27  */
28
29 #include "Port.h"
30  #include <stdlib.h>
31 #include <assert.h>
32
33
34 namespace FreebobStreaming {
35
36 IMPL_DEBUG_MODULE( Port, Port, DEBUG_LEVEL_NORMAL );
37
38 Port::Port(std::string name, enum E_PortType porttype, enum E_Direction direction)
39         : m_Name(name),
40         m_SignalType(E_PeriodSignalled),
41         m_BufferType(E_PointerBuffer),
42         m_enabled(true),
43         m_initialized(false),
44         m_buffersize(0),
45         m_eventsize(0),
46         m_DataType(E_Int24),
47         m_PortType(porttype),
48         m_Direction(direction),
49         m_buffer(0),
50         m_ringbuffer(0),
51         m_use_external_buffer(false),
52         m_do_ratecontrol(false),
53         m_event_interval(0),
54         m_slot_interval(0),
55         m_rate_counter(0),
56         m_rate_counter_minimum(0),
57         m_average_ratecontrol(false)
58        
59 {
60
61 }
62
63 /**
64  * The idea is that you set all port parameters, and then initialize the port.
65  * This allocates all resources and makes the port usable. However, you can't
66  * change the port properties anymore after this.
67  *
68  * @return true if successfull. false if not (all resources are freed).
69  */
70 bool Port::init() {
71         if (m_initialized) {
72                 debugFatal("Port already initialized... (%s)\n",m_Name.c_str());
73                 return false;
74         }       
75        
76         if (m_buffersize==0) {
77                 debugFatal("Cannot initialize a port with buffersize=0\n");
78                 return false;   
79         }
80        
81         switch (m_BufferType) {
82                 case E_PointerBuffer:
83                         if (m_use_external_buffer) {
84                                 // don't do anything
85                         } else if (!allocateInternalBuffer()) {
86                                 debugFatal("Could not allocate internal buffer!\n");
87                                 return false;
88                         }
89                         break;
90                        
91                 case E_RingBuffer:
92                         if (m_use_external_buffer) {
93                                 debugFatal("Cannot use an external ringbuffer! \n");
94                                 return false;
95                         } else if (!allocateInternalRingBuffer()) {
96                                 debugFatal("Could not allocate internal ringbuffer!\n");
97                                 return false;
98                         }
99                         break;
100                 default:
101                         debugFatal("Unsupported buffer type! (%d)\n",(int)m_BufferType);
102                         return false;
103                         break;
104         }
105
106         m_initialized=true;
107        
108         m_eventsize=getEventSize(); // this won't change, so cache it
109        
110         return m_initialized;
111 }
112
113 void Port::setVerboseLevel(int l) {
114         setDebugLevel(l);
115 }
116
117 bool Port::setName(std::string name) {
118         debugOutput( DEBUG_LEVEL_VERBOSE, "Setting name to %s for port %s\n",name.c_str(),m_Name.c_str());
119        
120         if (m_initialized) {
121                 debugFatal("Port already initialized... (%s)\n",m_Name.c_str());
122                 return false;
123         }
124        
125         m_Name=name;
126        
127         return true;
128 }
129
130 bool Port::setBufferSize(unsigned int newsize) {
131         debugOutput( DEBUG_LEVEL_VERBOSE, "Setting buffersize to %d for port %s\n",newsize,m_Name.c_str());
132         if (m_initialized) {
133                 debugFatal("Port already initialized... (%s)\n",m_Name.c_str());
134                 return false;
135         }
136
137         m_buffersize=newsize;
138         return true;
139
140 }
141
142 unsigned int Port::getEventSize() {
143         switch (m_DataType) {
144                 case E_Float:
145                         return sizeof(float);
146                 case E_Int24: // 24 bit 2's complement, packed in a 32bit integer (LSB's)
147                         return sizeof(uint32_t);
148                 case E_MidiEvent:
149                         return sizeof(uint32_t);
150                 default:
151                         return 0;
152         }
153 }
154
155 bool Port::setDataType(enum E_DataType d) {
156         debugOutput( DEBUG_LEVEL_VERBOSE, "Setting datatype to %d for port %s\n",(int) d,m_Name.c_str());
157         if (m_initialized) {
158                 debugFatal("Port already initialized... (%s)\n",m_Name.c_str());
159                 return false;
160         }
161        
162         // do some sanity checks
163         bool type_is_ok=false;
164         switch (m_PortType) {
165                 case E_Audio:
166                         if(d == E_Int24) type_is_ok=true;
167                         if(d == E_Float) type_is_ok=true;
168                         break;
169                 case E_Midi:
170                         if(d == E_MidiEvent) type_is_ok=true;
171                         break;
172                 case E_Control:
173                         if(d == E_Default) type_is_ok=true;
174                         break;
175                 default:
176                         break;
177         }
178        
179         if(!type_is_ok) {
180                 debugFatal("Datatype not supported by this type of port!\n");
181                 return false;
182         }
183        
184         m_DataType=d;
185         return true;
186 }
187
188 bool Port::setSignalType(enum E_SignalType s) {
189         debugOutput( DEBUG_LEVEL_VERBOSE, "Setting signaltype to %d for port %s\n",(int)s,m_Name.c_str());
190         if (m_initialized) {
191                 debugFatal("Port already initialized... (%s)\n",m_Name.c_str());
192                 return false;
193         }
194        
195         // do some sanity checks
196         bool type_is_ok=false;
197         switch (m_PortType) {
198                 case E_Audio:
199                         if(s == E_PeriodSignalled) type_is_ok=true;
200                         break;
201                 case E_Midi:
202                         if(s == E_PacketSignalled) type_is_ok=true;
203                         break;
204                 case E_Control:
205                         if(s == E_PeriodSignalled) type_is_ok=true;
206                         break;
207                 default:
208                         break;
209         }
210        
211         if(!type_is_ok) {
212                 debugFatal("Signalling type not supported by this type of port!\n");
213                 return false;
214         }
215        
216         m_SignalType=s;
217         return true;
218
219 }
220
221 bool Port::setBufferType(enum E_BufferType b) {
222         debugOutput( DEBUG_LEVEL_VERBOSE, "Setting buffer type to %d for port %s\n",(int)b,m_Name.c_str());
223         if (m_initialized) {
224                 debugFatal("Port already initialized... (%s)\n",m_Name.c_str());
225                 return false;
226         }
227        
228         // do some sanity checks
229         bool type_is_ok=false;
230         switch (m_PortType) {
231                 case E_Audio:
232                         if(b == E_PointerBuffer) type_is_ok=true;
233                         break;
234                 case E_Midi:
235                         if(b == E_RingBuffer) type_is_ok=true;
236                         break;
237                 case E_Control:
238                         break;
239                 default:
240                         break;
241         }
242        
243         if(!type_is_ok) {
244                 debugFatal("Buffer type not supported by this type of port!\n");
245                 return false;
246         }
247        
248         m_BufferType=b;
249         return true;
250
251 }
252
253 bool Port::useExternalBuffer(bool b) {
254        
255         debugOutput( DEBUG_LEVEL_VERBOSE, "Setting external buffer use to %d for port %s\n",(int)b,m_Name.c_str());
256        
257         if (m_initialized) {
258                 debugFatal("Port already initialized... (%s)\n",m_Name.c_str());
259                 return false;
260         }
261
262         m_use_external_buffer=b;
263         return true;
264 }
265
266 // buffer handling api's for pointer buffers
267 /**
268  * Get the buffer address (being the external or the internal one).
269  *
270  * @param buff
271  */
272 void *Port::getBufferAddress() {
273         assert(m_BufferType==E_PointerBuffer);
274         return m_buffer;
275 };
276
277 /**
278  * Set the external buffer address.
279  * only call this when specifying an external buffer before init()
280  *
281  * @param buff
282  */
283 void Port::setExternalBufferAddress(void *buff) {
284         assert(m_BufferType==E_PointerBuffer);
285         assert(m_use_external_buffer); // don't call this with an internal buffer!
286         m_buffer=buff;
287 };
288
289 // buffer handling api's for ringbuffers
290 bool Port::writeEvent(void *event) {
291         assert(m_BufferType==E_RingBuffer);
292         assert(m_ringbuffer);
293        
294         debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Writing event %08X with size %d to port %s\n",*((quadlet_t *)event),m_eventsize, m_Name.c_str());
295        
296         return (freebob_ringbuffer_write(m_ringbuffer, (char *)event, m_eventsize)==m_eventsize);
297 }
298
299 bool Port::readEvent(void *event) {
300         assert(m_ringbuffer);
301        
302         unsigned int read=freebob_ringbuffer_read(m_ringbuffer, (char *)event, m_eventsize);
303        
304         debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Reading event %X with size %d from port %s\n",*((quadlet_t *)event),m_eventsize,m_Name.c_str());
305         return (read==m_eventsize);
306 }
307
308 int Port::writeEvents(void *event, unsigned int nevents) {
309         assert(m_BufferType==E_RingBuffer);
310         assert(m_ringbuffer);
311        
312         unsigned int bytes2write=m_eventsize*nevents;
313        
314         unsigned int written=freebob_ringbuffer_write(m_ringbuffer, (char *)event,bytes2write)/m_eventsize;
315        
316         if(written) {
317                 int i=0;
318                 quadlet_t * tmp=(quadlet_t *)event;
319                 debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Written %d events (",written);
320                 for (i=0;i<written;i++) {
321                         debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, "%X ", *(tmp+i));
322                 }
323                 debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, ") to port %s\n",m_Name.c_str());
324         }
325        
326         return written;
327
328 }
329
330 int Port::readEvents(void *event, unsigned int nevents) {
331         assert(m_ringbuffer);
332        
333         unsigned int bytes2read=m_eventsize*nevents;
334        
335         unsigned int read=freebob_ringbuffer_read(m_ringbuffer, (char *)event, bytes2read)/m_eventsize;
336        
337         if(read) {
338                 int i=0;
339                 quadlet_t * tmp=(quadlet_t *)event;
340                 debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Read %d events (",read);
341                 for (i=0;i<read;i++) {
342                         debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, "%X ", *(tmp+i));
343                 }
344                 debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, ") from port %s\n",m_Name.c_str());
345         }
346        
347         return read;
348 }
349
350 /* rate control */
351 bool Port::canRead() {
352         bool byte_present_in_buffer;
353        
354         bool retval=false;
355        
356         assert(m_ringbuffer);
357        
358         byte_present_in_buffer=(freebob_ringbuffer_read_space(m_ringbuffer) >= m_eventsize);
359        
360         if(byte_present_in_buffer) {
361                
362                 if(!m_do_ratecontrol) {
363                         return true;
364                 }
365                
366                 if(m_rate_counter <= 0) {
367                         // update the counter
368                         if(m_average_ratecontrol) {
369                                 m_rate_counter += m_event_interval;
370                                 assert(m_rate_counter<m_event_interval);
371                         } else {
372                                 m_rate_counter = m_event_interval;
373                         }
374                
375                         retval=true;
376                 } else {
377                         debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Rate limit (%s)! rate_counter=%d \n",m_Name.c_str(),m_rate_counter);
378                
379                 }
380         }
381        
382        
383         m_rate_counter -= m_slot_interval;
384        
385         // we have to limit the decrement of the ratecounter somehow.
386         // m_rate_counter_minimum is initialized when enabling ratecontrol
387         if(m_rate_counter < m_rate_counter_minimum) {
388                 m_rate_counter = m_rate_counter_minimum;
389         }
390        
391         return retval;
392 }
393
394 bool Port::useRateControl(bool use, unsigned int slot_interval,
395                                     unsigned int event_interval, bool average) {
396
397         if (use) {
398                 debugOutput(DEBUG_LEVEL_VERBOSE, "Enabling rate control for port %s...\n",m_Name.c_str());
399                 if(slot_interval>event_interval) {
400                         debugWarning("Rate control not needed!\n",m_Name.c_str());
401                         m_do_ratecontrol=false;
402                         return false;
403                 }
404                 if(slot_interval==0) {
405                         debugFatal("Cannot have slot interval == 0!\n");
406                         m_do_ratecontrol=false;
407                         return false;
408                 }
409                 if(event_interval==0) {
410                         debugFatal("Cannot have event interval == 0!\n");
411                         m_do_ratecontrol=false;
412                         return false;
413                 }
414                 m_do_ratecontrol=use;
415                 m_event_interval=event_interval;
416                 m_slot_interval=slot_interval;
417                 m_rate_counter=0;
418                
419                 // NOTE: pretty arbitrary, but in average mode this limits the peak stream rate
420                 m_rate_counter_minimum=-(2*event_interval);
421                
422                 m_average_ratecontrol=average;
423
424         } else {
425                 debugOutput(DEBUG_LEVEL_VERBOSE, "Disabling rate control for port %s...\n",m_Name.c_str());
426                 m_do_ratecontrol=use;
427         }
428         return true;
429 }
430
431 /* Private functions */
432
433 bool Port::allocateInternalBuffer() {
434         int event_size=getEventSize();
435        
436         debugOutput(DEBUG_LEVEL_VERBOSE,
437                     "Allocating internal buffer of %d events with size %d (%s)\n",
438                     m_buffersize, event_size, m_Name.c_str());
439
440         if(m_buffer) {
441                 debugWarning("already has an internal buffer attached, re-allocating\n");
442                 freeInternalBuffer();
443         }
444
445         m_buffer=calloc(m_buffersize,event_size);
446         if (!m_buffer) {
447                 debugFatal("could not allocate internal buffer\n");
448                 m_buffersize=0;
449                 return false;
450         }
451
452         return true;
453 }
454
455 void Port::freeInternalBuffer() {
456         debugOutput(DEBUG_LEVEL_VERBOSE,
457                     "Freeing internal buffer (%s)\n",m_Name.c_str());
458
459         if(m_buffer) {
460                 free(m_buffer);
461                 m_buffer=0;
462         }
463 }
464
465 bool Port::allocateInternalRingBuffer() {
466         int event_size=getEventSize();
467        
468         debugOutput(DEBUG_LEVEL_VERBOSE,
469                     "Allocating internal buffer of %d events with size %d (%s)\n",
470                     m_buffersize, event_size, m_Name.c_str());
471
472         if(m_ringbuffer) {
473                 debugWarning("already has an internal ringbuffer attached, re-allocating\n");
474                 freeInternalRingBuffer();
475         }
476
477         m_ringbuffer=freebob_ringbuffer_create(m_buffersize * event_size);
478         if (!m_ringbuffer) {
479                 debugFatal("could not allocate internal ringbuffer\n");
480                 m_buffersize=0;
481                 return false;
482         }
483
484         return true;
485 }
486
487 void Port::freeInternalRingBuffer() {
488         debugOutput(DEBUG_LEVEL_VERBOSE,
489                     "Freeing internal ringbuffer (%s)\n",m_Name.c_str());
490
491         if(m_ringbuffer) {
492                 freebob_ringbuffer_free(m_ringbuffer);
493                 m_ringbuffer=0;
494         }
495 }
496
497 }
Note: See TracBrowser for help on using the browser.