Changeset 893

Show
Ignore:
Timestamp:
02/24/08 11:24:55 (13 years ago)
Author:
ppalmers
Message:

improve AMDTP receive performance

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/libffado/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.cpp

    r866 r893  
    4242    : StreamProcessor(parent, ePT_Receive) 
    4343    , m_dimension( dimension ) 
     44    , m_nb_audio_ports( 0 ) 
     45    , m_nb_midi_ports( 0 ) 
     46 
    4447{} 
    4548 
     
    6669    debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this); 
    6770    m_syt_interval = getSytInterval(); 
     71 
     72    if (!initPortCache()) { 
     73        debugError("Could not init port cache\n"); 
     74        return false; 
     75    } 
     76 
    6877    return true; 
    6978} 
     
    187196                        this,nevents,offset); 
    188197 
    189     bool no_problem=true; 
    190  
    191     for ( PortVectorIterator it = m_Ports.begin(); 
    192           it != m_Ports.end(); 
    193           ++it ) 
     198    // update the variable parts of the cache 
     199    updatePortCache(); 
     200 
     201    // decode audio data 
     202    switch(m_StreamProcessorManager.getAudioDataType()) { 
     203        case StreamProcessorManager::eADT_Int24: 
     204            decodeAudioPortsInt24((quadlet_t *)data, offset, nevents); 
     205            break; 
     206        case StreamProcessorManager::eADT_Float: 
     207            decodeAudioPortsFloat((quadlet_t *)data, offset, nevents); 
     208            break; 
     209    } 
     210 
     211    // do midi ports 
     212    decodeMidiPorts((quadlet_t *)data, offset, nevents); 
     213    return true; 
     214
     215 
     216//#ifdef __SSE2__ 
     217#if 0 // SSE code is not ready yet 
     218#include <emmintrin.h> 
     219#warning SSE2 build 
     220 
     221/** 
     222 * @brief demux events to all audio ports (int24) 
     223 * @param data  
     224 * @param offset  
     225 * @param nevents  
     226 */ 
     227void 
     228AmdtpReceiveStreamProcessor::decodeAudioPortsInt24(quadlet_t *data, 
     229                                                    unsigned int offset, 
     230                                                    unsigned int nevents) 
     231
     232    unsigned int j; 
     233    quadlet_t *target_event; 
     234    unsigned int i; 
     235 
     236    for (i = 0; i < m_nb_audio_ports; i++) { 
     237        struct _MBLA_port_cache &p = m_audio_ports.at(i); 
     238        target_event = (quadlet_t *)(data + i); 
     239        assert(nevents + offset <= p.buffer_size ); 
     240 
     241        if(p.buffer && p.enabled) { 
     242            quadlet_t *buffer = (quadlet_t *)(p.buffer); 
     243            buffer += offset; 
     244 
     245            for(j = 0; j < nevents; j += 1) { 
     246                *(buffer)=(ntohl((*target_event) ) & 0x00FFFFFF); 
     247                buffer++; 
     248                target_event+=m_dimension; 
     249            } 
     250        } 
     251    } 
     252
     253 
     254/** 
     255 * @brief demux events to all audio ports (float) 
     256 * @param data  
     257 * @param offset  
     258 * @param nevents  
     259 */ 
     260void 
     261AmdtpReceiveStreamProcessor::decodeAudioPortsFloat(quadlet_t *data, 
     262                                                    unsigned int offset, 
     263                                                    unsigned int nevents) 
     264
     265    unsigned int j; 
     266    quadlet_t *target_event; 
     267    unsigned int i; 
     268    const float multiplier = 1.0f / (float)(0x7FFFFF); 
     269 
     270    for (i = 0; i < m_nb_audio_ports; i++) { 
     271        struct _MBLA_port_cache &p = m_audio_ports.at(i); 
     272        target_event = (quadlet_t *)(data + i); 
     273        assert(nevents + offset <= p.buffer_size ); 
     274 
     275        if(p.buffer && p.enabled) { 
     276            float *buffer = (float *)(p.buffer); 
     277            buffer += offset; 
     278 
     279            for(j = 0; j < nevents; j += 1) { 
     280                unsigned int v = ntohl(*target_event) & 0x00FFFFFF; 
     281                // sign-extend highest bit of 24-bit int 
     282                int tmp = (int)(v << 8) / 256; 
     283                *buffer = tmp * multiplier; 
     284                buffer++; 
     285                target_event+=m_dimension; 
     286            } 
     287        } 
     288    } 
     289
     290 
     291#else 
     292/** 
     293 * @brief demux events to all audio ports (int24) 
     294 * @param data  
     295 * @param offset  
     296 * @param nevents  
     297 */ 
     298void 
     299AmdtpReceiveStreamProcessor::decodeAudioPortsInt24(quadlet_t *data, 
     300                                                    unsigned int offset, 
     301                                                    unsigned int nevents) 
     302
     303    unsigned int j; 
     304    quadlet_t *target_event; 
     305    unsigned int i; 
     306 
     307    for (i = 0; i < m_nb_audio_ports; i++) { 
     308        struct _MBLA_port_cache &p = m_audio_ports.at(i); 
     309        target_event = (quadlet_t *)(data + i); 
     310        assert(nevents + offset <= p.buffer_size ); 
     311 
     312        if(p.buffer && p.enabled) { 
     313            quadlet_t *buffer = (quadlet_t *)(p.buffer); 
     314            buffer += offset; 
     315 
     316            for(j = 0; j < nevents; j += 1) { 
     317                *(buffer)=(ntohl((*target_event) ) & 0x00FFFFFF); 
     318                buffer++; 
     319                target_event+=m_dimension; 
     320            } 
     321        } 
     322    } 
     323
     324 
     325/** 
     326 * @brief demux events to all audio ports (float) 
     327 * @param data  
     328 * @param offset  
     329 * @param nevents  
     330 */ 
     331void 
     332AmdtpReceiveStreamProcessor::decodeAudioPortsFloat(quadlet_t *data, 
     333                                                    unsigned int offset, 
     334                                                    unsigned int nevents) 
     335
     336    unsigned int j; 
     337    quadlet_t *target_event; 
     338    unsigned int i; 
     339    const float multiplier = 1.0f / (float)(0x7FFFFF); 
     340 
     341    for (i = 0; i < m_nb_audio_ports; i++) { 
     342        struct _MBLA_port_cache &p = m_audio_ports.at(i); 
     343        target_event = (quadlet_t *)(data + i); 
     344        assert(nevents + offset <= p.buffer_size ); 
     345 
     346        if(p.buffer && p.enabled) { 
     347            float *buffer = (float *)(p.buffer); 
     348            buffer += offset; 
     349 
     350            for(j = 0; j < nevents; j += 1) { 
     351                unsigned int v = ntohl(*target_event) & 0x00FFFFFF; 
     352                // sign-extend highest bit of 24-bit int 
     353                int tmp = (int)(v << 8) / 256; 
     354                *buffer = tmp * multiplier; 
     355                buffer++; 
     356                target_event+=m_dimension; 
     357            } 
     358        } 
     359    } 
     360
     361 
     362#endif 
     363 
     364/** 
     365 * @brief decode all midi ports in the cache from events 
     366 * @param data  
     367 * @param offset  
     368 * @param nevents  
     369 */ 
     370void 
     371AmdtpReceiveStreamProcessor::decodeMidiPorts(quadlet_t *data, 
     372                                              unsigned int offset, 
     373                                              unsigned int nevents) 
     374
     375    quadlet_t *target_event; 
     376    quadlet_t sample_int; 
     377    unsigned int i,j; 
     378 
     379    for (i = 0; i < m_nb_midi_ports; i++) { 
     380        struct _MIDI_port_cache &p = m_midi_ports.at(i); 
     381        if (p.buffer && p.enabled) { 
     382            uint32_t *buffer = (quadlet_t *)(p.buffer); 
     383            buffer += offset; 
     384 
     385            for (j = p.location;j < nevents; j += 8) { 
     386                target_event = (quadlet_t *) (data + ((j * m_dimension) + p.position)); 
     387                sample_int=ntohl(*target_event); 
     388                // FIXME: this assumes that 2X and 3X speed isn't used, 
     389                // because only the 1X slot is put into the ringbuffer 
     390                if(IEC61883_AM824_GET_LABEL(sample_int) != IEC61883_AM824_LABEL_MIDI_NO_DATA) { 
     391                    sample_int=(sample_int >> 16) & 0x000000FF; 
     392                    sample_int |= 0x01000000; // flag that there is a midi event present 
     393                    *buffer = sample_int; 
     394                    debugOutput(DEBUG_LEVEL_VERBOSE, "Received midi byte %08X on port %p index %d\n", sample_int, p, j-p.location); 
     395                } else { 
     396                    // make sure no event is received 
     397                    *buffer = 0; 
     398                } 
     399                buffer+=8; 
     400            } 
     401        } 
     402    } 
     403
     404 
     405bool 
     406AmdtpReceiveStreamProcessor::initPortCache() { 
     407    // make use of the fact that audio ports are the first ports in 
     408    // the cluster as per AMDTP. so we can sort the ports by position 
     409    // and have very efficient lookups: 
     410    // m_float_ports.at(i).buffer -> audio stream i buffer 
     411    // for midi ports we simply cache all port info since they are (usually) not 
     412    // that numerous 
     413    m_nb_audio_ports = 0; 
     414    m_audio_ports.clear(); 
     415     
     416    m_nb_midi_ports = 0; 
     417    m_midi_ports.clear(); 
     418     
     419    for(PortVectorIterator it = m_Ports.begin(); 
     420        it != m_Ports.end(); 
     421        ++it ) 
    194422    { 
    195         if((*it)->isDisabled()) {continue;}; 
    196  
    197         //FIXME: make this into a static_cast when not DEBUG? 
    198  
    199423        AmdtpPortInfo *pinfo=dynamic_cast<AmdtpPortInfo *>(*it); 
    200424        assert(pinfo); // this should not fail!! 
    201425 
    202         switch(pinfo->getFormat()) { 
    203         case AmdtpPortInfo::E_MBLA: 
    204             if(decodeMBLAEventsToPort(static_cast<AmdtpAudioPort *>(*it), (quadlet_t *)data, offset, nevents)) { 
    205                 debugWarning("Could not decode packet MBLA to port %s",(*it)->getName().c_str()); 
    206                 no_problem=false; 
    207             } 
    208             break; 
    209         case AmdtpPortInfo::E_SPDIF: // still unimplemented 
    210             break; 
    211         case AmdtpPortInfo::E_Midi: 
    212             if(decodeMidiEventsToPort(static_cast<AmdtpMidiPort *>(*it), (quadlet_t *)data, offset, nevents)) { 
    213                 debugWarning("Could not decode packet Midi to port %s",(*it)->getName().c_str()); 
    214                 no_problem=false; 
    215             } 
    216             break; 
    217         default: // ignore 
    218             break; 
    219         } 
    220     } 
    221     return no_problem; 
    222 
    223  
    224 #if USE_SSE 
    225 #error broken 
    226 typedef float v4sf __attribute__ ((vector_size (16))); 
    227 typedef int v4si __attribute__ ((vector_size (16))); 
    228 typedef int v2si __attribute__ ((vector_size (8))); 
    229  
    230 int 
    231 AmdtpReceiveStreamProcessor::decodeMBLAEventsToPort( 
    232                        AmdtpAudioPort *p, quadlet_t *data, 
    233                        unsigned int offset, unsigned int nevents) 
    234 
    235     unsigned int j=0; 
    236     quadlet_t *target_event; 
    237  
    238     target_event=(quadlet_t *)(data + p->getPosition()); 
    239  
    240     static const float multiplier = 1.0f / (float)(0x7FFFFF); 
    241     static const float sse_multiplier[4] __attribute__((aligned(16))) = { 
    242             1.0f / (float)(0x7FFFFF), 
    243             1.0f / (float)(0x7FFFFF), 
    244             1.0f / (float)(0x7FFFFF), 
    245             1.0f / (float)(0x7FFFFF) 
    246     }; 
    247     unsigned int tmp[4]; 
    248  
    249     switch(p->getDataType()) { 
    250         default: 
    251             debugError("bad type: %d\n", p->getDataType()); 
    252             return -1; 
    253         case Port::E_Int24: 
    254             { 
    255                 quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); 
    256  
    257                 assert(nevents + offset <= p->getBufferSize()); 
    258  
    259                 buffer+=offset; 
    260  
    261                 for(j = 0; j < nevents; j += 1) { // decode max nsamples 
    262                     *(buffer)=(ntohl((*target_event) ) & 0x00FFFFFF); 
    263                     buffer++; 
    264                     target_event+=m_dimension; 
     426        switch( pinfo->getFormat() ) 
     427        { 
     428            case AmdtpPortInfo::E_MBLA: 
     429                m_nb_audio_ports++; 
     430                break; 
     431            case AmdtpPortInfo::E_SPDIF: // still unimplemented 
     432                break; 
     433            case AmdtpPortInfo::E_Midi: 
     434                m_nb_midi_ports++; 
     435                break; 
     436            default: // ignore 
     437                break; 
     438        } 
     439    } 
     440 
     441    unsigned int idx; 
     442    for (idx = 0; idx < m_nb_audio_ports; idx++) { 
     443        for(PortVectorIterator it = m_Ports.begin(); 
     444            it != m_Ports.end(); 
     445            ++it ) 
     446        { 
     447            AmdtpPortInfo *pinfo=dynamic_cast<AmdtpPortInfo *>(*it); 
     448            debugOutput(DEBUG_LEVEL_VERY_VERBOSE, 
     449                        "idx %u: looking at port %s at position %u\n", 
     450                        idx, (*it)->getName().c_str(), pinfo->getPosition()); 
     451            if(pinfo->getPosition() == idx) { 
     452                struct _MBLA_port_cache p; 
     453                p.port = dynamic_cast<AmdtpAudioPort *>(*it); 
     454                if(p.port == NULL) { 
     455                    debugError("Port is not an AmdtpAudioPort!\n"); 
     456                    return false; 
    265457                } 
    266             } 
    267             break; 
    268         case Port::E_Float: 
    269             { 
    270                 float *buffer=(float *)(p->getBufferAddress()); 
    271  
    272                 assert(nevents + offset <= p->getBufferSize()); 
    273  
    274                 buffer += offset; 
    275                 j = 0; 
    276                 if(nevents > 3) { 
    277                     for(j = 0; j < nevents-3; j += 4) { 
    278                         tmp[0] = ntohl(*target_event); 
    279                         target_event += m_dimension; 
    280                         tmp[1] = ntohl(*target_event); 
    281                         target_event += m_dimension; 
    282                         tmp[2] = ntohl(*target_event); 
    283                         target_event += m_dimension; 
    284                         tmp[3] = ntohl(*target_event); 
    285                         target_event += m_dimension; 
    286                         asm("pslld $8, %[in2]\n\t" // sign extend 24th bit 
    287                                 "pslld $8, %[in1]\n\t" 
    288                                 "psrad $8, %[in2]\n\t" 
    289                                 "psrad $8, %[in1]\n\t" 
    290                                 "cvtpi2ps %[in2], %%xmm0\n\t" 
    291                                 "movlhps %%xmm0, %%xmm0\n\t" 
    292                                 "cvtpi2ps %[in1], %%xmm0\n\t" 
    293                                 "mulps %[ssemult], %%xmm0\n\t" 
    294                                 "movups %%xmm0, %[floatbuff]" 
    295                             : [floatbuff] "=m" (*(v4sf*)buffer) 
    296                             : [in1] "y" (*(v2si*)tmp), 
    297                         [in2] "y" (*(v2si*)(tmp+2)), 
    298                         [ssemult] "x" (*(v4sf*)sse_multiplier) 
    299                             : "xmm0"); 
    300                         buffer += 4; 
    301                     } 
    302                 } 
    303                 for(; j < nevents; ++j) { // decode max nsamples 
    304                     unsigned int v = ntohl(*target_event) & 0x00FFFFFF; 
    305                     // sign-extend highest bit of 24-bit int 
    306                     int tmp = (int)(v << 8) / 256; 
    307                     *buffer = tmp * multiplier; 
    308  
    309                     buffer++; 
    310                     target_event += m_dimension; 
    311                 } 
    312                 asm volatile("emms"); 
    313                 break; 
    314             } 
    315             break; 
    316     } 
    317  
    318     return 0; 
    319 
    320  
    321 int 
    322 AmdtpReceiveStreamProcessor::decodeMidiEventsToPort( 
    323                        AmdtpMidiPort *p, quadlet_t *data, 
    324                        unsigned int offset, unsigned int nevents) 
    325 
    326     #warning implement 
    327 
    328  
    329 #else 
    330  
    331 int 
    332 AmdtpReceiveStreamProcessor::decodeMBLAEventsToPort( 
    333                        AmdtpAudioPort *p, quadlet_t *data, 
    334                        unsigned int offset, unsigned int nevents) 
    335 
    336     unsigned int j=0; 
    337     quadlet_t *target_event; 
    338  
    339     target_event=(quadlet_t *)(data + p->getPosition()); 
    340  
    341     switch(m_StreamProcessorManager.getAudioDataType()) { 
    342         default: 
    343             debugError("bad type: %d\n", m_StreamProcessorManager.getAudioDataType()); 
    344             return -1; 
    345         case StreamProcessorManager::eADT_Int24: 
    346             { 
    347                 quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); 
    348  
    349                 assert(nevents + offset <= p->getBufferSize()); 
    350  
    351                 buffer+=offset; 
    352  
    353                 for(j = 0; j < nevents; j += 1) { // decode max nsamples 
    354                     *(buffer)=(ntohl((*target_event) ) & 0x00FFFFFF); 
    355                     buffer++; 
    356                     target_event+=m_dimension; 
    357                 } 
    358             } 
    359             break; 
    360         case StreamProcessorManager::eADT_Float: 
    361             { 
    362                 const float multiplier = 1.0f / (float)(0x7FFFFF); 
    363                 float *buffer=(float *)(p->getBufferAddress()); 
    364  
    365                 assert(nevents + offset <= p->getBufferSize()); 
    366  
    367                 buffer+=offset; 
    368  
    369                 for(j = 0; j < nevents; j += 1) { // decode max nsamples 
    370  
    371                     unsigned int v = ntohl(*target_event) & 0x00FFFFFF; 
    372                     // sign-extend highest bit of 24-bit int 
    373                     int tmp = (int)(v << 8) / 256; 
    374  
    375                     *buffer = tmp * multiplier; 
    376  
    377                     buffer++; 
    378                     target_event+=m_dimension; 
    379                 } 
    380             } 
    381             break; 
    382     } 
    383  
    384     return 0; 
    385 
    386  
    387 int 
    388 AmdtpReceiveStreamProcessor::decodeMidiEventsToPort( 
    389                        AmdtpMidiPort *p, quadlet_t *data, 
    390                        unsigned int offset, unsigned int nevents) 
    391 
    392     unsigned int j=0; 
    393     quadlet_t *target_event; 
    394     quadlet_t sample_int; 
    395     unsigned int position = p->getPosition(); 
    396     unsigned int location = p->getLocation(); 
    397  
    398     quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); 
    399  
    400     assert(nevents + offset <= p->getBufferSize()); 
    401  
    402     buffer+=offset; 
    403  
    404     // clear 
    405     memset(buffer, 0, nevents * 4); 
    406  
    407     // assumes that dbc%8 == 0, which is always true if data points to the 
    408     // start of a packet in blocking mode 
    409     // midi events that belong to the same time mpx-ed block should all be 
    410     // timed at the SYT timestamp of the packet. This basically means that they 
    411     // all correspond to the first audio frame in the packet. 
    412     for(j = location; j < nevents; j += 8) { 
    413         target_event=(quadlet_t *)(data + ((j * m_dimension) + position)); 
    414         sample_int=ntohl(*target_event); 
    415         // FIXME: this assumes that 2X and 3X speed isn't used, 
    416         // because only the 1X slot is put into the ringbuffer 
    417         if(IEC61883_AM824_GET_LABEL(sample_int) != IEC61883_AM824_LABEL_MIDI_NO_DATA) { 
    418             sample_int=(sample_int >> 16) & 0x000000FF; 
    419             sample_int |= 0x01000000; // flag that there is a midi event present 
    420             *buffer = sample_int; 
    421             debugOutput(DEBUG_LEVEL_VERBOSE, "Received midi byte %08X on port %p index %d\n", sample_int, p, j-location); 
    422         } 
    423         buffer += 8; // skip 8 frames 
    424     } 
    425  
    426     return 0; 
    427 
    428  
    429 #endif 
     458                p.buffer = NULL; // to be filled by updatePortCache 
     459                #ifdef DEBUG 
     460                p.buffer_size = (*it)->getBufferSize(); 
     461                #endif 
     462 
     463                m_audio_ports.push_back(p); 
     464                debugOutput(DEBUG_LEVEL_VERBOSE, 
     465                            "Cached port %s at position %u\n", 
     466                            p.port->getName().c_str(), idx); 
     467                goto next_index; 
     468            } 
     469        } 
     470        debugError("No MBLA port found for position %d\n", idx); 
     471        return false; 
     472next_index: 
     473        continue; 
     474    } 
     475 
     476    for(PortVectorIterator it = m_Ports.begin(); 
     477        it != m_Ports.end(); 
     478        ++it ) 
     479    { 
     480        AmdtpPortInfo *pinfo=dynamic_cast<AmdtpPortInfo *>(*it); 
     481        debugOutput(DEBUG_LEVEL_VERY_VERBOSE, 
     482                    "idx %u: looking at port %s at position %u, location %u\n", 
     483                    idx, (*it)->getName().c_str(), pinfo->getPosition(), pinfo->getLocation()); 
     484        if ((*it)->getPortType() == Port::E_Midi) { 
     485            struct _MIDI_port_cache p; 
     486            p.port = dynamic_cast<AmdtpMidiPort *>(*it); 
     487            if(p.port == NULL) { 
     488                debugError("Port is not an AmdtpMidiPort!\n"); 
     489                return false; 
     490            } 
     491            p.position = pinfo->getPosition(); 
     492            p.location = pinfo->getLocation(); 
     493            p.buffer = NULL; // to be filled by updatePortCache 
     494            #ifdef DEBUG 
     495            p.buffer_size = (*it)->getBufferSize(); 
     496            #endif 
     497 
     498            m_midi_ports.push_back(p); 
     499            debugOutput(DEBUG_LEVEL_VERBOSE, 
     500                        "Cached port %s at position %u, location %u\n", 
     501                        p.port->getName().c_str(), p.position, p.location); 
     502        } 
     503    } 
     504 
     505    return true; 
     506
     507 
     508void 
     509AmdtpReceiveStreamProcessor::updatePortCache() { 
     510    unsigned int idx; 
     511    for (idx = 0; idx < m_nb_audio_ports; idx++) { 
     512        struct _MBLA_port_cache& p = m_audio_ports.at(idx); 
     513        AmdtpAudioPort *port = p.port; 
     514        p.buffer = port->getBufferAddress(); 
     515        p.enabled = !port->isDisabled(); 
     516    } 
     517    for (idx = 0; idx < m_nb_midi_ports; idx++) { 
     518        struct _MIDI_port_cache& p = m_midi_ports.at(idx); 
     519        AmdtpMidiPort *port = p.port; 
     520        p.buffer = port->getBufferAddress(); 
     521        p.enabled = !port->isDisabled(); 
     522    } 
     523
     524 
    430525} // end of namespace Streaming 
  • trunk/libffado/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.h

    r864 r893  
    101101 
    102102private: 
    103     int decodeMBLAEventsToPort(AmdtpAudioPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); 
    104     int decodeMidiEventsToPort(AmdtpMidiPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents); 
     103    void decodeAudioPortsFloat(quadlet_t *data, unsigned int offset, unsigned int nevents); 
     104    void decodeAudioPortsInt24(quadlet_t *data, unsigned int offset, unsigned int nevents); 
     105    void decodeMidiPorts(quadlet_t *data, unsigned int offset, unsigned int nevents); 
    105106 
    106107    unsigned int getSytInterval(); 
     
    108109    int m_dimension; 
    109110    unsigned int m_syt_interval; 
     111 
     112private: // local port caching for performance 
     113    struct _MBLA_port_cache { 
     114        AmdtpAudioPort*     port; 
     115        void*               buffer; 
     116        bool                enabled; 
     117#ifdef DEBUG 
     118        unsigned int        buffer_size; 
     119#endif 
     120    }; 
     121    std::vector<struct _MBLA_port_cache> m_audio_ports; 
     122    unsigned int m_nb_audio_ports; 
     123 
     124    struct _MIDI_port_cache { 
     125        AmdtpMidiPort*      port; 
     126        void*               buffer; 
     127        bool                enabled; 
     128        unsigned int        position; 
     129        unsigned int        location; 
     130#ifdef DEBUG 
     131        unsigned int        buffer_size; 
     132#endif 
     133    }; 
     134    std::vector<struct _MIDI_port_cache> m_midi_ports; 
     135    unsigned int m_nb_midi_ports; 
     136 
     137    bool initPortCache(); 
     138    void updatePortCache(); 
    110139}; 
    111140