Index: /branches/ppalmers-streaming/src/libstreaming/StreamProcessor.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/StreamProcessor.h (revision 494) +++ /branches/ppalmers-streaming/src/libstreaming/StreamProcessor.h (revision 703) @@ -91,4 +91,6 @@ virtual bool putFrames(unsigned int nbframes, int64_t ts) = 0; ///< transfer the buffer contents from client virtual bool getFrames(unsigned int nbframes, int64_t ts) = 0; ///< transfer the buffer contents to the client + virtual bool putFramesDry(unsigned int nbframes, int64_t ts) = 0; ///< dry-process the buffer contents + virtual bool getFramesDry(unsigned int nbframes, int64_t ts) = 0; ///< dry-process the buffer contents virtual bool reset(); ///< reset the streams & buffers (e.g. after xrun) @@ -149,4 +151,15 @@ */ virtual bool canClientTransferFrames(unsigned int nframes) = 0; + + /** + * @brief drop nframes from the internal buffer + * + * this function drops nframes from the internal buffers, without any + * specification on what frames are dropped. Timestamps are not updated. + * + * @param nframes number of frames + * @return true if the operation was successful + */ + virtual bool dropFrames(unsigned int nframes); /** @@ -242,4 +255,5 @@ {return RAW1394_ISO_STOP;}; virtual bool putFrames(unsigned int nbframes, int64_t ts) {return false;}; + virtual bool putFramesDry(unsigned int nbframes, int64_t ts) {return false;}; virtual enum raw1394_iso_disposition putPacket(unsigned char *data, unsigned int length, @@ -276,9 +290,6 @@ unsigned int cycle, unsigned int dropped) {return RAW1394_ISO_STOP;}; virtual bool getFrames(unsigned int nbframes, int64_t ts) {return false;}; - - virtual enum raw1394_iso_disposition - getPacket(unsigned char *data, unsigned int *length, - unsigned char *tag, unsigned char *sy, - int cycle, unsigned int dropped, unsigned int max_length) = 0; + virtual bool getFramesDry(unsigned int nbframes, int64_t ts) {return false;}; + virtual void setVerboseLevel(int l); Index: /branches/ppalmers-streaming/src/libstreaming/motu/MotuPort.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/motu/MotuPort.h (revision 703) +++ /branches/ppalmers-streaming/src/libstreaming/motu/MotuPort.h (revision 703) @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 by Pieter Palmers + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation; + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef __FFADO_MOTUPORT__ +#define __FFADO_MOTUPORT__ + +/** + * This file implements the ports used in Motu devices + */ + +#include "../debugmodule/debugmodule.h" +#include "Port.h" +#include "MotuPortInfo.h" + +namespace Streaming { + +/*! +\brief The Base Class for Motu Audio Port + + +*/ +class MotuAudioPort + : public AudioPort, public MotuPortInfo +{ + +public: + + MotuAudioPort(std::string name, + enum E_Direction direction, + int position, + int size) + : AudioPort(name, direction), + MotuPortInfo( position, size) // TODO: add more port information parameters here if nescessary + {}; + + virtual ~MotuAudioPort() {}; + +protected: + +}; + +/*! +\brief The Base Class for an Motu Midi Port + + +*/ +class MotuMidiPort + : public MidiPort, public MotuPortInfo +{ + +public: + + MotuMidiPort(std::string name, + enum E_Direction direction, + int position) + : MidiPort(name, direction), + MotuPortInfo(position, 0) // TODO: add more port information parameters here if nescessary + {}; + + + virtual ~MotuMidiPort() {}; + +protected: + +}; + +/*! +\brief The Base Class for an Motu Control Port + + +*/ +class MotuControlPort + : public ControlPort, public MotuPortInfo +{ + +public: + + MotuControlPort(std::string name, + enum E_Direction direction, + int position) + : ControlPort(name, direction), + MotuPortInfo(position, 2) // TODO: add more port information parameters here if nescessary + {}; + + + virtual ~MotuControlPort() {}; + +protected: + +}; + +} // end of namespace Streaming + +#endif /* __FFADO_MOTUPORT__ */ + Index: /branches/ppalmers-streaming/src/libstreaming/motu/MotuStreamProcessor.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/motu/MotuStreamProcessor.cpp (revision 703) +++ /branches/ppalmers-streaming/src/libstreaming/motu/MotuStreamProcessor.cpp (revision 703) @@ -0,0 +1,1470 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 by Pieter Palmers + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation; + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "MotuStreamProcessor.h" +#include "Port.h" +#include "MotuPort.h" + +#include + +#include + +#include "cycletimer.h" + +// in ticks +#define TRANSMIT_TRANSFER_DELAY 6000U +// the number of cycles to send a packet in advance of it's timestamp +#define TRANSMIT_ADVANCE_CYCLES 1U + +namespace Streaming { + +IMPL_DEBUG_MODULE( MotuTransmitStreamProcessor, MotuTransmitStreamProcessor, DEBUG_LEVEL_NORMAL ); +IMPL_DEBUG_MODULE( MotuReceiveStreamProcessor, MotuReceiveStreamProcessor, DEBUG_LEVEL_NORMAL ); + +// Set to 1 to enable the generation of a 1 kHz test tone in analog output 1 +#define TESTTONE 1 + +// A macro to extract specific bits from a native endian quadlet +#define get_bits(_d,_start,_len) (((_d)>>((_start)-(_len)+1)) & ((1<<(_len))-1)) + +// Convert an SPH timestamp as received from the MOTU to a full timestamp in ticks. +static inline uint32_t sphRecvToFullTicks(uint32_t sph, uint32_t ct_now) { + +uint32_t timestamp = CYCLE_TIMER_TO_TICKS(sph & 0x1ffffff); +uint32_t now_cycles = CYCLE_TIMER_GET_CYCLES(ct_now); + +uint32_t ts_sec = CYCLE_TIMER_GET_SECS(ct_now); + // If the cycles have wrapped, correct ts_sec so it represents when timestamp + // was received. The timestamps sent by the MOTU are always 1 or two cycles + // in advance of the cycle timer (reasons unknown at this stage). In addition, + // iso buffering can delay the arrival of packets for quite a number of cycles + // (have seen a delay >12 cycles). + // Every so often we also see sph wrapping ahead of ct_now, so deal with that + // too. + if (CYCLE_TIMER_GET_CYCLES(sph) > now_cycles + 1000) { + if (ts_sec) + ts_sec--; + else + ts_sec = 127; + } else + if (now_cycles > CYCLE_TIMER_GET_CYCLES(sph) + 1000) { + if (ts_sec == 127) + ts_sec = 0; + else + ts_sec++; + } + return timestamp + ts_sec*TICKS_PER_SECOND; +} + +// Convert a full timestamp into an SPH timestamp as required by the MOTU +static inline uint32_t fullTicksToSph(int64_t timestamp) { + return TICKS_TO_CYCLE_TIMER(timestamp) & 0x1ffffff; +} + +/* transmit */ +MotuTransmitStreamProcessor::MotuTransmitStreamProcessor(int port, int framerate, + unsigned int event_size) + : TransmitStreamProcessor(port, framerate), m_event_size(event_size), + m_tx_dbc(0), + m_startup_count(-1), m_closedown_count(-1), m_streaming_active(0) { +} + +MotuTransmitStreamProcessor::~MotuTransmitStreamProcessor() { + +} + +bool MotuTransmitStreamProcessor::init() { + + debugOutput( DEBUG_LEVEL_VERBOSE, "Initializing (%p)...\n"); + // call the parent init + // this has to be done before allocating the buffers, + // because this sets the buffersizes from the processormanager + if(!TransmitStreamProcessor::init()) { + debugFatal("Could not do base class init (%p)\n",this); + return false; + } + + return true; +} + +void MotuTransmitStreamProcessor::setVerboseLevel(int l) { + setDebugLevel(l); // sets the debug level of the current object + TransmitStreamProcessor::setVerboseLevel(l); // also set the level of the base class +} + + +enum raw1394_iso_disposition +MotuTransmitStreamProcessor::getPacket(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_length) { + + int fc; + ffado_timestamp_t ts_tmp; + quadlet_t *quadlet = (quadlet_t *)data; + signed int i; + + // The number of events per packet expected by the MOTU is solely + // dependent on the current sample rate. An 'event' is one sample from + // all channels plus possibly other midi and control data. + signed n_events = m_framerate<=48000?8:(m_framerate<=96000?16:32); + + m_last_cycle=cycle; + + // determine if we want to send a packet or not + // note that we can't use getCycleTimer directly here, + // because packets are queued in advance. This means that + // we the packet we are constructing will be sent out + // on 'cycle', not 'now'. + unsigned int ctr=m_handler->getCycleTimer(); + int now_cycles = (int)CYCLE_TIMER_GET_CYCLES(ctr); + + // the difference between the cycle this + // packet is intended for and 'now' + int cycle_diff = diffCycles(cycle, now_cycles); + + // Signal that streaming is still active + m_streaming_active = 1; + + // as long as the cycle parameter is not in sync with + // the current time, the stream is considered not + // to be 'running' + // NOTE: this works only at startup + if (!m_running && cycle_diff >= 0 && cycle >= 0) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Xmit StreamProcessor %p started running at cycle %d\n",this, cycle); + m_running=true; + } + + if (!m_disabled && m_is_disabled) { + // this means that we are trying to enable + + // check if we are on or past the enable point + signed int cycles_past_enable=diffCycles(cycle, m_cycle_to_enable_at); + + if (cycles_past_enable >= 0) { + m_is_disabled=false; + + debugOutput(DEBUG_LEVEL_VERBOSE,"Enabling Tx StreamProcessor %p at %u\n", this, cycle); + + // initialize the buffer head & tail + m_SyncSource->m_data_buffer->getBufferHeadTimestamp(&ts_tmp, &fc); // thread safe + int64_t ts_head = (int64_t)ts_tmp; + + // the number of cycles the sync source lags (> 0) + // or leads (< 0) + int sync_lag_cycles=diffCycles(cycle, m_SyncSource->getLastCycle()); + + // account for the cycle lag between sync SP and this SP + // the last update of the sync source's timestamps was sync_lag_cycles + // cycles before the cycle we are calculating the timestamp for. + // if we were to use one-frame buffers, you would expect the + // frame that is sent on cycle CT to have a timestamp T1. + // ts_head however is for cycle CT-sync_lag_cycles, and lies + // therefore sync_lag_cycles * TICKS_PER_CYCLE earlier than + // T1. + ts_head = addTicks(ts_head, sync_lag_cycles * TICKS_PER_CYCLE); + +// These are just copied from AmdtpStreamProcessor. At some point we should +// verify that they make sense for the MOTU. + ts_head = substractTicks(ts_head, TICKS_PER_CYCLE); + // account for the number of cycles we are too late to enable + ts_head = addTicks(ts_head, cycles_past_enable * TICKS_PER_CYCLE); + // account for one extra packet of frames +// For the MOTU this subtraction doesn't seem necessary, and in general just makes it take +// longer to achieve stable sync. +// ts_head = substractTicks(ts_head, +// (uint32_t)((float)n_events * m_SyncSource->m_data_buffer->getRate())); +// ts_head = substractTicks(ts_head, +// (uint32_t)(m_SyncSource->m_data_buffer->getRate())); + m_data_buffer->setBufferTailTimestamp(ts_head); + + // Set up the startup count which keeps the output muted during + // sync stabilisation at startup. For now we go for about 2 seconds of + // muting. Note that this is a count of *packets*, not frames. + m_startup_count = 2*m_framerate/n_events; + + #ifdef DEBUG + if ((unsigned int)m_data_buffer->getFrameCounter() != m_data_buffer->getBufferSize()) { + debugWarning("m_data_buffer->getFrameCounter() != m_data_buffer->getBufferSize()\n"); + } + #endif + debugOutput(DEBUG_LEVEL_VERBOSE,"XMIT TS SET: TS=%10lld, LAG=%03d, FC=%4d\n", + ts_head, sync_lag_cycles, m_data_buffer->getFrameCounter()); + } else { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, + "will enable StreamProcessor %p at %u, now is %d\n", + this, m_cycle_to_enable_at, cycle); + } + } else if (m_disabled && !m_is_disabled) { + // trying to disable + debugOutput(DEBUG_LEVEL_VERBOSE,"disabling StreamProcessor %p at %u\n", + this, cycle); + m_is_disabled=true; + m_startup_count = -1; + } + + // Do housekeeping expected for all packets sent to the MOTU, even + // for packets containing no audio data. + *sy = 0x00; + *tag = 1; // All MOTU packets have a CIP-like header + + // the base timestamp is the one of the next sample in the buffer + m_data_buffer->getBufferHeadTimestamp(&ts_tmp, &fc); // thread safe + int64_t timestamp = (int64_t)ts_tmp; + + // we send a packet some cycles in advance, to avoid the + // following situation: + // suppose we are only a few ticks away from + // the moment to send this packet. therefore we decide + // not to send the packet, but send it in the next cycle. + // This means that the next time point will be 3072 ticks + // later, making that the timestamp will be expired when the + // packet is sent, unless TRANSFER_DELAY > 3072. + // this means that we need at least one cycle of extra buffering. + int64_t ticks_to_advance = TICKS_PER_CYCLE * TRANSMIT_ADVANCE_CYCLES; + + // if cycle lies cycle_diff cycles in the future, we should + // queue this packet cycle_diff * TICKS_PER_CYCLE earlier than + // we would if it were to be sent immediately. + ticks_to_advance += (int64_t)cycle_diff * TICKS_PER_CYCLE; + + // time until the packet is to be sent (if <= 0: send packet) + // For Amdtp this looked like + // // determine the 'now' time in ticks + // uint64_t cycle_timer=CYCLE_TIMER_TO_TICKS(ctr); + // int32_t until_next=diffTicks(timestamp, cycle_timer + ticks_to_advance); + // However, this didn't seem to work well with the MOTU's buffer structure. + // For now we'll revert to the "send trigger" we used earlier since this + // seems to work. + int32_t until_next = (cycle >= TICKS_TO_CYCLES(timestamp))?-1:1; + + // Size of a single data frame in quadlets + unsigned dbs = m_event_size / 4; + + // don't process the stream when it is not enabled, not running + // or when the next sample is not due yet. + if((until_next>0) || m_is_disabled || !m_running) { + // send dummy packet + + // construct the packet CIP-like header. Even if this is a data-less + // packet the dbs field is still set as if there were data blocks + // present. For data-less packets the dbc is the same as the previously + // transmitted block. + *quadlet = htonl(0x00000400 | ((getNodeId()&0x3f)<<24) | m_tx_dbc | (dbs<<16)); + quadlet++; + *quadlet = htonl(0x8222ffff); + quadlet++; + *length = 8; + + return RAW1394_ISO_DEFER; + } + + // add the transmit transfer delay to construct the playout time + ffado_timestamp_t ts=addTicks(timestamp, TRANSMIT_TRANSFER_DELAY); + + // Only read frames from the tx buffer when we're not in the process of + // stopping. When preparing for stop the buffer isn't necessarily being + // replinished so it's possible to cause a buffer underflow during + // shutdown if the buffer is read during this time. + if (m_closedown_count!=-1 || m_data_buffer->readFrames(n_events, (char *)(data + 8))) { + float ticks_per_frame = m_SyncSource->m_data_buffer->getRate(); + +#if TESTTONE + // FIXME: remove this hacked in 1 kHz test signal to + // analog-1 when testing is complete. Note that the tone is + // *never* added during closedown. + if (m_closedown_count<0) { + signed int int_tpf = (int)ticks_per_frame; + unsigned char *sample = data+8+16; + for (i=0; i= 24576000) { + a_cx -= 24576000; + } + } + } +#endif + // Increment the dbc (data block count). This is only done if the + // packet will contain events - that is, we are due to send some + // data. Otherwise a pad packet is sent which contains the DBC of + // the previously sent packet. This regime also means that the very + // first packet containing data will have a DBC of n_events, which + // matches what is observed from other systems. + m_tx_dbc += n_events; + if (m_tx_dbc > 0xff) + m_tx_dbc -= 0x100; + + // construct the packet CIP-like header. Even if this is a data-less + // packet the dbs field is still set as if there were data blocks + // present. For data-less packets the dbc is the same as the previously + // transmitted block. + *quadlet = htonl(0x00000400 | ((getNodeId()&0x3f)<<24) | m_tx_dbc | (dbs<<16)); + quadlet++; + *quadlet = htonl(0x8222ffff); + quadlet++; + + *length = n_events*m_event_size + 8; + + // Zero out data if we're in closedown or startup + if (m_closedown_count>=0 || m_startup_count>=0) { + memset(data+8,0,n_events*m_event_size); + } + + // Account for this packet's frames during startup / closedown. Note: + // * m_startup_count: -1 = not in startup delay, >-1 = in startup delay. + // * m_closedown_count: -1 = not in closedown mode, 0 = closedown + // preparation now finished, >0 = closedown preparation in + // progress. + if (m_closedown_count > 0) + m_closedown_count--; + if (m_startup_count >= 0) + m_startup_count--; + + // Set up each frames's SPH. + for (i=0; igetBufferSize()*m_ticks_per_frame); + + m_data_buffer->setTickOffset(offset); + + // reset all non-device specific stuff + // i.e. the iso stream and the associated ports + if (!TransmitStreamProcessor::reset()) { + debugFatal("Could not do base class reset\n"); + return false; + } + + // we should prefill the event buffer + if (!prefill()) { + debugFatal("Could not prefill buffers\n"); + return false; + } + + return true; +} + +bool MotuTransmitStreamProcessor::prepare() { + + debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing...\n"); + + // prepare all non-device specific stuff + // i.e. the iso stream and the associated ports + if (!TransmitStreamProcessor::prepare()) { + debugFatal("Could not prepare base class\n"); + return false; + } + + m_PeriodStat.setName("XMT PERIOD"); + m_PacketStat.setName("XMT PACKET"); + m_WakeupStat.setName("XMT WAKEUP"); + + debugOutput( DEBUG_LEVEL_VERBOSE, "Event size: %d\n", m_event_size); + + // allocate the event buffer + unsigned int ringbuffer_size_frames=m_nb_buffers * m_period; + + // allocate the internal buffer + m_ticks_per_frame = (TICKS_PER_SECOND*1.0) / ((float)m_framerate); + + assert(m_data_buffer); + // Note: terminology is slightly confused here. From the point of view + // of the buffer the event size is the size of a single complete "event" + // in the MOTU datastream, which consists of one sample from each audio + // channel plus a timestamp and other control data. Almost by + // definition then, the buffer's "events per frame" must be 1. With + // these values, data copies to/from the MOTU data stream can be handled + // by the generic copying functions. + m_data_buffer->setBufferSize(ringbuffer_size_frames); + m_data_buffer->setEventSize(m_event_size); + m_data_buffer->setEventsPerFrame(1); + + m_data_buffer->setUpdatePeriod(m_period); + m_data_buffer->setNominalRate(m_ticks_per_frame); + + // FIXME: check if the timestamp wraps at one second + m_data_buffer->setWrapValue(128L*TICKS_PER_SECOND); + + m_data_buffer->prepare(); + + // Set the parameters of ports we can: we want the audio ports to be + // period buffered, and the midi ports to be packet buffered. + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Setting up port %s\n",(*it)->getName().c_str()); + if(!(*it)->setBufferSize(m_period)) { + debugFatal("Could not set buffer size to %d\n",m_period); + return false; + } + + switch ((*it)->getPortType()) { + case Port::E_Audio: + if (!(*it)->setSignalType(Port::E_PeriodSignalled)) { + debugFatal("Could not set signal type to PeriodSignalling"); + return false; + } + break; + + case Port::E_Midi: + if (!(*it)->setSignalType(Port::E_PacketSignalled)) { + debugFatal("Could not set signal type to PacketSignalling"); + return false; + } + if (!(*it)->setBufferType(Port::E_RingBuffer)) { + debugFatal("Could not set buffer type"); + return false; + } + if (!(*it)->setDataType(Port::E_MidiEvent)) { + debugFatal("Could not set data type"); + return false; + } + // FIXME: probably need rate control too. See + // Port::useRateControl() and AmdtpStreamProcessor. + break; + + case Port::E_Control: + if (!(*it)->setSignalType(Port::E_PeriodSignalled)) { + debugFatal("Could not set signal type to PeriodSignalling"); + return false; + } + break; + + default: + debugWarning("Unsupported port type specified\n"); + break; + } + } + + // The API specific settings of the ports are already set before + // this routine is called, therefore we can init&prepare the ports + if (!initPorts()) { + debugFatal("Could not initialize ports!\n"); + return false; + } + + if(!preparePorts()) { + debugFatal("Could not initialize ports!\n"); + return false; + } + + return true; +} + +bool MotuTransmitStreamProcessor::prepareForStop() { + + // If the stream is disabled or isn't running there's no need to + // wait since the MOTU *should* still be in a "zero data" state. + // + // If the m_streaming_active flag is 0 it indicates that the + // transmit callback hasn't been called since a closedown was + // requested when this function was last called. This effectively + // signifies that the streaming thread has been exitted due to an + // xrun in either the receive or transmit handlers. In this case + // there's no point in waiting for the closedown count to hit zero + // because it never will; the zero data will never get to the MOTU. + // It's best to allow an immediate stop and let the xrun handler + // proceed as best it can. + // + // The ability to detect the lack of streaming also prevents the + // "wait for stop" in the stream processor manager's stop() method + // from hitting its timeout which in turn seems to increase the + // probability of a successful recovery. + if (m_is_disabled || !isRunning() || !m_streaming_active) + return true; + + if (m_closedown_count < 0) { + // No closedown has been initiated, so start one now. Set + // the closedown count to the number of zero packets which + // will be sent to the MOTU before closing off the iso + // streams. FIXME: 128 packets (each containing 8 frames at + // 48 kHz) is the experimentally-determined figure for 48 + // kHz with a period size of 1024. It seems that at least + // one period of zero samples need to be sent to allow for + // inter-thread communication occuring on period boundaries. + // This needs to be confirmed for other rates and period + // sizes. + signed n_events = m_framerate<=48000?8:(m_framerate<=96000?16:32); + m_closedown_count = m_period / n_events; + + // Set up a test to confirm that streaming is still active. + // If the streaming function hasn't been called by the next + // iteration through this function there's no point in + // continuing since it means the zero data will never get to + // the MOTU. + m_streaming_active = 0; + return false; + } + + // We are "go" for closedown once all requested zero packets + // (initiated by a previous call to this function) have been sent to + // the MOTU. + return m_closedown_count == 0; +} + +bool MotuTransmitStreamProcessor::prepareForStart() { +// Reset some critical variables required so the stream starts cleanly. This +// method is called once on every stream restart. Initialisations which should +// be done once should be placed in the init() method instead. + m_running = 0; + m_closedown_count = -1; + m_streaming_active = 0; + + // At this point we'll also disable the stream processor here. + // At this stage stream processors are always explicitly re-enabled + // after being started, so by starting in the disabled state we + // ensure that every start will be exactly the same. + disable(); + + return true; +} + +bool MotuTransmitStreamProcessor::prepareForEnable(uint64_t time_to_enable_at) { + + debugOutput(DEBUG_LEVEL_VERBOSE,"Preparing to enable...\n"); + + // for the transmit SP, we have to initialize the + // buffer timestamp to something sane, because this timestamp + // is used when it is SyncSource + + // the time we initialize to will determine the time at which + // the first sample in the buffer will be sent, so we should + // make it at least 'time_to_enable_at' + + uint64_t now=m_handler->getCycleTimer(); + unsigned int now_secs=CYCLE_TIMER_GET_SECS(now); + + // check if a wraparound on the secs will happen between + // now and the time we start + int until_enable=(int)time_to_enable_at - (int)CYCLE_TIMER_GET_CYCLES(now); + + if(until_enable>4000) { + // wraparound on CYCLE_TIMER_GET_CYCLES(now) + // this means that we are late starting up, + // and that the start lies in the previous second + if (now_secs==0) now_secs=127; + else now_secs--; + } else if (until_enable<-4000) { + // wraparound on time_to_enable_at + // this means that we are early and that the start + // point lies in the next second + now_secs++; + if (now_secs>=128) now_secs=0; + } + +//// uint64_t ts_head= now_secs*TICKS_PER_SECOND; +// uint64_t ts_head = time_to_enable_at*TICKS_PER_CYCLE; + uint64_t ts_head= now_secs*TICKS_PER_SECOND; + ts_head+=time_to_enable_at*TICKS_PER_CYCLE; + + // we also add the nb of cycles we transmit in advance + ts_head=addTicks(ts_head, TRANSMIT_ADVANCE_CYCLES*TICKS_PER_CYCLE); + + m_data_buffer->setBufferTailTimestamp(ts_head); + + if (!StreamProcessor::prepareForEnable(time_to_enable_at)) { + debugError("StreamProcessor::prepareForEnable failed\n"); + return false; + } + + return true; +} + +bool MotuTransmitStreamProcessor::transferSilence(unsigned int size) { + bool retval; + + // This function should tranfer 'size' frames of 'silence' to the event buffer + char *dummybuffer=(char *)calloc(size,m_event_size); + + transmitSilenceBlock(dummybuffer, size, 0); + + // add the silence data to the ringbuffer + if(m_data_buffer->writeFrames(size, dummybuffer, 0)) { + retval=true; + } else { + debugWarning("Could not write to event buffer\n"); + retval=false; + } + + free(dummybuffer); + + return retval; +} + +bool MotuTransmitStreamProcessor::putFrames(unsigned int nbframes, int64_t ts) { + m_PeriodStat.mark(m_data_buffer->getBufferFill()); + + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "MotuTransmitStreamProcessor::putFrames(%d, %llu)\n", nbframes, ts); + + // transfer the data +#if 0 +debugOutput(DEBUG_LEVEL_VERBOSE, "1 - timestamp is %d\n", ts); +#endif + m_data_buffer->blockProcessWriteFrames(nbframes, ts); +#if 0 +debugOutput(DEBUG_LEVEL_VERBOSE, " done\n"); +#endif + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " New timestamp: %llu\n", ts); + + return true; +} + +/* + * write received events to the stream ringbuffers. + */ + +bool MotuTransmitStreamProcessor::processWriteBlock(char *data, + unsigned int nevents, unsigned int offset) { + bool no_problem=true; + unsigned int i; + + // FIXME: ensure the MIDI and control streams are all zeroed until + // such time as they are fully implemented. + for (i=0; iisDisabled()) {continue;}; + + //FIXME: make this into a static_cast when not DEBUG? + Port *port=dynamic_cast(*it); + + switch(port->getPortType()) { + + case Port::E_Audio: + if (encodePortToMotuEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { + debugWarning("Could not encode port %s to MBLA events",(*it)->getName().c_str()); + no_problem=false; + } + break; + // midi is a packet based port, don't process + // case MotuPortInfo::E_Midi: + // break; + + default: // ignore + break; + } + } + return no_problem; +} + +int MotuTransmitStreamProcessor::transmitSilenceBlock(char *data, + unsigned int nevents, unsigned int offset) { + // This is the same as the non-silence version, except that is + // doesn't read from the port buffers. + + int problem=0; + + for ( PortVectorIterator it = m_PeriodPorts.begin(); + it != m_PeriodPorts.end(); + ++it ) { + //FIXME: make this into a static_cast when not DEBUG? + Port *port=dynamic_cast(*it); + + switch(port->getPortType()) { + + case Port::E_Audio: + if (encodeSilencePortToMotuEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { + debugWarning("Could not encode port %s to MBLA events",(*it)->getName().c_str()); + problem=1; + } + break; + // midi is a packet based port, don't process + // case MotuPortInfo::E_Midi: + // break; + + default: // ignore + break; + } + } + return problem; +} + +/** + * @brief decode a packet for the packet-based ports + * + * @param data Packet data + * @param nevents number of events in data (including events of other ports & port types) + * @param dbc DataBlockCount value for this packet + * @return true if all successfull + */ +bool MotuTransmitStreamProcessor::encodePacketPorts(quadlet_t *data, unsigned int nevents, + unsigned int dbc) { + bool ok=true; + char byte; + + // Use char here since the target address won't necessarily be + // aligned; use of an unaligned quadlet_t may cause issues on + // certain architectures. Besides, the target for MIDI data going + // directly to the MOTU isn't structured in quadlets anyway; it is a + // sequence of 3 unaligned bytes. + unsigned char *target = NULL; + + for ( PortVectorIterator it = m_PacketPorts.begin(); + it != m_PacketPorts.end(); + ++it ) { + + Port *port=static_cast(*it); + assert(port); // this should not fail!! + + // Currently the only packet type of events for MOTU + // is MIDI in mbla. However in future control data + // might also be sent via "packet" events. + // assert(pinfo->getFormat()==MotuPortInfo::E_Midi); + + // FIXME: MIDI output is completely untested at present. + switch (port->getPortType()) { + case Port::E_Midi: { + MotuMidiPort *mp=static_cast(*it); + + // Send a byte if we can. MOTU MIDI data is + // sent using a 3-byte sequence starting at + // the port's position. For now we'll + // always send in the first event of a + // packet, but this might need refinement + // later. + if (mp->canRead()) { + mp->readEvent(&byte); + target = (unsigned char *)data + mp->getPosition(); + *(target++) = 0x01; + *(target++) = 0x00; + *(target++) = byte; + } + break; + } + default: + debugOutput(DEBUG_LEVEL_VERBOSE, "Unknown packet-type port type %d\n",port->getPortType()); + return ok; + } + } + + return ok; +} + +int MotuTransmitStreamProcessor::encodePortToMotuEvents(MotuAudioPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents) { +// Encodes nevents worth of data from the given port into the given buffer. The +// format of the buffer is precisely that which will be sent to the MOTU. +// The basic idea: +// iterate over the ports +// * get port buffer address +// * loop over events +// - pick right sample in event based upon PortInfo +// - convert sample from Port format (E_Int24, E_Float, ..) to MOTU +// native format +// +// We include the ability to start the transfer from the given offset within +// the port (expressed in frames) so the 'efficient' transfer method can be +// utilised. + + unsigned int j=0; + + // Use char here since the target address won't necessarily be + // aligned; use of an unaligned quadlet_t may cause issues on certain + // architectures. Besides, the target (data going directly to the MOTU) + // isn't structured in quadlets anyway; it mainly consists of packed + // 24-bit integers. + unsigned char *target; + target = (unsigned char *)data + p->getPosition(); + + switch(p->getDataType()) { + default: + case Port::E_Int24: + { + quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); + + assert(nevents + offset <= p->getBufferSize()); + + // Offset is in frames, but each port is only a single + // channel, so the number of frames is the same as the + // number of quadlets to offset (assuming the port buffer + // uses one quadlet per sample, which is the case currently). + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { // Decode nsamples + *target = (*buffer >> 16) & 0xff; + *(target+1) = (*buffer >> 8) & 0xff; + *(target+2) = (*buffer) & 0xff; + + buffer++; + target+=m_event_size; + } + } + break; + case Port::E_Float: + { + const float multiplier = (float)(0x7FFFFF); + float *buffer=(float *)(p->getBufferAddress()); + + assert(nevents + offset <= p->getBufferSize()); + + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { // decode max nsamples + unsigned int v = (int)(*buffer * multiplier); + *target = (v >> 16) & 0xff; + *(target+1) = (v >> 8) & 0xff; + *(target+2) = v & 0xff; + + buffer++; + target+=m_event_size; + } + } + break; + } + + return 0; +} + +int MotuTransmitStreamProcessor::encodeSilencePortToMotuEvents(MotuAudioPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents) { + unsigned int j=0; + unsigned char *target = (unsigned char *)data + p->getPosition(); + + switch (p->getDataType()) { + default: + case Port::E_Int24: + case Port::E_Float: + for (j = 0; j < nevents; j++) { + *target = *(target+1) = *(target+2) = 0; + target += m_event_size; + } + break; + } + + return 0; +} + +/* --------------------- RECEIVE ----------------------- */ + +MotuReceiveStreamProcessor::MotuReceiveStreamProcessor(int port, int framerate, + unsigned int event_size) + : ReceiveStreamProcessor(port, framerate), m_event_size(event_size), + m_closedown_active(0) { + +} + +MotuReceiveStreamProcessor::~MotuReceiveStreamProcessor() { + +} + +bool MotuReceiveStreamProcessor::init() { + + // call the parent init + // this has to be done before allocating the buffers, + // because this sets the buffersizes from the processormanager + if(!ReceiveStreamProcessor::init()) { + debugFatal("Could not do base class init (%d)\n",this); + return false; + } + + return true; +} + +enum raw1394_iso_disposition +MotuReceiveStreamProcessor::putPacket(unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, unsigned char sy, + unsigned int cycle, unsigned int dropped) { + + enum raw1394_iso_disposition retval=RAW1394_ISO_OK; + // this is needed for the base class getLastCycle() to work. + // this avoids a function call like StreamProcessor::updateLastCycle() + m_last_cycle=cycle; + + // check our enable status + if (!m_disabled && m_is_disabled) { + // this means that we are trying to enable + + // check if we are on or past the enable point + int cycles_past_enable=diffCycles(cycle, m_cycle_to_enable_at); + + if (cycles_past_enable >= 0) { + m_is_disabled=false; + debugOutput(DEBUG_LEVEL_VERBOSE,"Enabling Rx StreamProcessor %p at %d\n", + this, cycle); + + // the previous timestamp is the one we need to start with + // because we're going to update the buffer again this loop + // using writeframes + m_data_buffer->setBufferTailTimestamp(m_last_timestamp); + + } else { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, + "will enable StreamProcessor %p at %u, now is %d\n", + this, m_cycle_to_enable_at, cycle); + } + } else if (m_disabled && !m_is_disabled) { + // trying to disable + debugOutput(DEBUG_LEVEL_VERBOSE,"disabling StreamProcessor %p at %u\n", this, cycle); + m_is_disabled=true; + } + + // If the packet length is 8 bytes (ie: just a CIP-like header) + // there is no isodata. + if (length > 8) { + // The iso data blocks from the MOTUs comprise a CIP-like + // header followed by a number of events (8 for 1x rates, 16 + // for 2x rates, 32 for 4x rates). + quadlet_t *quadlet = (quadlet_t *)data; + unsigned int dbs = get_bits(ntohl(quadlet[0]), 23, 8); // Size of one event in terms of fdf_size + unsigned int fdf_size = get_bits(ntohl(quadlet[1]), 23, 8) == 0x22 ? 32:0; // Event unit size in bits + + // Don't even attempt to process a packet if it isn't what + // we expect from a MOTU. Yes, an FDF value of 32 bears + // little relationship to the actual data (24 bit integer) + // sent by the MOTU - it's one of those areas where MOTU + // have taken a curious detour around the standards. + if (tag!=1 || fdf_size!=32) { + return RAW1394_ISO_OK; + } + + // put this after the check because event_length can become 0 on invalid packets + unsigned int event_length = (fdf_size * dbs) / 8; // Event size in bytes + unsigned int n_events = (length-8) / event_length; + + //=> store the previous timestamp + m_last_timestamp2=m_last_timestamp; + + // Acquire the timestamp of the last frame in the packet just + // received. Since every frame from the MOTU has its own timestamp + // we can just pick it straight from the packet. + uint32_t last_sph = ntohl(*(quadlet_t *)(data+8+(n_events-1)*event_length)); + m_last_timestamp = sphRecvToFullTicks(last_sph, m_handler->getCycleTimer()); + + // Signal that we're running + if(!m_running && n_events && m_last_timestamp2 && m_last_timestamp) { + debugOutput(DEBUG_LEVEL_VERBOSE,"Receive StreamProcessor %p started running at %d\n", this, cycle); + m_running=true; + } + + //=> don't process the stream samples when it is not enabled. + if(m_is_disabled) { + + // we keep track of the timestamp here + // this makes sure that we will have a somewhat accurate + // estimate as to when a period might be ready. i.e. it will not + // be ready earlier than this timestamp + period time + + // Set the timestamp as if a sample was put into the buffer by + // this present packet. This means we use the timestamp + // corresponding to the last frame which would have been added + // to the buffer this cycle if we weren't disabled - that is, + // m_last_timestamp. + m_data_buffer->setBufferTailTimestamp(m_last_timestamp); + + return RAW1394_ISO_DEFER; + } + + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "put packet...\n"); + + //=> process the packet + // add the data payload to the ringbuffer + // Note: the last argument to writeFrames is the timestamp of the *last sample* being + // added. + if(m_data_buffer->writeFrames(n_events, (char *)(data+8), m_last_timestamp)) { + retval=RAW1394_ISO_OK; + int dbc = get_bits(ntohl(quadlet[0]), 8, 8); + + // process all ports that should be handled on a per-packet base + // this is MIDI for AMDTP (due to the need of DBC) + if (!decodePacketPorts((quadlet_t *)(data+8), n_events, dbc)) { + debugWarning("Problem decoding Packet Ports\n"); + retval=RAW1394_ISO_DEFER; + } + + } else { + + debugWarning("Receive buffer overrun (cycle %d, FC=%d, PC=%d)\n", + cycle, m_data_buffer->getFrameCounter(), m_handler->getPacketCount()); + + m_xruns++; + + // disable the processing, will be re-enabled when + // the xrun is handled + m_disabled=true; + m_is_disabled=true; + + retval=RAW1394_ISO_DEFER; + } + } + + return retval; +} + +// returns the delay between the actual (real) time of a timestamp as received, +// and the timestamp that is passed on for the same event. This is to cope with +// ISO buffering +int MotuReceiveStreamProcessor::getMinimalSyncDelay() { + unsigned int n_events = m_framerate<=48000?8:(m_framerate<=96000?16:32); + + return (int)(m_handler->getWakeupInterval() * n_events * m_ticks_per_frame); +} + +bool MotuReceiveStreamProcessor::reset() { + + debugOutput( DEBUG_LEVEL_VERBOSE, "Resetting...\n"); + + m_data_buffer->setTickOffset(0); + + // reset all non-device specific stuff + // i.e. the iso stream and the associated ports + if(!ReceiveStreamProcessor::reset()) { + debugFatal("Could not do base class reset\n"); + return false; + } + + return true; +} + +bool MotuReceiveStreamProcessor::prepare() { + + // prepare all non-device specific stuff + // i.e. the iso stream and the associated ports + if(!ReceiveStreamProcessor::prepare()) { + debugFatal("Could not prepare base class\n"); + return false; + } + + debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing...\n"); + + m_PeriodStat.setName("RCV PERIOD"); + m_PacketStat.setName("RCV PACKET"); + m_WakeupStat.setName("RCV WAKEUP"); + + // setup any specific stuff here + // FIXME: m_frame_size would be a better name + debugOutput( DEBUG_LEVEL_VERBOSE, "Event size: %d\n", m_event_size); + + // prepare the framerate estimate + m_ticks_per_frame = (TICKS_PER_SECOND*1.0) / ((float)m_framerate); + + // initialize internal buffer + unsigned int ringbuffer_size_frames=m_nb_buffers * m_period; + + unsigned int events_per_frame = m_framerate<=48000?8:(m_framerate<=96000?16:32); + + assert(m_data_buffer); + m_data_buffer->setBufferSize(ringbuffer_size_frames); + m_data_buffer->setEventSize(m_event_size); + m_data_buffer->setEventsPerFrame(1); + +// JMW: The rx buffer receives a new timestamp once per received frame so I think the +// buffer update period is events_per_frame, not events per period. +// m_data_buffer->setUpdatePeriod(m_period); + m_data_buffer->setUpdatePeriod(events_per_frame); + m_data_buffer->setNominalRate(m_ticks_per_frame); + + m_data_buffer->setWrapValue(128L*TICKS_PER_SECOND); + + m_data_buffer->prepare(); + + // set the parameters of ports we can: + // we want the audio ports to be period buffered, + // and the midi ports to be packet buffered + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + debugOutput(DEBUG_LEVEL_VERBOSE, "Setting up port %s\n",(*it)->getName().c_str()); + + if(!(*it)->setBufferSize(m_period)) { + debugFatal("Could not set buffer size to %d\n",m_period); + return false; + } + + switch ((*it)->getPortType()) { + case Port::E_Audio: + if(!(*it)->setSignalType(Port::E_PeriodSignalled)) { + debugFatal("Could not set signal type to PeriodSignalling"); + return false; + } + break; + case Port::E_Midi: + if(!(*it)->setSignalType(Port::E_PacketSignalled)) { + debugFatal("Could not set signal type to PacketSignalling"); + return false; + } + if (!(*it)->setBufferType(Port::E_RingBuffer)) { + debugFatal("Could not set buffer type"); + return false; + } + if (!(*it)->setDataType(Port::E_MidiEvent)) { + debugFatal("Could not set data type"); + return false; + } + // FIXME: probably need rate control too. See + // Port::useRateControl() and AmdtpStreamProcessor. + break; + case Port::E_Control: + if(!(*it)->setSignalType(Port::E_PeriodSignalled)) { + debugFatal("Could not set signal type to PeriodSignalling"); + return false; + } + break; + default: + debugWarning("Unsupported port type specified\n"); + break; + } + + } + + // The API specific settings of the ports are already set before + // this routine is called, therefore we can init&prepare the ports + if(!initPorts()) { + debugFatal("Could not initialize ports!\n"); + return false; + } + + if(!preparePorts()) { + debugFatal("Could not initialize ports!\n"); + return false; + } + + return true; + +} + + +bool MotuReceiveStreamProcessor::prepareForStop() { + + // A MOTU receive stream can stop at any time. However, signify + // that stopping is in progress because other streams (notably the + // transmit stream) may keep going for some time and cause an + // overflow in the receive buffers. If a closedown is in progress + // the receive handler simply throws all incoming data away so + // no buffer overflow can occur. + m_closedown_active = 1; + return true; +} + +bool MotuReceiveStreamProcessor::prepareForStart() { +// Reset some critical variables required so the stream starts cleanly. This +// method is called once on every stream restart, including those during +// xrun recovery. Initialisations which should be done once should be +// placed in the init() method instead. + m_running = 0; + m_closedown_active = 0; + + // At this point we'll also disable the stream processor here. + // At this stage stream processors are always explicitly re-enabled + // after being started, so by starting in the disabled state we + // ensure that every start will be exactly the same. + disable(); + + return true; +} + +bool MotuReceiveStreamProcessor::getFrames(unsigned int nbframes, int64_t ts) { + + m_PeriodStat.mark(m_data_buffer->getBufferFill()); + + // ask the buffer to process nbframes of frames + // using it's registered client's processReadBlock(), + // which should be ours + m_data_buffer->blockProcessReadFrames(nbframes); + + return true; +} + +/** + * \brief write received events to the port ringbuffers. + */ +bool MotuReceiveStreamProcessor::processReadBlock(char *data, + unsigned int nevents, unsigned int offset) +{ + bool no_problem=true; + for ( PortVectorIterator it = m_PeriodPorts.begin(); + it != m_PeriodPorts.end(); + ++it ) { + if((*it)->isDisabled()) {continue;}; + + //FIXME: make this into a static_cast when not DEBUG? + Port *port=dynamic_cast(*it); + + switch(port->getPortType()) { + + case Port::E_Audio: + if(decodeMotuEventsToPort(static_cast(*it), (quadlet_t *)data, offset, nevents)) { + debugWarning("Could not decode packet MBLA to port %s",(*it)->getName().c_str()); + no_problem=false; + } + break; + // midi is a packet based port, don't process + // case MotuPortInfo::E_Midi: + // break; + + default: // ignore + break; + } + } + return no_problem; +} + +/** + * @brief decode a packet for the packet-based ports + * + * @param data Packet data + * @param nevents number of events in data (including events of other ports & port types) + * @param dbc DataBlockCount value for this packet + * @return true if all successfull + */ +bool MotuReceiveStreamProcessor::decodePacketPorts(quadlet_t *data, unsigned int nevents, + unsigned int dbc) { + bool ok=true; + + // Use char here since the source address won't necessarily be + // aligned; use of an unaligned quadlet_t may cause issues on + // certain architectures. Besides, the source for MIDI data going + // directly to the MOTU isn't structured in quadlets anyway; it is a + // sequence of 3 unaligned bytes. + unsigned char *src = NULL; + + for ( PortVectorIterator it = m_PacketPorts.begin(); + it != m_PacketPorts.end(); + ++it ) { + + Port *port=dynamic_cast(*it); + assert(port); // this should not fail!! + + // Currently the only packet type of events for MOTU + // is MIDI in mbla. However in future control data + // might also be sent via "packet" events, so allow + // for this possible expansion. + + // FIXME: MIDI input is completely untested at present. + switch (port->getPortType()) { + case Port::E_Midi: { + MotuMidiPort *mp=static_cast(*it); + signed int sample; + unsigned int j = 0; + // Get MIDI bytes if present anywhere in the + // packet. MOTU MIDI data is sent using a + // 3-byte sequence starting at the port's + // position. It's thought that there can never + // be more than one MIDI byte per packet, but + // for completeness we'll check the entire packet + // anyway. + src = (unsigned char *)data + mp->getPosition(); + while (j < nevents) { + if (*src==0x01 && *(src+1)==0x00) { + sample = *(src+2); + if (!mp->writeEvent(&sample)) { + debugWarning("MIDI packet port events lost\n"); + ok = false; + } + } + j++; + src += m_event_size; + } + break; + } + default: + debugOutput(DEBUG_LEVEL_VERBOSE, "Unknown packet-type port format %d\n",port->getPortType()); + return ok; + } + } + + return ok; +} + +signed int MotuReceiveStreamProcessor::decodeMotuEventsToPort(MotuAudioPort *p, + quadlet_t *data, unsigned int offset, unsigned int nevents) +{ + unsigned int j=0; + + // Use char here since a port's source address won't necessarily be + // aligned; use of an unaligned quadlet_t may cause issues on + // certain architectures. Besides, the source (data coming directly + // from the MOTU) isn't structured in quadlets anyway; it mainly + // consists of packed 24-bit integers. + + unsigned char *src_data; + src_data = (unsigned char *)data + p->getPosition(); + + switch(p->getDataType()) { + default: + case Port::E_Int24: + { + quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); + + assert(nevents + offset <= p->getBufferSize()); + + // Offset is in frames, but each port is only a single + // channel, so the number of frames is the same as the + // number of quadlets to offset (assuming the port buffer + // uses one quadlet per sample, which is the case currently). + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { // Decode nsamples + *buffer = (*src_data<<16)+(*(src_data+1)<<8)+*(src_data+2); + // Sign-extend highest bit of 24-bit int. + // FIXME: this isn't strictly needed since E_Int24 is a 24-bit, + // but doing so shouldn't break anything and makes the data + // easier to deal with during debugging. + if (*src_data & 0x80) + *buffer |= 0xff000000; + + buffer++; + src_data+=m_event_size; + } + } + break; + case Port::E_Float: + { + const float multiplier = 1.0f / (float)(0x7FFFFF); + float *buffer=(float *)(p->getBufferAddress()); + + assert(nevents + offset <= p->getBufferSize()); + + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { // decode max nsamples + + unsigned int v = (*src_data<<16)+(*(src_data+1)<<8)+*(src_data+2); + + // sign-extend highest bit of 24-bit int + int tmp = (int)(v << 8) / 256; + + *buffer = tmp * multiplier; + + buffer++; + src_data+=m_event_size; + } + } + break; + } + + return 0; +} + +signed int MotuReceiveStreamProcessor::setEventSize(unsigned int size) { + m_event_size = size; + return 0; +} + +unsigned int MotuReceiveStreamProcessor::getEventSize(void) { +// +// Return the size of a single event sent by the MOTU as part of an iso +// data packet in bytes. +// + return m_event_size; +} + +void MotuReceiveStreamProcessor::setVerboseLevel(int l) { + setDebugLevel(l); + ReceiveStreamProcessor::setVerboseLevel(l); +} + +} // end of namespace Streaming Index: /branches/ppalmers-streaming/src/libstreaming/motu/MotuPortInfo.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/motu/MotuPortInfo.cpp (revision 703) +++ /branches/ppalmers-streaming/src/libstreaming/motu/MotuPortInfo.cpp (revision 703) @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 by Pieter Palmers + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation; + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "MotuPortInfo.h" +#include + +namespace Streaming { + + +} // end of namespace Streaming Index: /branches/ppalmers-streaming/src/libstreaming/motu/MotuStreamProcessor.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/motu/MotuStreamProcessor.h (revision 703) +++ /branches/ppalmers-streaming/src/libstreaming/motu/MotuStreamProcessor.h (revision 703) @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 by Pieter Palmers + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation; + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef __FFADO_MOTUSTREAMPROCESSOR__ +#define __FFADO_MOTUSTREAMPROCESSOR__ +#include + +#include "../debugmodule/debugmodule.h" +#include "StreamProcessor.h" + +#include "../libutil/DelayLockedLoop.h" + +namespace Streaming { + +class MotuAudioPort; + +/** + * This class implements the outgoing stream processing for + * motu devices + */ +class MotuTransmitStreamProcessor + : public TransmitStreamProcessor +{ +public: + + MotuTransmitStreamProcessor(int port, int framerate, + unsigned int event_size); + + virtual ~MotuTransmitStreamProcessor(); + + enum raw1394_iso_disposition + getPacket(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_length); + + bool init(); + bool reset(); + bool prepare(); + + bool prepareForStop(); + bool prepareForStart(); + + bool prepareForEnable(uint64_t time_to_enable_at); + + bool putFrames(unsigned int nbframes, int64_t ts); ///< transfer the buffer contents from the client + + // These two are important to calculate the optimal ISO DMA buffers + // size. An estimate will do. + unsigned int getPacketsPerPeriod() {return (m_period*8000) / m_framerate;}; + unsigned int getMaxPacketSize() {return m_framerate<=48000?616:(m_framerate<=96000?1032:1160);}; + + int getMinimalSyncDelay(); + + void setVerboseLevel(int l); + +protected: + /* + * An iso packet mostly consists of multiple events. m_event_size + * is the size of a single 'event' in bytes. + */ + unsigned int m_event_size; + + // Keep track of transmission data block count + unsigned int m_tx_dbc; + + // Used to zero output data during startup while sync is established + signed int m_startup_count; + + // Used to keep track of the close-down zeroing of output data + signed int m_closedown_count; + signed int m_streaming_active; + + bool prefill(); + + bool transferSilence(unsigned int size); + + bool processWriteBlock(char *data, unsigned int nevents, unsigned int offset); + + bool encodePacketPorts(quadlet_t *data, unsigned int nevents, unsigned int dbc); + + int transmitSilenceBlock(char *data, unsigned int nevents, + unsigned int offset); + + int encodePortToMotuEvents(MotuAudioPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents); + int encodeSilencePortToMotuEvents(MotuAudioPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents); + + DECLARE_DEBUG_MODULE; + +}; + +/** + * This class implements the incoming stream processing for + * motu devices + */ +class MotuReceiveStreamProcessor + : public ReceiveStreamProcessor +{ + +public: + + MotuReceiveStreamProcessor(int port, int framerate, unsigned int event_size); + virtual ~MotuReceiveStreamProcessor(); + + enum raw1394_iso_disposition putPacket(unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, unsigned char sy, + unsigned int cycle, unsigned int dropped); + + bool getFrames(unsigned int nbframes, int64_t ts); ///< transfer the buffer contents to the client + + bool init(); + bool reset(); + bool prepare(); + + // these two are important to calculate the optimal + // ISO DMA buffers size + // an estimate will do + unsigned int getPacketsPerPeriod() {return (m_period*8000) / m_framerate;}; + unsigned int getMaxPacketSize() {return m_framerate<=48000?616:(m_framerate<=96000?1032:1160);}; + + int getMinimalSyncDelay(); + + virtual void setVerboseLevel(int l); + + signed int setEventSize(unsigned int size); + unsigned int getEventSize(void); + + virtual bool prepareForStop(); + virtual bool prepareForStart(); + +protected: + + bool processReadBlock(char *data, unsigned int nevents, unsigned int offset); + + bool decodePacketPorts(quadlet_t *data, unsigned int nevents, unsigned int dbc); + signed int decodeMotuEventsToPort(MotuAudioPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents); + + /* + * An iso packet mostly consists of multiple events. m_event_size + * is the size of a single 'event' in bytes. + */ + unsigned int m_event_size; + + // Signifies a closedown is in progress, in which case incoming data + // is junked. + signed int m_closedown_active; + + uint64_t m_last_timestamp; /// last timestamp (in ticks) + uint64_t m_last_timestamp2; /// last timestamp (in ticks) + uint64_t m_last_timestamp_at_period_ticks; + + DECLARE_DEBUG_MODULE; + +}; + +} // end of namespace Streaming + +#endif /* __FFADO_MOTUSTREAMPROCESSOR__ */ + + Index: /branches/ppalmers-streaming/src/libstreaming/motu/MotuPort.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/motu/MotuPort.cpp (revision 703) +++ /branches/ppalmers-streaming/src/libstreaming/motu/MotuPort.cpp (revision 703) @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 by Pieter Palmers + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation; + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "MotuPort.h" +#include + +namespace Streaming { + +} // end of namespace Streaming Index: /branches/ppalmers-streaming/src/libstreaming/motu/MotuPortInfo.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/motu/MotuPortInfo.h (revision 703) +++ /branches/ppalmers-streaming/src/libstreaming/motu/MotuPortInfo.h (revision 703) @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 by Pieter Palmers + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation; + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef __FFADO_MOTUPORTINFO__ +#define __FFADO_MOTUPORTINFO__ + +#include "../debugmodule/debugmodule.h" +#include + +namespace Streaming { +/*! +\brief Class containing the stream information for a Motu channel + + Contains the information that enables the decoding routine to find + this port's data in the ISO events + +*/ +class MotuPortInfo { + +public: + /** + * Sometimes a channel can have multiple formats, depending on the + * device configuration (e.g. an SPDIF port could be plain audio in 24bit integer + * or AC3 passthrough in IEC compliant frames.) + * + * This kind of enum allows to discriminate these formats when decoding + * If all channels always have the same format, you won't be needing this + */ +// enum E_Formats { +// E_MBLA, // Multibit linear audio +// E_Midi, // MIDI +// }; + + /** + * Initialize Motu portinfo + * should not be called directly, is inherited by motu ports + * + * the position parameter is an example + * the name parameter is mandatory + * + * @param position Start position of port's data in iso event + * @param format Format of data in iso event + * @param size Size in bits of port's data in iso event + * @return + */ + MotuPortInfo( int position, int size) + : m_position(position), m_size(size) + {}; + virtual ~MotuPortInfo() {}; + + + int getPosition() {return m_position;}; + int getSize() {return m_size;}; + +protected: + + int m_position; + int m_size; + +}; + +} // end of namespace Streaming + +#endif /* __FFADO_MOTUPORTINFO__ */ Index: /branches/ppalmers-streaming/src/libstreaming/cycletimer.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/cycletimer.h (revision 498) +++ /branches/ppalmers-streaming/src/libstreaming/cycletimer.h (revision 703) @@ -66,4 +66,6 @@ ) #define CYCLE_TIMER_WRAP_TICKS(x) ((x % TICKS_PER_SECOND)) + +#define INVALID_TIMESTAMP_TICKS 0xFFFFFFFFFFFFFFFFULL DECLARE_GLOBAL_DEBUG_MODULE; @@ -247,6 +249,6 @@ uint64_t timestamp; - debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"SYT=%08X CY=%04X CTR=%08X\n", - syt_timestamp,rcv_cycle,ctr_now); + debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"SYT=%04llX CY=%u CTR=%08llX\n", + syt_timestamp, rcv_cycle, ctr_now); // reconstruct the full cycle @@ -309,5 +311,5 @@ if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) { debugWarning("back-converted timestamp not equal to SYT\n"); - debugWarning("TS=%011llu TSC=%08X SYT=%04X\n", + debugWarning("TS=%011llu TSC=%08lX SYT=%04X\n", timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp); } @@ -333,5 +335,5 @@ uint64_t timestamp; - debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"SYT=%08X CY=%04X CTR=%08X\n", + debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"SYT=%08llX CY=%04X CTR=%08llX\n", syt_timestamp,xmt_cycle,ctr_now); @@ -395,5 +397,5 @@ if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) { debugWarning("back-converted timestamp not equal to SYT\n"); - debugWarning("TS=%011llu TSC=%08X SYT=%04X\n", + debugWarning("TS=%011llu TSC=%08lX SYT=%04X\n", timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp); } Index: /branches/ppalmers-streaming/src/libstreaming/AmdtpStreamProcessor.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/AmdtpStreamProcessor.cpp (revision 554) +++ /branches/ppalmers-streaming/src/libstreaming/AmdtpStreamProcessor.cpp (revision 703) @@ -38,6 +38,6 @@ namespace Streaming { -IMPL_DEBUG_MODULE( AmdtpTransmitStreamProcessor, AmdtpTransmitStreamProcessor, DEBUG_LEVEL_NORMAL ); -IMPL_DEBUG_MODULE( AmdtpReceiveStreamProcessor, AmdtpReceiveStreamProcessor, DEBUG_LEVEL_NORMAL ); +IMPL_DEBUG_MODULE( AmdtpTransmitStreamProcessor, AmdtpTransmitStreamProcessor, DEBUG_LEVEL_VERBOSE ); +IMPL_DEBUG_MODULE( AmdtpReceiveStreamProcessor, AmdtpReceiveStreamProcessor, DEBUG_LEVEL_VERBOSE ); @@ -84,6 +84,6 @@ if (cycle<0) { - debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Xmit handler for cycle %d, (running=%d, enabled=%d,%d)\n", - cycle, m_running, m_disabled, m_is_disabled); + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE,"Xmit handler for cycle %d, (running=%d)\n", + cycle, m_running); *tag = 0; @@ -94,6 +94,6 @@ } - debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"Xmit handler for cycle %d, (running=%d, enabled=%d,%d)\n", - cycle, m_running, m_disabled, m_is_disabled); + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE,"Xmit handler for cycle %d, (running=%d)\n", + cycle, m_running); m_last_cycle=cycle; @@ -153,62 +153,23 @@ uint64_t ts_head; signed int fc; - if (!m_disabled && m_is_disabled) { // this means that we are trying to enable - // check if we are on or past the enable point - int cycles_past_enable=diffCycles(cycle, m_cycle_to_enable_at); - - if (cycles_past_enable >= 0) { - m_is_disabled=false; - - debugOutput(DEBUG_LEVEL_VERBOSE,"Enabling StreamProcessor %p at %u\n", this, cycle); - - // initialize the buffer head & tail - ffado_timestamp_t ts_head_tmp; - m_SyncSource->m_data_buffer->getBufferHeadTimestamp(&ts_head_tmp, &fc); // thread safe - ts_head=(uint64_t)ts_head_tmp; - - // the number of cycles the sync source lags (> 0) - // or leads (< 0) - int sync_lag_cycles=diffCycles(cycle, m_SyncSource->getLastCycle()); - - // account for the cycle lag between sync SP and this SP - // the last update of the sync source's timestamps was sync_lag_cycles - // cycles before the cycle we are calculating the timestamp for. - // if we were to use one-frame buffers, you would expect the - // frame that is sent on cycle CT to have a timestamp T1. - // ts_head however is for cycle CT-sync_lag_cycles, and lies - // therefore sync_lag_cycles * TICKS_PER_CYCLE earlier than - // T1. - ts_head = addTicks(ts_head, (sync_lag_cycles) * TICKS_PER_CYCLE); - - ts_head = substractTicks(ts_head, TICKS_PER_CYCLE); - - // account for the number of cycles we are too late to enable - ts_head = addTicks(ts_head, cycles_past_enable * TICKS_PER_CYCLE); - - // account for one extra packet of frames - ts_head = substractTicks(ts_head, - (uint32_t)((float)m_syt_interval * m_SyncSource->m_data_buffer->getRate())); - - m_data_buffer->setBufferHeadTimestamp(ts_head); - - #ifdef DEBUG - if ((unsigned int)m_data_buffer->getFrameCounter() != m_data_buffer->getBufferSize()) { - debugWarning("m_data_buffer->getFrameCounter() != m_data_buffer->getBufferSize()\n"); - } - #endif - debugOutput(DEBUG_LEVEL_VERBOSE,"XMIT TS SET: TS=%10llu, LAG=%03d, FC=%4d\n", - ts_head, sync_lag_cycles, m_data_buffer->getFrameCounter()); - } else { - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, - "will enable StreamProcessor %p at %u, now is %d\n", - this, m_cycle_to_enable_at, cycle); - } - } else if (m_disabled && !m_is_disabled) { - // trying to disable - debugOutput(DEBUG_LEVEL_VERBOSE,"disabling StreamProcessor %p at %u\n", - this, cycle); - m_is_disabled=true; - } - + uint64_t presentation_time; + unsigned int presentation_cycle; + int cycles_until_presentation; + + const int min_cycles_beforehand = 2; // FIXME: should become a define + const int max_cycles_beforehand = 15; // FIXME: should become a define + + if( !m_running || !m_data_buffer->isEnabled() ) { + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, + "Not running (%d) or buffer not enabled (enabled=%d)\n", + m_running, m_data_buffer->isEnabled()); + + // not running or not enabled + goto send_empty_packet; + } + +try_block_of_frames: + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "Try for cycle %d\n", cycle); + // check whether the packet buffer has packets for us to send. // the base timestamp is the one of the next sample in the buffer ffado_timestamp_t ts_head_tmp; @@ -216,108 +177,117 @@ ts_head=(uint64_t)ts_head_tmp; - // we send a packet some cycles in advance, to avoid the - // following situation: - // suppose we are only a few ticks away from - // the moment to send this packet. therefore we decide - // not to send the packet, but send it in the next cycle. - // This means that the next time point will be 3072 ticks - // later, making that the timestamp will be expired when the - // packet is sent, unless TRANSFER_DELAY > 3072. - // this means that we need at least one cycle of extra buffering. - uint32_t ticks_to_advance = TICKS_PER_CYCLE * TRANSMIT_ADVANCE_CYCLES; - - // if cycle lies cycle_diff cycles in the future, we should - // queue this packet cycle_diff * TICKS_PER_CYCLE earlier than - // we would if it were to be sent immediately. - ticks_to_advance += cycle_diff * TICKS_PER_CYCLE; - - // determine the 'now' time in ticks - uint32_t cycle_timer=CYCLE_TIMER_TO_TICKS(ctr); - - cycle_timer = addTicks(cycle_timer, ticks_to_advance); - - // time until the packet is to be sent (if > 0: send packet) - int32_t until_next=diffTicks(ts_head, cycle_timer); - - // if until_next < 0 we should send a filled packet - // otherwise we should send a NO-DATA packet - if((until_next<0) && (m_running)) { - // add the transmit transfer delay to construct the playout time (=SYT timestamp) - uint32_t ts_packet=addTicks(ts_head, TRANSMIT_TRANSFER_DELAY); - - // if we are disabled, send a silent packet - // and advance the buffer head timestamp - if(m_is_disabled) { - -// transmitSilenceBlock((char *)(data+8), m_syt_interval, 0); -// m_dbc += fillDataPacketHeader(packet, length, ts_packet); -// -// debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "XMIT SYNC: CY=%04u TSH=%011llu TSP=%011lu\n", -// cycle, ts_head, ts_packet); -// -// // update the base timestamp -// uint32_t ts_step=(uint32_t)((float)(m_syt_interval) -// *m_SyncSource->m_data_buffer->getRate()); -// -// // the next buffer head timestamp -// ts_head=addTicks(ts_head,ts_step); -// m_data_buffer->setBufferHeadTimestamp(ts_head); -// - // no-data - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "XMIT SYNC: CY=%04u NONE\n", cycle); - m_dbc += fillNoDataPacketHeader(packet, length); - // defer to make sure we get to be enabled asap - return RAW1394_ISO_DEFER; - - } else { // enabled & packet due, read from the buffer - if (m_data_buffer->readFrames(m_syt_interval, (char *)(data + 8))) { - m_dbc += fillDataPacketHeader(packet, length, ts_packet); - - // process all ports that should be handled on a per-packet base - // this is MIDI for AMDTP (due to the need of DBC) - if (!encodePacketPorts((quadlet_t *)(data+8), m_syt_interval, packet->dbc)) { - debugWarning("Problem encoding Packet Ports\n"); - } - - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "XMIT DATA: CY=%04u TSH=%011llu TSP=%011lu\n", - cycle, ts_head, ts_packet); - - return RAW1394_ISO_OK; - - } else if (now_cycles determine wether this is a problem, since we might still + // have some time to send it + // 2) there are enough packets + // => determine whether we have to send them in this packet + if (fc < (signed int)m_syt_interval) { + // not enough frames in the buffer, + debugOutput(DEBUG_LEVEL_VERBOSE, + "Insufficient frames: CY=%04u, PTC=%04u, CUP=%04d\n", + cycle, presentation_cycle, cycles_until_presentation); + // we can still postpone the queueing of the packets + // if we are far enough ahead of the presentation time + if( cycles_until_presentation <= min_cycles_beforehand ) { + // we have an invalid timestamp or we are too late + // meaning that we in some sort of xrun state + // signal xrun situation ??HERE?? + m_xruns++; + } else { + // there is still time left to send the packet + } + // in any case we send an empty packet on this cycle + goto send_empty_packet; // UGLY but effective + } else { + // there are enough frames, so check the time they are intended for + // all frames have a certain 'time window' in which they can be sent + // this corresponds to the range of the timestamp mechanism: + // we can send a packet 15 cycles in advance of the 'presentation time' + // in theory we can send the packet up till one cycle before the presentation time, + // however this is not very smart. + + // There are 3 options: + // 1) the frame block is too early + // => send an empty packet + // 2) the frame block is within the window + // => send it + // 3) the frame block is too late + // => discard (and raise xrun?) + // get next block of frames and repeat + + if (cycles_until_presentation <= min_cycles_beforehand) { + // we are too late + debugOutput(DEBUG_LEVEL_VERBOSE, + "Too late: CY=%04u, PTC=%04u, CUP=%04d, TSP=%011llu (%04u)\n", + cycle, + presentation_cycle, cycles_until_presentation, + presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time)); + // remove the samples + m_data_buffer->dropFrames(m_syt_interval); + + // signal some xrun situation ??HERE?? + m_xruns++; + + // try a new block of frames + goto try_block_of_frames; // UGLY but effective + } else if (cycles_until_presentation >= max_cycles_beforehand) { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, + "Too early: CY=%04u, PTC=%04u, CUP=%04d, TSP=%011llu (%04u)\n", + cycle, + presentation_cycle, cycles_until_presentation, + presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time)); + // we are too early, send only an empty packet + goto send_empty_packet; // UGLY but effective + } else { + // send the packet + goto send_packet; // UGLY but effective + } + } + + debugError("Should never reach this code!\n"); return RAW1394_ISO_ERROR; - + +send_empty_packet: + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "XMIT NONE: CY=%04u, TSP=%011llu (%04u)\n", + cycle, + presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time)); + + m_dbc += fillNoDataPacketHeader(packet, length); + return RAW1394_ISO_DEFER; + +send_packet: + if (m_data_buffer->readFrames(m_syt_interval, (char *)(data + 8))) { + m_dbc += fillDataPacketHeader(packet, length, presentation_time); + + // process all ports that should be handled on a per-packet base + // this is MIDI for AMDTP (due to the need of DBC) + if (!encodePacketPorts((quadlet_t *)(data+8), m_syt_interval, packet->dbc)) { + debugWarning("Problem encoding Packet Ports\n"); + } + + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "XMIT DATA: CY=%04u, TSH=%011llu (%04u), TSP=%011llu (%04u)\n", + cycle, + ts_head, (unsigned int)TICKS_TO_CYCLES(ts_head), + presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time)); + + return RAW1394_ISO_OK; + } + // else: + debugError("This is impossible, since we checked the buffer size before!\n"); + return RAW1394_ISO_ERROR; } @@ -384,9 +354,5 @@ m_WakeupStat.reset(); - // we have to make sure that the buffer HEAD timestamp - // lies in the future for every possible buffer fill case. - int offset=(int)(m_ringbuffer_size_frames*getTicksPerFrame()); - - m_data_buffer->setTickOffset(offset); + m_data_buffer->setTickOffset(0); // reset all non-device specific stuff @@ -589,49 +555,8 @@ bool AmdtpTransmitStreamProcessor::prepareForStop() { - disable(); return true; } bool AmdtpTransmitStreamProcessor::prepareForEnable(uint64_t time_to_enable_at) { - - debugOutput(DEBUG_LEVEL_VERBOSE,"Preparing to enable...\n"); - - // for the transmit SP, we have to initialize the - // buffer timestamp to something sane, because this timestamp - // is used when it is SyncSource - - // the time we initialize to will determine the time at which - // the first sample in the buffer will be sent, so we should - // make it at least 'time_to_enable_at' - - uint64_t now=m_handler->getCycleTimer(); - unsigned int now_secs=CYCLE_TIMER_GET_SECS(now); - - // check if a wraparound on the secs will happen between - // now and the time we start - int until_enable=(int)time_to_enable_at - (int)CYCLE_TIMER_GET_CYCLES(now); - - if(until_enable>4000) { - // wraparound on CYCLE_TIMER_GET_CYCLES(now) - // this means that we are late starting up, - // and that the start lies in the previous second - if (now_secs==0) now_secs=127; - else now_secs--; - } else if (until_enable<-4000) { - // wraparound on time_to_enable_at - // this means that we are early and that the start - // point lies in the next second - now_secs++; - if (now_secs>=128) now_secs=0; - } - - uint64_t ts_head= now_secs*TICKS_PER_SECOND; - ts_head+=time_to_enable_at*TICKS_PER_CYCLE; - - // we also add the nb of cycles we transmit in advance - ts_head=addTicks(ts_head, TRANSMIT_ADVANCE_CYCLES*TICKS_PER_CYCLE); - - m_data_buffer->setBufferTailTimestamp(ts_head); - if (!StreamProcessor::prepareForEnable(time_to_enable_at)) { @@ -666,13 +591,33 @@ m_PeriodStat.mark(m_data_buffer->getBufferFill()); - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "AmdtpTransmitStreamProcessor::putFrames(%d, %llu)\n", nbframes, ts); + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "AmdtpTransmitStreamProcessor::putFrames(%d, %llu)\n", nbframes, ts); // transfer the data m_data_buffer->blockProcessWriteFrames(nbframes, ts); - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " New timestamp: %llu\n", ts); - - return true; -} + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, " New timestamp: %llu\n", ts); + + return true; // FIXME: what about failure? +} + +bool AmdtpTransmitStreamProcessor::putFramesDry(unsigned int nbframes, int64_t ts) { + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "AmdtpTransmitStreamProcessor::putFramesDry(%d, %llu)\n", nbframes, ts); + + bool retval; + char dummybuffer[sizeof(quadlet_t)*nbframes*m_dimension]; + + transmitSilenceBlock(dummybuffer, nbframes, 0); + // add the silence data to the ringbuffer + if(m_data_buffer->writeFrames(nbframes, dummybuffer, ts)) { + retval=true; + } else { + debugWarning("Could not write to event buffer\n"); + retval=false; + } + + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, " New timestamp: %llu\n", ts); + return retval; +} + /* * write received events to the stream ringbuffers. @@ -800,7 +745,7 @@ IEC61883_AM824_LABEL_MIDI_1X)); - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "MIDI port %s, pos=%d, loc=%d, dbc=%d, nevents=%d, dim=%d\n", + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "MIDI port %s, pos=%d, loc=%d, dbc=%d, nevents=%d, dim=%d\n", mp->getName().c_str(), mp->getPosition(), mp->getLocation(), dbc, nevents, m_dimension); - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "base=%p, target=%p, value=%08X\n", + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "base=%p, target=%p, value=%08X\n", data, target_event, tmpval); @@ -937,8 +882,8 @@ } - debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"ch%2u: CY=%4u, SYT=%08X (%4ucy + %04uticks) (running=%d, disabled=%d,%d)\n", + debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"ch%2u: CY=%4u, SYT=%08X (%4ucy + %04uticks) (running=%d)\n", channel, cycle,ntohs(packet->syt), CYCLE_TIMER_GET_CYCLES(ntohs(packet->syt)), CYCLE_TIMER_GET_OFFSET(ntohs(packet->syt)), - m_running,m_disabled,m_is_disabled); + m_running); debugOutput(DEBUG_LEVEL_VERY_VERBOSE, @@ -952,27 +897,4 @@ #endif - if (!m_disabled && m_is_disabled) { // this means that we are trying to enable - // check if we are on or past the enable point - int cycles_past_enable=diffCycles(cycle, m_cycle_to_enable_at); - - if (cycles_past_enable >= 0) { - m_is_disabled=false; - debugOutput(DEBUG_LEVEL_VERBOSE,"Enabling StreamProcessor %p at %d (SYT=%04X)\n", - this, cycle, ntohs(packet->syt)); - // the previous timestamp is the one we need to start with - // because we're going to update the buffer again this loop - // using writeframes - m_data_buffer->setBufferTailTimestamp(m_last_timestamp); - - } else { - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, - "will enable StreamProcessor %p at %u, now is %d\n", - this, m_cycle_to_enable_at, cycle); - } - } else if (m_disabled && !m_is_disabled) { - // trying to disable - debugOutput(DEBUG_LEVEL_VERBOSE,"disabling StreamProcessor %p at %u\n", this, cycle); - m_is_disabled=true; - } // check if this is a valid packet @@ -1011,24 +933,4 @@ } - //=> don't process the stream samples when it is not enabled. - if(m_is_disabled) { - - // we keep track of the timestamp here - // this makes sure that we will have a somewhat accurate - // estimate as to when a period might be ready. i.e. it will not - // be ready earlier than this timestamp + period time - - // the next (possible) sample is not this one, but lies - // SYT_INTERVAL * rate later - uint64_t ts=addTicks(m_last_timestamp, - (uint64_t)((float)m_syt_interval * getTicksPerFrame())); - - // set the timestamp as if there will be a sample put into - // the buffer by the next packet. - m_data_buffer->setBufferTailTimestamp(ts); - - return RAW1394_ISO_DEFER; - } - #ifdef DEBUG_OFF if((cycle % 1000) == 0) { @@ -1065,13 +967,8 @@ } else { - debugWarning("Receive buffer overrun (cycle %d, FC=%d, PC=%d)\n", - cycle, m_data_buffer->getFrameCounter(), m_handler->getPacketCount()); +// debugWarning("Receive buffer overrun (cycle %d, FC=%d, PC=%d)\n", +// cycle, m_data_buffer->getFrameCounter(), m_handler->getPacketCount()); m_xruns++; - - // disable the processing, will be re-enabled when - // the xrun is handled - m_disabled=true; - m_is_disabled=true; retval=RAW1394_ISO_DEFER; @@ -1275,58 +1172,4 @@ bool AmdtpReceiveStreamProcessor::getFrames(unsigned int nbframes, int64_t ts) { - m_PeriodStat.mark(m_data_buffer->getBufferFill()); - uint64_t ts_head; - signed int fc; - int32_t lag_ticks; - float lag_frames; - - // in order to sync up multiple received streams, we should - // use the ts parameter. It specifies the time of the block's - // first sample. - - ffado_timestamp_t ts_head_tmp; - m_data_buffer->getBufferHeadTimestamp(&ts_head_tmp, &fc); - ts_head=(uint64_t)ts_head_tmp; - lag_ticks=diffTicks(ts, ts_head); - float rate=m_data_buffer->getRate(); - - assert(rate!=0.0); - - lag_frames=(((float)lag_ticks)/rate); - - if (lag_frames>=1.0) { - // the stream leads - debugOutput( DEBUG_LEVEL_VERBOSE, "stream (%p): lags with %6d ticks = %10.5f frames (rate=%10.5f)\n",this,lag_ticks,lag_frames,rate); - - if (lag_frames>=10.0) { - debugOutput( DEBUG_LEVEL_VERBOSE, " %lld, %llu, %d\n", ts, ts_head, fc); - } - - // ditch the excess frames - char dummy[m_data_buffer->getBytesPerFrame()]; // one frame of garbage - int frames_to_ditch=(int)(lag_frames); - debugOutput( DEBUG_LEVEL_VERBOSE, "stream (%p): ditching %d frames (@ ts=%lld)\n",this,frames_to_ditch,ts); - - while (frames_to_ditch--) { -// m_data_buffer->readFrames(1, dummy); - } - - } else if (lag_frames<=-1.0) { - // the stream leads - debugOutput( DEBUG_LEVEL_VERBOSE, "stream (%p): leads with %6d ticks = %10.5f frames (rate=%10.5f)\n",this,lag_ticks,lag_frames,rate); - - if (lag_frames<=-10.0) { - debugOutput( DEBUG_LEVEL_VERBOSE, " %lld, %llu, %d\n", ts, ts_head, fc); - } - - // add some padding frames - int frames_to_add=(int)lag_frames; - debugOutput( DEBUG_LEVEL_VERBOSE, "stream (%p): adding %d frames (@ ts=%lld)\n",this,-frames_to_add,ts); - - while (frames_to_add++) { -// m_data_buffer->writeDummyFrame(); - } - } - // ask the buffer to process nbframes of frames // using it's registered client's processReadBlock(), @@ -1334,4 +1177,16 @@ m_data_buffer->blockProcessReadFrames(nbframes); + return true; +} + +bool AmdtpReceiveStreamProcessor::getFramesDry(unsigned int nbframes, int64_t ts) { + int frames_to_ditch=(int)(nbframes); + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "stream (%p): dry run %d frames (@ ts=%lld)\n", + this, frames_to_ditch, ts); + char dummy[m_data_buffer->getBytesPerFrame()]; // one frame of garbage + + while (frames_to_ditch--) { + m_data_buffer->readFrames(1, dummy); + } return true; } Index: /branches/ppalmers-streaming/src/libstreaming/IsoHandlerManager.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/IsoHandlerManager.cpp (revision 494) +++ /branches/ppalmers-streaming/src/libstreaming/IsoHandlerManager.cpp (revision 703) @@ -86,4 +86,5 @@ { debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n"); + pthread_mutex_init(&m_debug_lock, NULL); return true; @@ -107,8 +108,13 @@ // updateCycleTimers(); + pthread_mutex_lock(&m_debug_lock); + if(!iterate()) { debugFatal("Could not iterate the isoManager\n"); + pthread_mutex_unlock(&m_debug_lock); return false; } + + pthread_mutex_unlock(&m_debug_lock); return true; Index: /branches/ppalmers-streaming/src/libstreaming/amdtp/AmdtpPort.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/amdtp/AmdtpPort.h (revision 703) +++ /branches/ppalmers-streaming/src/libstreaming/amdtp/AmdtpPort.h (revision 703) @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2005-2007 by Pieter Palmers + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation; + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef __FFADO_AMDTPPORT__ +#define __FFADO_AMDTPPORT__ + +/** + * This file implements the AMDTP ports as used in the BeBoB's + */ + +#include "../debugmodule/debugmodule.h" +#include "Port.h" +#include "AmdtpPortInfo.h" + +namespace Streaming { + +/*! +\brief The Base Class for an AMDTP Audio Port + + The AMDTP/AM824/IEC61883-6 port that represents audio. + +*/ +class AmdtpAudioPort + : public AudioPort, public AmdtpPortInfo +{ + +public: + + AmdtpAudioPort(std::string name, + enum E_Direction direction, + int position, + int location, + enum E_Formats format) + : AudioPort(name, direction), + AmdtpPortInfo(position, location, format) + {}; + + virtual ~AmdtpAudioPort() {}; + +protected: + +}; + +/*! +\brief The Base Class for an AMDTP Midi Port + + The AMDTP/AM824/IEC61883-6 port that represents midi. + +*/ +class AmdtpMidiPort + : public MidiPort, public AmdtpPortInfo +{ + +public: + + AmdtpMidiPort(std::string name, + enum E_Direction direction, + int position, + int location, + enum E_Formats format) + : MidiPort(name, direction), + AmdtpPortInfo(position, location, format) + {}; + + + virtual ~AmdtpMidiPort() {}; + +protected: + +}; + +} // end of namespace Streaming + +#endif /* __FFADO_AMDTPPORT__ */ Index: /branches/ppalmers-streaming/src/libstreaming/amdtp/AmdtpPortInfo.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/amdtp/AmdtpPortInfo.cpp (revision 703) +++ /branches/ppalmers-streaming/src/libstreaming/amdtp/AmdtpPortInfo.cpp (revision 703) @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2005-2007 by Pieter Palmers + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation; + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "AmdtpPortInfo.h" +#include + + +namespace Streaming { + + +} // end of namespace Streaming Index: /branches/ppalmers-streaming/src/libstreaming/amdtp/AmdtpPort.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/amdtp/AmdtpPort.cpp (revision 703) +++ /branches/ppalmers-streaming/src/libstreaming/amdtp/AmdtpPort.cpp (revision 703) @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005-2007 by Pieter Palmers + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation; + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "AmdtpPort.h" +#include + +namespace Streaming { + +} // end of namespace Streaming Index: /branches/ppalmers-streaming/src/libstreaming/amdtp/AmdtpPortInfo.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/amdtp/AmdtpPortInfo.h (revision 703) +++ /branches/ppalmers-streaming/src/libstreaming/amdtp/AmdtpPortInfo.h (revision 703) @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2007 by Pieter Palmers + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation; + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef __FFADO_AMDTPPORTINFO__ +#define __FFADO_AMDTPPORTINFO__ + +#include "../debugmodule/debugmodule.h" +#include + +namespace Streaming { +/*! +\brief Class containing the stream information for an AMDTP channel + + Contains the information that maps the port to an AMDTP stream position (i.e. channel) + this allows the AMDTP stream demultiplexer to find the channel associated + to this port. + +*/ +class AmdtpPortInfo { + +public: + /** + * Sometimes a channel can have multiple formats, depending on the + * device configuration (e.g. an SPDIF port could be plain audio in 24bit integer + * or AC3 passthrough in IEC compliant frames.) + * + * This kind of enum allows to discriminate these formats when decoding + * If all channels always have the same format, you won't be needing this + */ + enum E_Formats { + E_MBLA, ///< multibit linear audio + E_Midi, ///< midi + E_SPDIF,///< IEC.... format + }; + + AmdtpPortInfo( int position, int location, enum E_Formats format) + : m_position(position), m_location(location), m_format(format) + {}; + virtual ~AmdtpPortInfo() {}; + + + int getLocation() {return m_location;}; + int getPosition() {return m_position;}; + enum E_Formats getFormat() {return m_format;}; + +protected: + int m_position; + int m_location; + enum E_Formats m_format; + +}; + +} // end of namespace Streaming + +#endif /* __FFADO_AMDTPPORTINFO__ */ Index: /branches/ppalmers-streaming/src/libstreaming/AmdtpStreamProcessor.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/AmdtpStreamProcessor.h (revision 494) +++ /branches/ppalmers-streaming/src/libstreaming/AmdtpStreamProcessor.h (revision 703) @@ -96,4 +96,5 @@ bool putFrames(unsigned int nbframes, int64_t ts); ///< transfer the buffer contents from the client + bool putFramesDry(unsigned int nbframes, int64_t ts); // We have 1 period of samples = m_period @@ -188,4 +189,5 @@ bool getFrames(unsigned int nbframes, int64_t ts); ///< transfer the buffer contents to the client + bool getFramesDry(unsigned int nbframes, int64_t ts); // We have 1 period of samples = m_period Index: /branches/ppalmers-streaming/src/libstreaming/StreamProcessorManager.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/StreamProcessorManager.cpp (revision 512) +++ /branches/ppalmers-streaming/src/libstreaming/StreamProcessorManager.cpp (revision 703) @@ -173,5 +173,5 @@ debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n"); - m_isoManager=new IsoHandlerManager(m_thread_realtime, m_thread_priority); + m_isoManager=new IsoHandlerManager(m_thread_realtime, m_thread_priority + 1); if(!m_isoManager) { @@ -296,21 +296,6 @@ } - // EXPERIMENT: - // the only stream that should be running is the sync - // source stream, as this is the one that defines - // when to signal buffers. Maybe we get an xrun at startup, - // but that should be handled. - - // the problem is that otherwise a setup with a device - // that waits for decent input before sending output - // will not start up (e.g. the bounce device), because - // all streams are required to be running. - - // other streams still have at least ENABLE_DELAY_CYCLES cycles - // to start up -// if(!m_SyncSource->isRunning()) notRunning=true; - usleep(1000); - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Running check: %d\n",notRunning); + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Running check: %d\n", notRunning); } @@ -342,5 +327,5 @@ // we want to make sure that everything is running well, // so wait for a while - usleep(USECS_PER_CYCLE * CYCLES_TO_SLEEP_AFTER_RUN_SIGNAL); +// usleep(USECS_PER_CYCLE * CYCLES_TO_SLEEP_AFTER_RUN_SIGNAL); debugOutput( DEBUG_LEVEL_VERBOSE, " StreamProcessor streams running...\n"); @@ -373,4 +358,6 @@ it != m_ReceiveProcessors.end(); ++it ) { + // get the receive SP's going at receiving data + (*it)->m_data_buffer->setTransparent(false); (*it)->reset(); } @@ -379,7 +366,68 @@ it != m_TransmitProcessors.end(); ++it ) { + // make sure the SP retains it's prefilled data + (*it)->m_data_buffer->setTransparent(false); (*it)->reset(); } - + + dumpInfo(); + // All buffers are prefilled and set-up, the only thing + // that remains a mistery is the timestamp information. +// m_SyncSource->m_data_buffer->setTransparent(false); +// debugShowBackLog(); + + debugOutput( DEBUG_LEVEL_VERBOSE, "Waiting for sync...\n"); + // in order to obtain that, we wait for the first periods to be + // received. + int nb_sync_runs=10; + while(nb_sync_runs--) { // or while not sync-ed? + waitForPeriod(); + + // drop the frames for all receive SP's + dryRun(StreamProcessor::E_Receive); + + // we don't have to dryrun for the xmit SP's since they + // are not sending data yet. + } + + debugOutput( DEBUG_LEVEL_VERBOSE, " sync at TS=%011llu...\n", m_time_of_transfer); + // FIXME: xruns can screw up the framecounter accounting. do something more sane here + resetXrunCounters(); + // lock the isohandlermanager such that things freeze +// debugShowBackLog(); + + // we now should have decent sync info + // the buffers of the receive streams should be (approx) empty + // the buffers of the xmit streams should be full + + // note what should the timestamp of the first sample be? + + // at this point the buffer head timestamp of the transmit buffers can be + // set properly since we know the sync source's timestamp of the last + // buffer transfer. we also know the rate. + + debugOutput( DEBUG_LEVEL_VERBOSE, " propagate sync info...\n"); + // FIXME: in the SPM it would be nice to have system time instead of + // 1394 time +// float rate=m_SyncSource->getTicksPerFrame(); +// int64_t one_ringbuffer_in_ticks=(int64_t)(((float)(m_nb_buffers*m_period))*rate); +// // the data at the front of the buffer is intended to be transmitted +// // nb_periods*period_size after the last received period +// int64_t transmit_timestamp = addTicks(m_time_of_transfer, one_ringbuffer_in_ticks); + + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + // FIXME: encapsulate + (*it)->m_data_buffer->setBufferHeadTimestamp(m_time_of_transfer); + //(*it)->m_data_buffer->setNominalRate(rate); //CHECK!!! + } + + dumpInfo(); + + // this is the place were we should be syncing the received streams too + // check how much they differ + + debugOutput( DEBUG_LEVEL_VERBOSE, "Enabling StreamProcessors...\n"); @@ -387,4 +435,5 @@ // FIXME: this should not be in cycles, but in 'time' + // FIXME: remove the timestamp unsigned int enable_at=TICKS_TO_CYCLES(now)+ENABLE_DELAY_CYCLES; if (enable_at > 8000) enable_at -= 8000; @@ -395,5 +444,41 @@ } - return true; + debugOutput( DEBUG_LEVEL_VERBOSE, "Running dry for a while...\n"); + // run some cycles 'dry' such that everything can stabilize + int nb_dryrun_cycles=10; + bool no_xrun; + while(nb_dryrun_cycles--) { + waitForPeriod(); + no_xrun = dryRun(); // dry run both receive and xmit + + if (no_xrun) { + debugOutput( DEBUG_LEVEL_VERBOSE, " This dry-run was not xrun free...\n" ); + resetXrunCounters(); + // FIXME: xruns can screw up the framecounter accounting. do something more sane here + } + } + + // now we should clear the xrun flags + resetXrunCounters(); + + // and off we go + return true; +} + +void StreamProcessorManager::resetXrunCounters(){ + debugOutput( DEBUG_LEVEL_VERBOSE, "Resetting xrun flags...\n"); + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) + { + (*it)->resetXrunCounter(); + } + + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) + { + (*it)->resetXrunCounter(); + } } @@ -492,4 +577,9 @@ } + debugOutput( DEBUG_LEVEL_VERBOSE, "Disabling StreamProcessors...\n"); + if (!disableStreamProcessors()) { + debugFatal("Could not disable StreamProcessors...\n"); + return false; + } debugOutput( DEBUG_LEVEL_VERBOSE, "Stopping handlers...\n"); @@ -585,51 +675,51 @@ // now we wait for the SP's to get enabled - debugOutput( DEBUG_LEVEL_VERBOSE, "Waiting for all StreamProcessors to be enabled...\n"); - // we have to wait until all streamprocessors indicate that they are running - // i.e. that there is actually some data stream flowing - int wait_cycles=ENABLE_TIMEOUT_MSEC; // two seconds - bool notEnabled=true; - while (notEnabled && wait_cycles) { - wait_cycles--; - notEnabled=false; - - for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); - it != m_ReceiveProcessors.end(); - ++it ) { - if(!(*it)->isEnabled()) notEnabled=true; - } - - for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); - it != m_TransmitProcessors.end(); - ++it ) { - if(!(*it)->isEnabled()) notEnabled=true; - } - usleep(1000); // one cycle - } - - if(!wait_cycles) { // timout has occurred - debugFatal("One or more streams couldn't be enabled (timeout):\n"); - - for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); - it != m_ReceiveProcessors.end(); - ++it ) { - if(!(*it)->isEnabled()) { - debugFatal(" receive stream %p not enabled\n",*it); - } else { - debugFatal(" receive stream %p enabled\n",*it); - } - } - - for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); - it != m_TransmitProcessors.end(); - ++it ) { - if(!(*it)->isEnabled()) { - debugFatal(" transmit stream %p not enabled\n",*it); - } else { - debugFatal(" transmit stream %p enabled\n",*it); - } - } - return false; - } +// debugOutput( DEBUG_LEVEL_VERBOSE, "Waiting for all StreamProcessors to be enabled...\n"); +// // we have to wait until all streamprocessors indicate that they are running +// // i.e. that there is actually some data stream flowing +// int wait_cycles=ENABLE_TIMEOUT_MSEC; // two seconds +// bool notEnabled=true; +// while (notEnabled && wait_cycles) { +// wait_cycles--; +// notEnabled=false; +// +// for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); +// it != m_ReceiveProcessors.end(); +// ++it ) { +// if(!(*it)->isEnabled()) notEnabled=true; +// } +// +// for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); +// it != m_TransmitProcessors.end(); +// ++it ) { +// if(!(*it)->isEnabled()) notEnabled=true; +// } +// usleep(1000); // one cycle +// } +// +// if(!wait_cycles) { // timout has occurred +// debugFatal("One or more streams couldn't be enabled (timeout):\n"); +// +// for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); +// it != m_ReceiveProcessors.end(); +// ++it ) { +// if(!(*it)->isEnabled()) { +// debugFatal(" receive stream %p not enabled\n",*it); +// } else { +// debugFatal(" receive stream %p enabled\n",*it); +// } +// } +// +// for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); +// it != m_TransmitProcessors.end(); +// ++it ) { +// if(!(*it)->isEnabled()) { +// debugFatal(" transmit stream %p not enabled\n",*it); +// } else { +// debugFatal(" transmit stream %p enabled\n",*it); +// } +// } +// return false; +// } debugOutput( DEBUG_LEVEL_VERBOSE, " => all StreamProcessors enabled...\n"); @@ -661,4 +751,5 @@ ++it ) { (*it)->disable(); + (*it)->m_data_buffer->setTransparent(true); } @@ -667,4 +758,5 @@ ++it ) { (*it)->disable(); + (*it)->m_data_buffer->setTransparent(true); } @@ -833,5 +925,5 @@ } debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "XF at %011llu ticks, RBF=%d, XBF=%d, SUM=%d...\n", - m_time_of_transfer,rcv_bf,xmt_bf,rcv_bf+xmt_bf); + m_time_of_transfer, rcv_bf, xmt_bf, rcv_bf+xmt_bf); #endif @@ -925,5 +1017,5 @@ int64_t one_frame_in_ticks=(int64_t)(((float)m_period)*rate); - int64_t receive_timestamp = substractTicks(m_time_of_transfer,one_frame_in_ticks); + int64_t receive_timestamp = substractTicks(m_time_of_transfer, one_frame_in_ticks); if(receive_timestamp<0) { @@ -951,8 +1043,99 @@ it != m_TransmitProcessors.end(); ++it ) { - - if(!(*it)->putFrames(m_period, (int64_t)m_time_of_transfer)) { + // FIXME: in the SPM it would be nice to have system time instead of + // 1394 time + float rate=m_SyncSource->getTicksPerFrame(); + int64_t one_ringbuffer_in_ticks=(int64_t)(((float)(m_nb_buffers*m_period))*rate); + + // the data we are putting into the buffer is intended to be transmitted + // one ringbuffer size after it has been received + int64_t transmit_timestamp = addTicks(m_time_of_transfer, one_ringbuffer_in_ticks); + + if(!(*it)->putFrames(m_period, transmit_timestamp)) { debugOutput(DEBUG_LEVEL_VERBOSE, "could not putFrames(%u,%llu) to stream processor (%p)\n", - m_period, m_time_of_transfer, *it); + m_period, transmit_timestamp, *it); + return false; // buffer overrun + } + + } + } + + return true; +} + +/** + * @brief Dry run one period for both receive and transmit StreamProcessors + * + * Process one period of frames for all streamprocessors, without touching the + * client buffers. This only removes an incoming period from the ISO receive buffer and + * puts one period of silence into the transmit buffers. + * + * @return true if successful, false otherwise (indicates xrun). + */ +bool StreamProcessorManager::dryRun() { + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Dry-running period...\n"); + if (!dryRun(StreamProcessor::E_Receive)) return false; + if (!dryRun(StreamProcessor::E_Transmit)) return false; + return true; +} + +/** + * @brief Dry run one period for either the receive or transmit StreamProcessors + * + * see dryRun() + * + * @param t The processor type to dryRun for (receive or transmit) + * @return true if successful, false otherwise (indicates xrun). + */ + +bool StreamProcessorManager::dryRun(enum StreamProcessor::EProcessorType t) { + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Transferring period...\n"); + + // a static cast could make sure that there is no performance + // penalty for the virtual functions (to be checked) + if (t==StreamProcessor::E_Receive) { + + // determine the time at which we want reception to start + float rate=m_SyncSource->getTicksPerFrame(); + int64_t one_frame_in_ticks=(int64_t)(((float)m_period)*rate); + + int64_t receive_timestamp = substractTicks(m_time_of_transfer, one_frame_in_ticks); + + if(receive_timestamp<0) { + debugWarning("receive ts < 0.0 : %lld, m_time_of_transfer= %llu, one_frame_in_ticks=%lld\n", + receive_timestamp, m_time_of_transfer, one_frame_in_ticks); + } + if(receive_timestamp>(128L*TICKS_PER_SECOND)) { + debugWarning("receive ts > 128L*TICKS_PER_SECOND : %lld, m_time_of_transfer= %llu, one_frame_in_ticks=%lld\n", + receive_timestamp, m_time_of_transfer, one_frame_in_ticks); + } + + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + + if(!(*it)->getFramesDry(m_period, receive_timestamp)) { + debugOutput(DEBUG_LEVEL_VERBOSE,"could not getFrames(%u, %11llu) from stream processor (%p)\n", + m_period, m_time_of_transfer,*it); + return false; // buffer underrun + } + + } + } else { + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + // FIXME: in the SPM it would be nice to have system time instead of + // 1394 time + float rate=m_SyncSource->getTicksPerFrame(); + int64_t one_ringbuffer_in_ticks=(int64_t)(((float)(m_nb_buffers*m_period))*rate); + + // the data we are putting into the buffer is intended to be transmitted + // one ringbuffer size after it has been received + int64_t transmit_timestamp = addTicks(m_time_of_transfer, one_ringbuffer_in_ticks); + + if(!(*it)->putFramesDry(m_period, transmit_timestamp)) { + debugOutput(DEBUG_LEVEL_VERBOSE, "could not putFrames(%u,%llu) to stream processor (%p)\n", + m_period, transmit_timestamp, *it); return false; // buffer overrun } Index: /branches/ppalmers-streaming/src/libstreaming/IsoHandlerManager.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/IsoHandlerManager.h (revision 445) +++ /branches/ppalmers-streaming/src/libstreaming/IsoHandlerManager.h (revision 703) @@ -102,4 +102,5 @@ bool Execute(); // note that this is called in we while(running) loop bool Init(); + pthread_mutex_t m_debug_lock; // the state machine @@ -149,5 +150,4 @@ Util::PosixThread *m_isoManagerThread; - // debug stuff DECLARE_DEBUG_MODULE; Index: /branches/ppalmers-streaming/src/libstreaming/StreamProcessor.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/StreamProcessor.cpp (revision 494) +++ /branches/ppalmers-streaming/src/libstreaming/StreamProcessor.cpp (revision 703) @@ -211,4 +211,5 @@ } #endif + m_data_buffer->enable(); m_disabled=false; @@ -217,4 +218,5 @@ bool StreamProcessor::disable() { + m_data_buffer->disable(); m_disabled=true; return true; @@ -261,5 +263,5 @@ int32_t until_next=diffTicks(time_at_period,cycle_timer); - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "=> TAP=%11llu, CTR=%11llu, UTN=%11lld\n", + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "=> TAP=%11llu, CTR=%11llu, UTN=%11ld\n", time_at_period, cycle_timer, until_next ); @@ -274,4 +276,9 @@ uint64_t StreamProcessor::getTimeAtPeriodUsecs() { return (uint64_t)((float)getTimeAtPeriod() * TICKS_PER_USEC); +} + +bool StreamProcessor::dropFrames(unsigned int nbframes) { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "StreamProcessor::dropFrames(%d)\n", nbframes); + return m_data_buffer->dropFrames(nbframes); } Index: /branches/ppalmers-streaming/src/libstreaming/StreamProcessorManager.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/StreamProcessorManager.h (revision 445) +++ /branches/ppalmers-streaming/src/libstreaming/StreamProcessorManager.h (revision 703) @@ -87,4 +87,7 @@ bool transfer(); ///< transfer the buffer contents from/to client bool transfer(enum StreamProcessor::EProcessorType); ///< transfer the buffer contents from/to client (single processor type) + + bool dryRun(); + bool dryRun(enum StreamProcessor::EProcessorType); int getDelayedUsecs() {return m_delayed_usecs;}; @@ -93,4 +96,6 @@ private: + void resetXrunCounters(); + int m_delayed_usecs; // this stores the time at which the next transfer should occur Index: /anches/ppalmers-streaming/src/libstreaming/MotuPort.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/MotuPort.h (revision 554) +++ (revision ) @@ -1,116 +1,0 @@ -/* - * Copyright (C) 2005-2007 by Jonathan Woithe - * Copyright (C) 2005-2007 by Pieter Palmers - * - * This file is part of FFADO - * FFADO = Free Firewire (pro-)audio drivers for linux - * - * FFADO is based upon FreeBoB. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software Foundation; - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#ifndef __FFADO_MOTUPORT__ -#define __FFADO_MOTUPORT__ - -/** - * This file implements the ports used in Motu devices - */ - -#include "../debugmodule/debugmodule.h" -#include "Port.h" -#include "MotuPortInfo.h" - -namespace Streaming { - -/*! -\brief The Base Class for Motu Audio Port - - -*/ -class MotuAudioPort - : public AudioPort, public MotuPortInfo -{ - -public: - - MotuAudioPort(std::string name, - enum E_Direction direction, - int position, - int size) - : AudioPort(name, direction), - MotuPortInfo( position, size) // TODO: add more port information parameters here if nescessary - {}; - - virtual ~MotuAudioPort() {}; - -protected: - -}; - -/*! -\brief The Base Class for an Motu Midi Port - - -*/ -class MotuMidiPort - : public MidiPort, public MotuPortInfo -{ - -public: - - MotuMidiPort(std::string name, - enum E_Direction direction, - int position) - : MidiPort(name, direction), - MotuPortInfo(position, 0) // TODO: add more port information parameters here if nescessary - {}; - - - virtual ~MotuMidiPort() {}; - -protected: - -}; - -/*! -\brief The Base Class for an Motu Control Port - - -*/ -class MotuControlPort - : public ControlPort, public MotuPortInfo -{ - -public: - - MotuControlPort(std::string name, - enum E_Direction direction, - int position) - : ControlPort(name, direction), - MotuPortInfo(position, 2) // TODO: add more port information parameters here if nescessary - {}; - - - virtual ~MotuControlPort() {}; - -protected: - -}; - -} // end of namespace Streaming - -#endif /* __FFADO_MOTUPORT__ */ - Index: /anches/ppalmers-streaming/src/libstreaming/MotuStreamProcessor.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/MotuStreamProcessor.h (revision 512) +++ (revision ) @@ -1,182 +1,0 @@ -/* - * Copyright (C) 2005-2007 by Jonathan Woithe - * Copyright (C) 2005-2007 by Pieter Palmers - * - * This file is part of FFADO - * FFADO = Free Firewire (pro-)audio drivers for linux - * - * FFADO is based upon FreeBoB. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software Foundation; - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#ifndef __FFADO_MOTUSTREAMPROCESSOR__ -#define __FFADO_MOTUSTREAMPROCESSOR__ -#include - -#include "../debugmodule/debugmodule.h" -#include "StreamProcessor.h" - -#include "../libutil/DelayLockedLoop.h" - -namespace Streaming { - -class MotuAudioPort; - -/** - * This class implements the outgoing stream processing for - * motu devices - */ -class MotuTransmitStreamProcessor - : public TransmitStreamProcessor -{ -public: - - MotuTransmitStreamProcessor(int port, int framerate, - unsigned int event_size); - - virtual ~MotuTransmitStreamProcessor(); - - enum raw1394_iso_disposition - getPacket(unsigned char *data, unsigned int *length, - unsigned char *tag, unsigned char *sy, - int cycle, unsigned int dropped, unsigned int max_length); - - bool init(); - bool reset(); - bool prepare(); - - bool prepareForStop(); - bool prepareForStart(); - - bool prepareForEnable(uint64_t time_to_enable_at); - - bool putFrames(unsigned int nbframes, int64_t ts); ///< transfer the buffer contents from the client - - // These two are important to calculate the optimal ISO DMA buffers - // size. An estimate will do. - unsigned int getPacketsPerPeriod() {return (m_period*8000) / m_framerate;}; - unsigned int getMaxPacketSize() {return m_framerate<=48000?616:(m_framerate<=96000?1032:1160);}; - - int getMinimalSyncDelay(); - - void setVerboseLevel(int l); - -protected: - /* - * An iso packet mostly consists of multiple events. m_event_size - * is the size of a single 'event' in bytes. - */ - unsigned int m_event_size; - - // Keep track of transmission data block count - unsigned int m_tx_dbc; - - // Used to zero output data during startup while sync is established - signed int m_startup_count; - - // Used to keep track of the close-down zeroing of output data - signed int m_closedown_count; - signed int m_streaming_active; - - bool prefill(); - - bool transferSilence(unsigned int size); - - bool processWriteBlock(char *data, unsigned int nevents, unsigned int offset); - - bool encodePacketPorts(quadlet_t *data, unsigned int nevents, unsigned int dbc); - - int transmitSilenceBlock(char *data, unsigned int nevents, - unsigned int offset); - - int encodePortToMotuEvents(MotuAudioPort *p, quadlet_t *data, - unsigned int offset, unsigned int nevents); - int encodeSilencePortToMotuEvents(MotuAudioPort *p, quadlet_t *data, - unsigned int offset, unsigned int nevents); - - DECLARE_DEBUG_MODULE; - -}; - -/** - * This class implements the incoming stream processing for - * motu devices - */ -class MotuReceiveStreamProcessor - : public ReceiveStreamProcessor -{ - -public: - - MotuReceiveStreamProcessor(int port, int framerate, unsigned int event_size); - virtual ~MotuReceiveStreamProcessor(); - - enum raw1394_iso_disposition putPacket(unsigned char *data, unsigned int length, - unsigned char channel, unsigned char tag, unsigned char sy, - unsigned int cycle, unsigned int dropped); - - bool getFrames(unsigned int nbframes, int64_t ts); ///< transfer the buffer contents to the client - - bool init(); - bool reset(); - bool prepare(); - - // these two are important to calculate the optimal - // ISO DMA buffers size - // an estimate will do - unsigned int getPacketsPerPeriod() {return (m_period*8000) / m_framerate;}; - unsigned int getMaxPacketSize() {return m_framerate<=48000?616:(m_framerate<=96000?1032:1160);}; - - int getMinimalSyncDelay(); - - virtual void setVerboseLevel(int l); - - signed int setEventSize(unsigned int size); - unsigned int getEventSize(void); - - virtual bool prepareForStop(); - virtual bool prepareForStart(); - -protected: - - bool processReadBlock(char *data, unsigned int nevents, unsigned int offset); - - bool decodePacketPorts(quadlet_t *data, unsigned int nevents, unsigned int dbc); - signed int decodeMotuEventsToPort(MotuAudioPort *p, quadlet_t *data, unsigned int offset, unsigned int nevents); - - /* - * An iso packet mostly consists of multiple events. m_event_size - * is the size of a single 'event' in bytes. - */ - unsigned int m_event_size; - - // Signifies a closedown is in progress, in which case incoming data - // is junked. - signed int m_closedown_active; - - uint64_t m_last_timestamp; /// last timestamp (in ticks) - uint64_t m_last_timestamp2; /// last timestamp (in ticks) - uint64_t m_last_timestamp_at_period_ticks; - - DECLARE_DEBUG_MODULE; - -}; - -} // end of namespace Streaming - -#endif /* __FFADO_MOTUSTREAMPROCESSOR__ */ - - Index: /anches/ppalmers-streaming/src/libstreaming/MotuPortInfo.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/MotuPortInfo.cpp (revision 445) +++ (revision ) @@ -1,31 +1,0 @@ -/* - * Copyright (C) 2005-2007 by Jonathan Woithe - * Copyright (C) 2005-2007 by Pieter Palmers - * - * This file is part of FFADO - * FFADO = Free Firewire (pro-)audio drivers for linux - * - * FFADO is based upon FreeBoB. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software Foundation; - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include "MotuPortInfo.h" -#include - -namespace Streaming { - - -} // end of namespace Streaming Index: /anches/ppalmers-streaming/src/libstreaming/AmdtpPort.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/AmdtpPort.h (revision 554) +++ (revision ) @@ -1,94 +1,0 @@ -/* - * Copyright (C) 2005-2007 by Pieter Palmers - * - * This file is part of FFADO - * FFADO = Free Firewire (pro-)audio drivers for linux - * - * FFADO is based upon FreeBoB. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software Foundation; - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#ifndef __FFADO_AMDTPPORT__ -#define __FFADO_AMDTPPORT__ - -/** - * This file implements the AMDTP ports as used in the BeBoB's - */ - -#include "../debugmodule/debugmodule.h" -#include "Port.h" -#include "AmdtpPortInfo.h" - -namespace Streaming { - -/*! -\brief The Base Class for an AMDTP Audio Port - - The AMDTP/AM824/IEC61883-6 port that represents audio. - -*/ -class AmdtpAudioPort - : public AudioPort, public AmdtpPortInfo -{ - -public: - - AmdtpAudioPort(std::string name, - enum E_Direction direction, - int position, - int location, - enum E_Formats format) - : AudioPort(name, direction), - AmdtpPortInfo(position, location, format) - {}; - - virtual ~AmdtpAudioPort() {}; - -protected: - -}; - -/*! -\brief The Base Class for an AMDTP Midi Port - - The AMDTP/AM824/IEC61883-6 port that represents midi. - -*/ -class AmdtpMidiPort - : public MidiPort, public AmdtpPortInfo -{ - -public: - - AmdtpMidiPort(std::string name, - enum E_Direction direction, - int position, - int location, - enum E_Formats format) - : MidiPort(name, direction), - AmdtpPortInfo(position, location, format) - {}; - - - virtual ~AmdtpMidiPort() {}; - -protected: - -}; - -} // end of namespace Streaming - -#endif /* __FFADO_AMDTPPORT__ */ Index: /anches/ppalmers-streaming/src/libstreaming/AmdtpPortInfo.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/AmdtpPortInfo.cpp (revision 445) +++ (revision ) @@ -1,31 +1,0 @@ -/* - * Copyright (C) 2005-2007 by Pieter Palmers - * - * This file is part of FFADO - * FFADO = Free Firewire (pro-)audio drivers for linux - * - * FFADO is based upon FreeBoB. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software Foundation; - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include "AmdtpPortInfo.h" -#include - - -namespace Streaming { - - -} // end of namespace Streaming Index: /anches/ppalmers-streaming/src/libstreaming/MotuPort.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/MotuPort.cpp (revision 445) +++ (revision ) @@ -1,30 +1,0 @@ -/* - * Copyright (C) 2005-2007 by Jonathan Woithe - * Copyright (C) 2005-2007 by Pieter Palmers - * - * This file is part of FFADO - * FFADO = Free Firewire (pro-)audio drivers for linux - * - * FFADO is based upon FreeBoB. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software Foundation; - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include "MotuPort.h" -#include - -namespace Streaming { - -} // end of namespace Streaming Index: /anches/ppalmers-streaming/src/libstreaming/MotuStreamProcessor.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/MotuStreamProcessor.cpp (revision 577) +++ (revision ) @@ -1,1470 +1,0 @@ -/* - * Copyright (C) 2005-2007 by Jonathan Woithe - * Copyright (C) 2005-2007 by Pieter Palmers - * - * This file is part of FFADO - * FFADO = Free Firewire (pro-)audio drivers for linux - * - * FFADO is based upon FreeBoB. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software Foundation; - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include "MotuStreamProcessor.h" -#include "Port.h" -#include "MotuPort.h" - -#include - -#include - -#include "cycletimer.h" - -// in ticks -#define TRANSMIT_TRANSFER_DELAY 6000U -// the number of cycles to send a packet in advance of it's timestamp -#define TRANSMIT_ADVANCE_CYCLES 1U - -namespace Streaming { - -IMPL_DEBUG_MODULE( MotuTransmitStreamProcessor, MotuTransmitStreamProcessor, DEBUG_LEVEL_NORMAL ); -IMPL_DEBUG_MODULE( MotuReceiveStreamProcessor, MotuReceiveStreamProcessor, DEBUG_LEVEL_NORMAL ); - -// Set to 1 to enable the generation of a 1 kHz test tone in analog output 1 -#define TESTTONE 1 - -// A macro to extract specific bits from a native endian quadlet -#define get_bits(_d,_start,_len) (((_d)>>((_start)-(_len)+1)) & ((1<<(_len))-1)) - -// Convert an SPH timestamp as received from the MOTU to a full timestamp in ticks. -static inline uint32_t sphRecvToFullTicks(uint32_t sph, uint32_t ct_now) { - -uint32_t timestamp = CYCLE_TIMER_TO_TICKS(sph & 0x1ffffff); -uint32_t now_cycles = CYCLE_TIMER_GET_CYCLES(ct_now); - -uint32_t ts_sec = CYCLE_TIMER_GET_SECS(ct_now); - // If the cycles have wrapped, correct ts_sec so it represents when timestamp - // was received. The timestamps sent by the MOTU are always 1 or two cycles - // in advance of the cycle timer (reasons unknown at this stage). In addition, - // iso buffering can delay the arrival of packets for quite a number of cycles - // (have seen a delay >12 cycles). - // Every so often we also see sph wrapping ahead of ct_now, so deal with that - // too. - if (CYCLE_TIMER_GET_CYCLES(sph) > now_cycles + 1000) { - if (ts_sec) - ts_sec--; - else - ts_sec = 127; - } else - if (now_cycles > CYCLE_TIMER_GET_CYCLES(sph) + 1000) { - if (ts_sec == 127) - ts_sec = 0; - else - ts_sec++; - } - return timestamp + ts_sec*TICKS_PER_SECOND; -} - -// Convert a full timestamp into an SPH timestamp as required by the MOTU -static inline uint32_t fullTicksToSph(int64_t timestamp) { - return TICKS_TO_CYCLE_TIMER(timestamp) & 0x1ffffff; -} - -/* transmit */ -MotuTransmitStreamProcessor::MotuTransmitStreamProcessor(int port, int framerate, - unsigned int event_size) - : TransmitStreamProcessor(port, framerate), m_event_size(event_size), - m_tx_dbc(0), - m_startup_count(-1), m_closedown_count(-1), m_streaming_active(0) { -} - -MotuTransmitStreamProcessor::~MotuTransmitStreamProcessor() { - -} - -bool MotuTransmitStreamProcessor::init() { - - debugOutput( DEBUG_LEVEL_VERBOSE, "Initializing (%p)...\n"); - // call the parent init - // this has to be done before allocating the buffers, - // because this sets the buffersizes from the processormanager - if(!TransmitStreamProcessor::init()) { - debugFatal("Could not do base class init (%p)\n",this); - return false; - } - - return true; -} - -void MotuTransmitStreamProcessor::setVerboseLevel(int l) { - setDebugLevel(l); // sets the debug level of the current object - TransmitStreamProcessor::setVerboseLevel(l); // also set the level of the base class -} - - -enum raw1394_iso_disposition -MotuTransmitStreamProcessor::getPacket(unsigned char *data, unsigned int *length, - unsigned char *tag, unsigned char *sy, - int cycle, unsigned int dropped, unsigned int max_length) { - - int fc; - ffado_timestamp_t ts_tmp; - quadlet_t *quadlet = (quadlet_t *)data; - signed int i; - - // The number of events per packet expected by the MOTU is solely - // dependent on the current sample rate. An 'event' is one sample from - // all channels plus possibly other midi and control data. - signed n_events = m_framerate<=48000?8:(m_framerate<=96000?16:32); - - m_last_cycle=cycle; - - // determine if we want to send a packet or not - // note that we can't use getCycleTimer directly here, - // because packets are queued in advance. This means that - // we the packet we are constructing will be sent out - // on 'cycle', not 'now'. - unsigned int ctr=m_handler->getCycleTimer(); - int now_cycles = (int)CYCLE_TIMER_GET_CYCLES(ctr); - - // the difference between the cycle this - // packet is intended for and 'now' - int cycle_diff = diffCycles(cycle, now_cycles); - - // Signal that streaming is still active - m_streaming_active = 1; - - // as long as the cycle parameter is not in sync with - // the current time, the stream is considered not - // to be 'running' - // NOTE: this works only at startup - if (!m_running && cycle_diff >= 0 && cycle >= 0) { - debugOutput(DEBUG_LEVEL_VERBOSE, "Xmit StreamProcessor %p started running at cycle %d\n",this, cycle); - m_running=true; - } - - if (!m_disabled && m_is_disabled) { - // this means that we are trying to enable - - // check if we are on or past the enable point - signed int cycles_past_enable=diffCycles(cycle, m_cycle_to_enable_at); - - if (cycles_past_enable >= 0) { - m_is_disabled=false; - - debugOutput(DEBUG_LEVEL_VERBOSE,"Enabling Tx StreamProcessor %p at %u\n", this, cycle); - - // initialize the buffer head & tail - m_SyncSource->m_data_buffer->getBufferHeadTimestamp(&ts_tmp, &fc); // thread safe - int64_t ts_head = (int64_t)ts_tmp; - - // the number of cycles the sync source lags (> 0) - // or leads (< 0) - int sync_lag_cycles=diffCycles(cycle, m_SyncSource->getLastCycle()); - - // account for the cycle lag between sync SP and this SP - // the last update of the sync source's timestamps was sync_lag_cycles - // cycles before the cycle we are calculating the timestamp for. - // if we were to use one-frame buffers, you would expect the - // frame that is sent on cycle CT to have a timestamp T1. - // ts_head however is for cycle CT-sync_lag_cycles, and lies - // therefore sync_lag_cycles * TICKS_PER_CYCLE earlier than - // T1. - ts_head = addTicks(ts_head, sync_lag_cycles * TICKS_PER_CYCLE); - -// These are just copied from AmdtpStreamProcessor. At some point we should -// verify that they make sense for the MOTU. - ts_head = substractTicks(ts_head, TICKS_PER_CYCLE); - // account for the number of cycles we are too late to enable - ts_head = addTicks(ts_head, cycles_past_enable * TICKS_PER_CYCLE); - // account for one extra packet of frames -// For the MOTU this subtraction doesn't seem necessary, and in general just makes it take -// longer to achieve stable sync. -// ts_head = substractTicks(ts_head, -// (uint32_t)((float)n_events * m_SyncSource->m_data_buffer->getRate())); -// ts_head = substractTicks(ts_head, -// (uint32_t)(m_SyncSource->m_data_buffer->getRate())); - m_data_buffer->setBufferTailTimestamp(ts_head); - - // Set up the startup count which keeps the output muted during - // sync stabilisation at startup. For now we go for about 2 seconds of - // muting. Note that this is a count of *packets*, not frames. - m_startup_count = 2*m_framerate/n_events; - - #ifdef DEBUG - if ((unsigned int)m_data_buffer->getFrameCounter() != m_data_buffer->getBufferSize()) { - debugWarning("m_data_buffer->getFrameCounter() != m_data_buffer->getBufferSize()\n"); - } - #endif - debugOutput(DEBUG_LEVEL_VERBOSE,"XMIT TS SET: TS=%10lld, LAG=%03d, FC=%4d\n", - ts_head, sync_lag_cycles, m_data_buffer->getFrameCounter()); - } else { - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, - "will enable StreamProcessor %p at %u, now is %d\n", - this, m_cycle_to_enable_at, cycle); - } - } else if (m_disabled && !m_is_disabled) { - // trying to disable - debugOutput(DEBUG_LEVEL_VERBOSE,"disabling StreamProcessor %p at %u\n", - this, cycle); - m_is_disabled=true; - m_startup_count = -1; - } - - // Do housekeeping expected for all packets sent to the MOTU, even - // for packets containing no audio data. - *sy = 0x00; - *tag = 1; // All MOTU packets have a CIP-like header - - // the base timestamp is the one of the next sample in the buffer - m_data_buffer->getBufferHeadTimestamp(&ts_tmp, &fc); // thread safe - int64_t timestamp = (int64_t)ts_tmp; - - // we send a packet some cycles in advance, to avoid the - // following situation: - // suppose we are only a few ticks away from - // the moment to send this packet. therefore we decide - // not to send the packet, but send it in the next cycle. - // This means that the next time point will be 3072 ticks - // later, making that the timestamp will be expired when the - // packet is sent, unless TRANSFER_DELAY > 3072. - // this means that we need at least one cycle of extra buffering. - int64_t ticks_to_advance = TICKS_PER_CYCLE * TRANSMIT_ADVANCE_CYCLES; - - // if cycle lies cycle_diff cycles in the future, we should - // queue this packet cycle_diff * TICKS_PER_CYCLE earlier than - // we would if it were to be sent immediately. - ticks_to_advance += (int64_t)cycle_diff * TICKS_PER_CYCLE; - - // time until the packet is to be sent (if <= 0: send packet) - // For Amdtp this looked like - // // determine the 'now' time in ticks - // uint64_t cycle_timer=CYCLE_TIMER_TO_TICKS(ctr); - // int32_t until_next=diffTicks(timestamp, cycle_timer + ticks_to_advance); - // However, this didn't seem to work well with the MOTU's buffer structure. - // For now we'll revert to the "send trigger" we used earlier since this - // seems to work. - int32_t until_next = (cycle >= TICKS_TO_CYCLES(timestamp))?-1:1; - - // Size of a single data frame in quadlets - unsigned dbs = m_event_size / 4; - - // don't process the stream when it is not enabled, not running - // or when the next sample is not due yet. - if((until_next>0) || m_is_disabled || !m_running) { - // send dummy packet - - // construct the packet CIP-like header. Even if this is a data-less - // packet the dbs field is still set as if there were data blocks - // present. For data-less packets the dbc is the same as the previously - // transmitted block. - *quadlet = htonl(0x00000400 | ((getNodeId()&0x3f)<<24) | m_tx_dbc | (dbs<<16)); - quadlet++; - *quadlet = htonl(0x8222ffff); - quadlet++; - *length = 8; - - return RAW1394_ISO_DEFER; - } - - // add the transmit transfer delay to construct the playout time - ffado_timestamp_t ts=addTicks(timestamp, TRANSMIT_TRANSFER_DELAY); - - // Only read frames from the tx buffer when we're not in the process of - // stopping. When preparing for stop the buffer isn't necessarily being - // replinished so it's possible to cause a buffer underflow during - // shutdown if the buffer is read during this time. - if (m_closedown_count!=-1 || m_data_buffer->readFrames(n_events, (char *)(data + 8))) { - float ticks_per_frame = m_SyncSource->m_data_buffer->getRate(); - -#if TESTTONE - // FIXME: remove this hacked in 1 kHz test signal to - // analog-1 when testing is complete. Note that the tone is - // *never* added during closedown. - if (m_closedown_count<0) { - signed int int_tpf = (int)ticks_per_frame; - unsigned char *sample = data+8+16; - for (i=0; i= 24576000) { - a_cx -= 24576000; - } - } - } -#endif - // Increment the dbc (data block count). This is only done if the - // packet will contain events - that is, we are due to send some - // data. Otherwise a pad packet is sent which contains the DBC of - // the previously sent packet. This regime also means that the very - // first packet containing data will have a DBC of n_events, which - // matches what is observed from other systems. - m_tx_dbc += n_events; - if (m_tx_dbc > 0xff) - m_tx_dbc -= 0x100; - - // construct the packet CIP-like header. Even if this is a data-less - // packet the dbs field is still set as if there were data blocks - // present. For data-less packets the dbc is the same as the previously - // transmitted block. - *quadlet = htonl(0x00000400 | ((getNodeId()&0x3f)<<24) | m_tx_dbc | (dbs<<16)); - quadlet++; - *quadlet = htonl(0x8222ffff); - quadlet++; - - *length = n_events*m_event_size + 8; - - // Zero out data if we're in closedown or startup - if (m_closedown_count>=0 || m_startup_count>=0) { - memset(data+8,0,n_events*m_event_size); - } - - // Account for this packet's frames during startup / closedown. Note: - // * m_startup_count: -1 = not in startup delay, >-1 = in startup delay. - // * m_closedown_count: -1 = not in closedown mode, 0 = closedown - // preparation now finished, >0 = closedown preparation in - // progress. - if (m_closedown_count > 0) - m_closedown_count--; - if (m_startup_count >= 0) - m_startup_count--; - - // Set up each frames's SPH. - for (i=0; igetBufferSize()*m_ticks_per_frame); - - m_data_buffer->setTickOffset(offset); - - // reset all non-device specific stuff - // i.e. the iso stream and the associated ports - if (!TransmitStreamProcessor::reset()) { - debugFatal("Could not do base class reset\n"); - return false; - } - - // we should prefill the event buffer - if (!prefill()) { - debugFatal("Could not prefill buffers\n"); - return false; - } - - return true; -} - -bool MotuTransmitStreamProcessor::prepare() { - - debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing...\n"); - - // prepare all non-device specific stuff - // i.e. the iso stream and the associated ports - if (!TransmitStreamProcessor::prepare()) { - debugFatal("Could not prepare base class\n"); - return false; - } - - m_PeriodStat.setName("XMT PERIOD"); - m_PacketStat.setName("XMT PACKET"); - m_WakeupStat.setName("XMT WAKEUP"); - - debugOutput( DEBUG_LEVEL_VERBOSE, "Event size: %d\n", m_event_size); - - // allocate the event buffer - unsigned int ringbuffer_size_frames=m_nb_buffers * m_period; - - // allocate the internal buffer - m_ticks_per_frame = (TICKS_PER_SECOND*1.0) / ((float)m_framerate); - - assert(m_data_buffer); - // Note: terminology is slightly confused here. From the point of view - // of the buffer the event size is the size of a single complete "event" - // in the MOTU datastream, which consists of one sample from each audio - // channel plus a timestamp and other control data. Almost by - // definition then, the buffer's "events per frame" must be 1. With - // these values, data copies to/from the MOTU data stream can be handled - // by the generic copying functions. - m_data_buffer->setBufferSize(ringbuffer_size_frames); - m_data_buffer->setEventSize(m_event_size); - m_data_buffer->setEventsPerFrame(1); - - m_data_buffer->setUpdatePeriod(m_period); - m_data_buffer->setNominalRate(m_ticks_per_frame); - - // FIXME: check if the timestamp wraps at one second - m_data_buffer->setWrapValue(128L*TICKS_PER_SECOND); - - m_data_buffer->prepare(); - - // Set the parameters of ports we can: we want the audio ports to be - // period buffered, and the midi ports to be packet buffered. - for ( PortVectorIterator it = m_Ports.begin(); - it != m_Ports.end(); - ++it ) { - debugOutput(DEBUG_LEVEL_VERBOSE, "Setting up port %s\n",(*it)->getName().c_str()); - if(!(*it)->setBufferSize(m_period)) { - debugFatal("Could not set buffer size to %d\n",m_period); - return false; - } - - switch ((*it)->getPortType()) { - case Port::E_Audio: - if (!(*it)->setSignalType(Port::E_PeriodSignalled)) { - debugFatal("Could not set signal type to PeriodSignalling"); - return false; - } - break; - - case Port::E_Midi: - if (!(*it)->setSignalType(Port::E_PacketSignalled)) { - debugFatal("Could not set signal type to PacketSignalling"); - return false; - } - if (!(*it)->setBufferType(Port::E_RingBuffer)) { - debugFatal("Could not set buffer type"); - return false; - } - if (!(*it)->setDataType(Port::E_MidiEvent)) { - debugFatal("Could not set data type"); - return false; - } - // FIXME: probably need rate control too. See - // Port::useRateControl() and AmdtpStreamProcessor. - break; - - case Port::E_Control: - if (!(*it)->setSignalType(Port::E_PeriodSignalled)) { - debugFatal("Could not set signal type to PeriodSignalling"); - return false; - } - break; - - default: - debugWarning("Unsupported port type specified\n"); - break; - } - } - - // The API specific settings of the ports are already set before - // this routine is called, therefore we can init&prepare the ports - if (!initPorts()) { - debugFatal("Could not initialize ports!\n"); - return false; - } - - if(!preparePorts()) { - debugFatal("Could not initialize ports!\n"); - return false; - } - - return true; -} - -bool MotuTransmitStreamProcessor::prepareForStop() { - - // If the stream is disabled or isn't running there's no need to - // wait since the MOTU *should* still be in a "zero data" state. - // - // If the m_streaming_active flag is 0 it indicates that the - // transmit callback hasn't been called since a closedown was - // requested when this function was last called. This effectively - // signifies that the streaming thread has been exitted due to an - // xrun in either the receive or transmit handlers. In this case - // there's no point in waiting for the closedown count to hit zero - // because it never will; the zero data will never get to the MOTU. - // It's best to allow an immediate stop and let the xrun handler - // proceed as best it can. - // - // The ability to detect the lack of streaming also prevents the - // "wait for stop" in the stream processor manager's stop() method - // from hitting its timeout which in turn seems to increase the - // probability of a successful recovery. - if (m_is_disabled || !isRunning() || !m_streaming_active) - return true; - - if (m_closedown_count < 0) { - // No closedown has been initiated, so start one now. Set - // the closedown count to the number of zero packets which - // will be sent to the MOTU before closing off the iso - // streams. FIXME: 128 packets (each containing 8 frames at - // 48 kHz) is the experimentally-determined figure for 48 - // kHz with a period size of 1024. It seems that at least - // one period of zero samples need to be sent to allow for - // inter-thread communication occuring on period boundaries. - // This needs to be confirmed for other rates and period - // sizes. - signed n_events = m_framerate<=48000?8:(m_framerate<=96000?16:32); - m_closedown_count = m_period / n_events; - - // Set up a test to confirm that streaming is still active. - // If the streaming function hasn't been called by the next - // iteration through this function there's no point in - // continuing since it means the zero data will never get to - // the MOTU. - m_streaming_active = 0; - return false; - } - - // We are "go" for closedown once all requested zero packets - // (initiated by a previous call to this function) have been sent to - // the MOTU. - return m_closedown_count == 0; -} - -bool MotuTransmitStreamProcessor::prepareForStart() { -// Reset some critical variables required so the stream starts cleanly. This -// method is called once on every stream restart. Initialisations which should -// be done once should be placed in the init() method instead. - m_running = 0; - m_closedown_count = -1; - m_streaming_active = 0; - - // At this point we'll also disable the stream processor here. - // At this stage stream processors are always explicitly re-enabled - // after being started, so by starting in the disabled state we - // ensure that every start will be exactly the same. - disable(); - - return true; -} - -bool MotuTransmitStreamProcessor::prepareForEnable(uint64_t time_to_enable_at) { - - debugOutput(DEBUG_LEVEL_VERBOSE,"Preparing to enable...\n"); - - // for the transmit SP, we have to initialize the - // buffer timestamp to something sane, because this timestamp - // is used when it is SyncSource - - // the time we initialize to will determine the time at which - // the first sample in the buffer will be sent, so we should - // make it at least 'time_to_enable_at' - - uint64_t now=m_handler->getCycleTimer(); - unsigned int now_secs=CYCLE_TIMER_GET_SECS(now); - - // check if a wraparound on the secs will happen between - // now and the time we start - int until_enable=(int)time_to_enable_at - (int)CYCLE_TIMER_GET_CYCLES(now); - - if(until_enable>4000) { - // wraparound on CYCLE_TIMER_GET_CYCLES(now) - // this means that we are late starting up, - // and that the start lies in the previous second - if (now_secs==0) now_secs=127; - else now_secs--; - } else if (until_enable<-4000) { - // wraparound on time_to_enable_at - // this means that we are early and that the start - // point lies in the next second - now_secs++; - if (now_secs>=128) now_secs=0; - } - -//// uint64_t ts_head= now_secs*TICKS_PER_SECOND; -// uint64_t ts_head = time_to_enable_at*TICKS_PER_CYCLE; - uint64_t ts_head= now_secs*TICKS_PER_SECOND; - ts_head+=time_to_enable_at*TICKS_PER_CYCLE; - - // we also add the nb of cycles we transmit in advance - ts_head=addTicks(ts_head, TRANSMIT_ADVANCE_CYCLES*TICKS_PER_CYCLE); - - m_data_buffer->setBufferTailTimestamp(ts_head); - - if (!StreamProcessor::prepareForEnable(time_to_enable_at)) { - debugError("StreamProcessor::prepareForEnable failed\n"); - return false; - } - - return true; -} - -bool MotuTransmitStreamProcessor::transferSilence(unsigned int size) { - bool retval; - - // This function should tranfer 'size' frames of 'silence' to the event buffer - char *dummybuffer=(char *)calloc(size,m_event_size); - - transmitSilenceBlock(dummybuffer, size, 0); - - // add the silence data to the ringbuffer - if(m_data_buffer->writeFrames(size, dummybuffer, 0)) { - retval=true; - } else { - debugWarning("Could not write to event buffer\n"); - retval=false; - } - - free(dummybuffer); - - return retval; -} - -bool MotuTransmitStreamProcessor::putFrames(unsigned int nbframes, int64_t ts) { - m_PeriodStat.mark(m_data_buffer->getBufferFill()); - - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "MotuTransmitStreamProcessor::putFrames(%d, %llu)\n", nbframes, ts); - - // transfer the data -#if 0 -debugOutput(DEBUG_LEVEL_VERBOSE, "1 - timestamp is %d\n", ts); -#endif - m_data_buffer->blockProcessWriteFrames(nbframes, ts); -#if 0 -debugOutput(DEBUG_LEVEL_VERBOSE, " done\n"); -#endif - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " New timestamp: %llu\n", ts); - - return true; -} - -/* - * write received events to the stream ringbuffers. - */ - -bool MotuTransmitStreamProcessor::processWriteBlock(char *data, - unsigned int nevents, unsigned int offset) { - bool no_problem=true; - unsigned int i; - - // FIXME: ensure the MIDI and control streams are all zeroed until - // such time as they are fully implemented. - for (i=0; iisDisabled()) {continue;}; - - //FIXME: make this into a static_cast when not DEBUG? - Port *port=dynamic_cast(*it); - - switch(port->getPortType()) { - - case Port::E_Audio: - if (encodePortToMotuEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { - debugWarning("Could not encode port %s to MBLA events",(*it)->getName().c_str()); - no_problem=false; - } - break; - // midi is a packet based port, don't process - // case MotuPortInfo::E_Midi: - // break; - - default: // ignore - break; - } - } - return no_problem; -} - -int MotuTransmitStreamProcessor::transmitSilenceBlock(char *data, - unsigned int nevents, unsigned int offset) { - // This is the same as the non-silence version, except that is - // doesn't read from the port buffers. - - int problem=0; - - for ( PortVectorIterator it = m_PeriodPorts.begin(); - it != m_PeriodPorts.end(); - ++it ) { - //FIXME: make this into a static_cast when not DEBUG? - Port *port=dynamic_cast(*it); - - switch(port->getPortType()) { - - case Port::E_Audio: - if (encodeSilencePortToMotuEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { - debugWarning("Could not encode port %s to MBLA events",(*it)->getName().c_str()); - problem=1; - } - break; - // midi is a packet based port, don't process - // case MotuPortInfo::E_Midi: - // break; - - default: // ignore - break; - } - } - return problem; -} - -/** - * @brief decode a packet for the packet-based ports - * - * @param data Packet data - * @param nevents number of events in data (including events of other ports & port types) - * @param dbc DataBlockCount value for this packet - * @return true if all successfull - */ -bool MotuTransmitStreamProcessor::encodePacketPorts(quadlet_t *data, unsigned int nevents, - unsigned int dbc) { - bool ok=true; - char byte; - - // Use char here since the target address won't necessarily be - // aligned; use of an unaligned quadlet_t may cause issues on - // certain architectures. Besides, the target for MIDI data going - // directly to the MOTU isn't structured in quadlets anyway; it is a - // sequence of 3 unaligned bytes. - unsigned char *target = NULL; - - for ( PortVectorIterator it = m_PacketPorts.begin(); - it != m_PacketPorts.end(); - ++it ) { - - Port *port=static_cast(*it); - assert(port); // this should not fail!! - - // Currently the only packet type of events for MOTU - // is MIDI in mbla. However in future control data - // might also be sent via "packet" events. - // assert(pinfo->getFormat()==MotuPortInfo::E_Midi); - - // FIXME: MIDI output is completely untested at present. - switch (port->getPortType()) { - case Port::E_Midi: { - MotuMidiPort *mp=static_cast(*it); - - // Send a byte if we can. MOTU MIDI data is - // sent using a 3-byte sequence starting at - // the port's position. For now we'll - // always send in the first event of a - // packet, but this might need refinement - // later. - if (mp->canRead()) { - mp->readEvent(&byte); - target = (unsigned char *)data + mp->getPosition(); - *(target++) = 0x01; - *(target++) = 0x00; - *(target++) = byte; - } - break; - } - default: - debugOutput(DEBUG_LEVEL_VERBOSE, "Unknown packet-type port type %d\n",port->getPortType()); - return ok; - } - } - - return ok; -} - -int MotuTransmitStreamProcessor::encodePortToMotuEvents(MotuAudioPort *p, quadlet_t *data, - unsigned int offset, unsigned int nevents) { -// Encodes nevents worth of data from the given port into the given buffer. The -// format of the buffer is precisely that which will be sent to the MOTU. -// The basic idea: -// iterate over the ports -// * get port buffer address -// * loop over events -// - pick right sample in event based upon PortInfo -// - convert sample from Port format (E_Int24, E_Float, ..) to MOTU -// native format -// -// We include the ability to start the transfer from the given offset within -// the port (expressed in frames) so the 'efficient' transfer method can be -// utilised. - - unsigned int j=0; - - // Use char here since the target address won't necessarily be - // aligned; use of an unaligned quadlet_t may cause issues on certain - // architectures. Besides, the target (data going directly to the MOTU) - // isn't structured in quadlets anyway; it mainly consists of packed - // 24-bit integers. - unsigned char *target; - target = (unsigned char *)data + p->getPosition(); - - switch(p->getDataType()) { - default: - case Port::E_Int24: - { - quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); - - assert(nevents + offset <= p->getBufferSize()); - - // Offset is in frames, but each port is only a single - // channel, so the number of frames is the same as the - // number of quadlets to offset (assuming the port buffer - // uses one quadlet per sample, which is the case currently). - buffer+=offset; - - for(j = 0; j < nevents; j += 1) { // Decode nsamples - *target = (*buffer >> 16) & 0xff; - *(target+1) = (*buffer >> 8) & 0xff; - *(target+2) = (*buffer) & 0xff; - - buffer++; - target+=m_event_size; - } - } - break; - case Port::E_Float: - { - const float multiplier = (float)(0x7FFFFF); - float *buffer=(float *)(p->getBufferAddress()); - - assert(nevents + offset <= p->getBufferSize()); - - buffer+=offset; - - for(j = 0; j < nevents; j += 1) { // decode max nsamples - unsigned int v = (int)(*buffer * multiplier); - *target = (v >> 16) & 0xff; - *(target+1) = (v >> 8) & 0xff; - *(target+2) = v & 0xff; - - buffer++; - target+=m_event_size; - } - } - break; - } - - return 0; -} - -int MotuTransmitStreamProcessor::encodeSilencePortToMotuEvents(MotuAudioPort *p, quadlet_t *data, - unsigned int offset, unsigned int nevents) { - unsigned int j=0; - unsigned char *target = (unsigned char *)data + p->getPosition(); - - switch (p->getDataType()) { - default: - case Port::E_Int24: - case Port::E_Float: - for (j = 0; j < nevents; j++) { - *target = *(target+1) = *(target+2) = 0; - target += m_event_size; - } - break; - } - - return 0; -} - -/* --------------------- RECEIVE ----------------------- */ - -MotuReceiveStreamProcessor::MotuReceiveStreamProcessor(int port, int framerate, - unsigned int event_size) - : ReceiveStreamProcessor(port, framerate), m_event_size(event_size), - m_closedown_active(0) { - -} - -MotuReceiveStreamProcessor::~MotuReceiveStreamProcessor() { - -} - -bool MotuReceiveStreamProcessor::init() { - - // call the parent init - // this has to be done before allocating the buffers, - // because this sets the buffersizes from the processormanager - if(!ReceiveStreamProcessor::init()) { - debugFatal("Could not do base class init (%d)\n",this); - return false; - } - - return true; -} - -enum raw1394_iso_disposition -MotuReceiveStreamProcessor::putPacket(unsigned char *data, unsigned int length, - unsigned char channel, unsigned char tag, unsigned char sy, - unsigned int cycle, unsigned int dropped) { - - enum raw1394_iso_disposition retval=RAW1394_ISO_OK; - // this is needed for the base class getLastCycle() to work. - // this avoids a function call like StreamProcessor::updateLastCycle() - m_last_cycle=cycle; - - // check our enable status - if (!m_disabled && m_is_disabled) { - // this means that we are trying to enable - - // check if we are on or past the enable point - int cycles_past_enable=diffCycles(cycle, m_cycle_to_enable_at); - - if (cycles_past_enable >= 0) { - m_is_disabled=false; - debugOutput(DEBUG_LEVEL_VERBOSE,"Enabling Rx StreamProcessor %p at %d\n", - this, cycle); - - // the previous timestamp is the one we need to start with - // because we're going to update the buffer again this loop - // using writeframes - m_data_buffer->setBufferTailTimestamp(m_last_timestamp); - - } else { - debugOutput(DEBUG_LEVEL_VERY_VERBOSE, - "will enable StreamProcessor %p at %u, now is %d\n", - this, m_cycle_to_enable_at, cycle); - } - } else if (m_disabled && !m_is_disabled) { - // trying to disable - debugOutput(DEBUG_LEVEL_VERBOSE,"disabling StreamProcessor %p at %u\n", this, cycle); - m_is_disabled=true; - } - - // If the packet length is 8 bytes (ie: just a CIP-like header) - // there is no isodata. - if (length > 8) { - // The iso data blocks from the MOTUs comprise a CIP-like - // header followed by a number of events (8 for 1x rates, 16 - // for 2x rates, 32 for 4x rates). - quadlet_t *quadlet = (quadlet_t *)data; - unsigned int dbs = get_bits(ntohl(quadlet[0]), 23, 8); // Size of one event in terms of fdf_size - unsigned int fdf_size = get_bits(ntohl(quadlet[1]), 23, 8) == 0x22 ? 32:0; // Event unit size in bits - - // Don't even attempt to process a packet if it isn't what - // we expect from a MOTU. Yes, an FDF value of 32 bears - // little relationship to the actual data (24 bit integer) - // sent by the MOTU - it's one of those areas where MOTU - // have taken a curious detour around the standards. - if (tag!=1 || fdf_size!=32) { - return RAW1394_ISO_OK; - } - - // put this after the check because event_length can become 0 on invalid packets - unsigned int event_length = (fdf_size * dbs) / 8; // Event size in bytes - unsigned int n_events = (length-8) / event_length; - - //=> store the previous timestamp - m_last_timestamp2=m_last_timestamp; - - // Acquire the timestamp of the last frame in the packet just - // received. Since every frame from the MOTU has its own timestamp - // we can just pick it straight from the packet. - uint32_t last_sph = ntohl(*(quadlet_t *)(data+8+(n_events-1)*event_length)); - m_last_timestamp = sphRecvToFullTicks(last_sph, m_handler->getCycleTimer()); - - // Signal that we're running - if(!m_running && n_events && m_last_timestamp2 && m_last_timestamp) { - debugOutput(DEBUG_LEVEL_VERBOSE,"Receive StreamProcessor %p started running at %d\n", this, cycle); - m_running=true; - } - - //=> don't process the stream samples when it is not enabled. - if(m_is_disabled) { - - // we keep track of the timestamp here - // this makes sure that we will have a somewhat accurate - // estimate as to when a period might be ready. i.e. it will not - // be ready earlier than this timestamp + period time - - // Set the timestamp as if a sample was put into the buffer by - // this present packet. This means we use the timestamp - // corresponding to the last frame which would have been added - // to the buffer this cycle if we weren't disabled - that is, - // m_last_timestamp. - m_data_buffer->setBufferTailTimestamp(m_last_timestamp); - - return RAW1394_ISO_DEFER; - } - - debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "put packet...\n"); - - //=> process the packet - // add the data payload to the ringbuffer - // Note: the last argument to writeFrames is the timestamp of the *last sample* being - // added. - if(m_data_buffer->writeFrames(n_events, (char *)(data+8), m_last_timestamp)) { - retval=RAW1394_ISO_OK; - int dbc = get_bits(ntohl(quadlet[0]), 8, 8); - - // process all ports that should be handled on a per-packet base - // this is MIDI for AMDTP (due to the need of DBC) - if (!decodePacketPorts((quadlet_t *)(data+8), n_events, dbc)) { - debugWarning("Problem decoding Packet Ports\n"); - retval=RAW1394_ISO_DEFER; - } - - } else { - - debugWarning("Receive buffer overrun (cycle %d, FC=%d, PC=%d)\n", - cycle, m_data_buffer->getFrameCounter(), m_handler->getPacketCount()); - - m_xruns++; - - // disable the processing, will be re-enabled when - // the xrun is handled - m_disabled=true; - m_is_disabled=true; - - retval=RAW1394_ISO_DEFER; - } - } - - return retval; -} - -// returns the delay between the actual (real) time of a timestamp as received, -// and the timestamp that is passed on for the same event. This is to cope with -// ISO buffering -int MotuReceiveStreamProcessor::getMinimalSyncDelay() { - unsigned int n_events = m_framerate<=48000?8:(m_framerate<=96000?16:32); - - return (int)(m_handler->getWakeupInterval() * n_events * m_ticks_per_frame); -} - -bool MotuReceiveStreamProcessor::reset() { - - debugOutput( DEBUG_LEVEL_VERBOSE, "Resetting...\n"); - - m_data_buffer->setTickOffset(0); - - // reset all non-device specific stuff - // i.e. the iso stream and the associated ports - if(!ReceiveStreamProcessor::reset()) { - debugFatal("Could not do base class reset\n"); - return false; - } - - return true; -} - -bool MotuReceiveStreamProcessor::prepare() { - - // prepare all non-device specific stuff - // i.e. the iso stream and the associated ports - if(!ReceiveStreamProcessor::prepare()) { - debugFatal("Could not prepare base class\n"); - return false; - } - - debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing...\n"); - - m_PeriodStat.setName("RCV PERIOD"); - m_PacketStat.setName("RCV PACKET"); - m_WakeupStat.setName("RCV WAKEUP"); - - // setup any specific stuff here - // FIXME: m_frame_size would be a better name - debugOutput( DEBUG_LEVEL_VERBOSE, "Event size: %d\n", m_event_size); - - // prepare the framerate estimate - m_ticks_per_frame = (TICKS_PER_SECOND*1.0) / ((float)m_framerate); - - // initialize internal buffer - unsigned int ringbuffer_size_frames=m_nb_buffers * m_period; - - unsigned int events_per_frame = m_framerate<=48000?8:(m_framerate<=96000?16:32); - - assert(m_data_buffer); - m_data_buffer->setBufferSize(ringbuffer_size_frames); - m_data_buffer->setEventSize(m_event_size); - m_data_buffer->setEventsPerFrame(1); - -// JMW: The rx buffer receives a new timestamp once per received frame so I think the -// buffer update period is events_per_frame, not events per period. -// m_data_buffer->setUpdatePeriod(m_period); - m_data_buffer->setUpdatePeriod(events_per_frame); - m_data_buffer->setNominalRate(m_ticks_per_frame); - - m_data_buffer->setWrapValue(128L*TICKS_PER_SECOND); - - m_data_buffer->prepare(); - - // set the parameters of ports we can: - // we want the audio ports to be period buffered, - // and the midi ports to be packet buffered - for ( PortVectorIterator it = m_Ports.begin(); - it != m_Ports.end(); - ++it ) - { - debugOutput(DEBUG_LEVEL_VERBOSE, "Setting up port %s\n",(*it)->getName().c_str()); - - if(!(*it)->setBufferSize(m_period)) { - debugFatal("Could not set buffer size to %d\n",m_period); - return false; - } - - switch ((*it)->getPortType()) { - case Port::E_Audio: - if(!(*it)->setSignalType(Port::E_PeriodSignalled)) { - debugFatal("Could not set signal type to PeriodSignalling"); - return false; - } - break; - case Port::E_Midi: - if(!(*it)->setSignalType(Port::E_PacketSignalled)) { - debugFatal("Could not set signal type to PacketSignalling"); - return false; - } - if (!(*it)->setBufferType(Port::E_RingBuffer)) { - debugFatal("Could not set buffer type"); - return false; - } - if (!(*it)->setDataType(Port::E_MidiEvent)) { - debugFatal("Could not set data type"); - return false; - } - // FIXME: probably need rate control too. See - // Port::useRateControl() and AmdtpStreamProcessor. - break; - case Port::E_Control: - if(!(*it)->setSignalType(Port::E_PeriodSignalled)) { - debugFatal("Could not set signal type to PeriodSignalling"); - return false; - } - break; - default: - debugWarning("Unsupported port type specified\n"); - break; - } - - } - - // The API specific settings of the ports are already set before - // this routine is called, therefore we can init&prepare the ports - if(!initPorts()) { - debugFatal("Could not initialize ports!\n"); - return false; - } - - if(!preparePorts()) { - debugFatal("Could not initialize ports!\n"); - return false; - } - - return true; - -} - - -bool MotuReceiveStreamProcessor::prepareForStop() { - - // A MOTU receive stream can stop at any time. However, signify - // that stopping is in progress because other streams (notably the - // transmit stream) may keep going for some time and cause an - // overflow in the receive buffers. If a closedown is in progress - // the receive handler simply throws all incoming data away so - // no buffer overflow can occur. - m_closedown_active = 1; - return true; -} - -bool MotuReceiveStreamProcessor::prepareForStart() { -// Reset some critical variables required so the stream starts cleanly. This -// method is called once on every stream restart, including those during -// xrun recovery. Initialisations which should be done once should be -// placed in the init() method instead. - m_running = 0; - m_closedown_active = 0; - - // At this point we'll also disable the stream processor here. - // At this stage stream processors are always explicitly re-enabled - // after being started, so by starting in the disabled state we - // ensure that every start will be exactly the same. - disable(); - - return true; -} - -bool MotuReceiveStreamProcessor::getFrames(unsigned int nbframes, int64_t ts) { - - m_PeriodStat.mark(m_data_buffer->getBufferFill()); - - // ask the buffer to process nbframes of frames - // using it's registered client's processReadBlock(), - // which should be ours - m_data_buffer->blockProcessReadFrames(nbframes); - - return true; -} - -/** - * \brief write received events to the port ringbuffers. - */ -bool MotuReceiveStreamProcessor::processReadBlock(char *data, - unsigned int nevents, unsigned int offset) -{ - bool no_problem=true; - for ( PortVectorIterator it = m_PeriodPorts.begin(); - it != m_PeriodPorts.end(); - ++it ) { - if((*it)->isDisabled()) {continue;}; - - //FIXME: make this into a static_cast when not DEBUG? - Port *port=dynamic_cast(*it); - - switch(port->getPortType()) { - - case Port::E_Audio: - if(decodeMotuEventsToPort(static_cast(*it), (quadlet_t *)data, offset, nevents)) { - debugWarning("Could not decode packet MBLA to port %s",(*it)->getName().c_str()); - no_problem=false; - } - break; - // midi is a packet based port, don't process - // case MotuPortInfo::E_Midi: - // break; - - default: // ignore - break; - } - } - return no_problem; -} - -/** - * @brief decode a packet for the packet-based ports - * - * @param data Packet data - * @param nevents number of events in data (including events of other ports & port types) - * @param dbc DataBlockCount value for this packet - * @return true if all successfull - */ -bool MotuReceiveStreamProcessor::decodePacketPorts(quadlet_t *data, unsigned int nevents, - unsigned int dbc) { - bool ok=true; - - // Use char here since the source address won't necessarily be - // aligned; use of an unaligned quadlet_t may cause issues on - // certain architectures. Besides, the source for MIDI data going - // directly to the MOTU isn't structured in quadlets anyway; it is a - // sequence of 3 unaligned bytes. - unsigned char *src = NULL; - - for ( PortVectorIterator it = m_PacketPorts.begin(); - it != m_PacketPorts.end(); - ++it ) { - - Port *port=dynamic_cast(*it); - assert(port); // this should not fail!! - - // Currently the only packet type of events for MOTU - // is MIDI in mbla. However in future control data - // might also be sent via "packet" events, so allow - // for this possible expansion. - - // FIXME: MIDI input is completely untested at present. - switch (port->getPortType()) { - case Port::E_Midi: { - MotuMidiPort *mp=static_cast(*it); - signed int sample; - unsigned int j = 0; - // Get MIDI bytes if present anywhere in the - // packet. MOTU MIDI data is sent using a - // 3-byte sequence starting at the port's - // position. It's thought that there can never - // be more than one MIDI byte per packet, but - // for completeness we'll check the entire packet - // anyway. - src = (unsigned char *)data + mp->getPosition(); - while (j < nevents) { - if (*src==0x01 && *(src+1)==0x00) { - sample = *(src+2); - if (!mp->writeEvent(&sample)) { - debugWarning("MIDI packet port events lost\n"); - ok = false; - } - } - j++; - src += m_event_size; - } - break; - } - default: - debugOutput(DEBUG_LEVEL_VERBOSE, "Unknown packet-type port format %d\n",port->getPortType()); - return ok; - } - } - - return ok; -} - -signed int MotuReceiveStreamProcessor::decodeMotuEventsToPort(MotuAudioPort *p, - quadlet_t *data, unsigned int offset, unsigned int nevents) -{ - unsigned int j=0; - - // Use char here since a port's source address won't necessarily be - // aligned; use of an unaligned quadlet_t may cause issues on - // certain architectures. Besides, the source (data coming directly - // from the MOTU) isn't structured in quadlets anyway; it mainly - // consists of packed 24-bit integers. - - unsigned char *src_data; - src_data = (unsigned char *)data + p->getPosition(); - - switch(p->getDataType()) { - default: - case Port::E_Int24: - { - quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); - - assert(nevents + offset <= p->getBufferSize()); - - // Offset is in frames, but each port is only a single - // channel, so the number of frames is the same as the - // number of quadlets to offset (assuming the port buffer - // uses one quadlet per sample, which is the case currently). - buffer+=offset; - - for(j = 0; j < nevents; j += 1) { // Decode nsamples - *buffer = (*src_data<<16)+(*(src_data+1)<<8)+*(src_data+2); - // Sign-extend highest bit of 24-bit int. - // FIXME: this isn't strictly needed since E_Int24 is a 24-bit, - // but doing so shouldn't break anything and makes the data - // easier to deal with during debugging. - if (*src_data & 0x80) - *buffer |= 0xff000000; - - buffer++; - src_data+=m_event_size; - } - } - break; - case Port::E_Float: - { - const float multiplier = 1.0f / (float)(0x7FFFFF); - float *buffer=(float *)(p->getBufferAddress()); - - assert(nevents + offset <= p->getBufferSize()); - - buffer+=offset; - - for(j = 0; j < nevents; j += 1) { // decode max nsamples - - unsigned int v = (*src_data<<16)+(*(src_data+1)<<8)+*(src_data+2); - - // sign-extend highest bit of 24-bit int - int tmp = (int)(v << 8) / 256; - - *buffer = tmp * multiplier; - - buffer++; - src_data+=m_event_size; - } - } - break; - } - - return 0; -} - -signed int MotuReceiveStreamProcessor::setEventSize(unsigned int size) { - m_event_size = size; - return 0; -} - -unsigned int MotuReceiveStreamProcessor::getEventSize(void) { -// -// Return the size of a single event sent by the MOTU as part of an iso -// data packet in bytes. -// - return m_event_size; -} - -void MotuReceiveStreamProcessor::setVerboseLevel(int l) { - setDebugLevel(l); - ReceiveStreamProcessor::setVerboseLevel(l); -} - -} // end of namespace Streaming Index: /anches/ppalmers-streaming/src/libstreaming/MotuPortInfo.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/MotuPortInfo.h (revision 554) +++ (revision ) @@ -1,85 +1,0 @@ -/* - * Copyright (C) 2005-2007 by Jonathan Woithe - * Copyright (C) 2005-2007 by Pieter Palmers - * - * This file is part of FFADO - * FFADO = Free Firewire (pro-)audio drivers for linux - * - * FFADO is based upon FreeBoB. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software Foundation; - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#ifndef __FFADO_MOTUPORTINFO__ -#define __FFADO_MOTUPORTINFO__ - -#include "../debugmodule/debugmodule.h" -#include - -namespace Streaming { -/*! -\brief Class containing the stream information for a Motu channel - - Contains the information that enables the decoding routine to find - this port's data in the ISO events - -*/ -class MotuPortInfo { - -public: - /** - * Sometimes a channel can have multiple formats, depending on the - * device configuration (e.g. an SPDIF port could be plain audio in 24bit integer - * or AC3 passthrough in IEC compliant frames.) - * - * This kind of enum allows to discriminate these formats when decoding - * If all channels always have the same format, you won't be needing this - */ -// enum E_Formats { -// E_MBLA, // Multibit linear audio -// E_Midi, // MIDI -// }; - - /** - * Initialize Motu portinfo - * should not be called directly, is inherited by motu ports - * - * the position parameter is an example - * the name parameter is mandatory - * - * @param position Start position of port's data in iso event - * @param format Format of data in iso event - * @param size Size in bits of port's data in iso event - * @return - */ - MotuPortInfo( int position, int size) - : m_position(position), m_size(size) - {}; - virtual ~MotuPortInfo() {}; - - - int getPosition() {return m_position;}; - int getSize() {return m_size;}; - -protected: - - int m_position; - int m_size; - -}; - -} // end of namespace Streaming - -#endif /* __FFADO_MOTUPORTINFO__ */ Index: /anches/ppalmers-streaming/src/libstreaming/AmdtpPort.cpp =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/AmdtpPort.cpp (revision 445) +++ (revision ) @@ -1,29 +1,0 @@ -/* - * Copyright (C) 2005-2007 by Pieter Palmers - * - * This file is part of FFADO - * FFADO = Free Firewire (pro-)audio drivers for linux - * - * FFADO is based upon FreeBoB. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software Foundation; - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include "AmdtpPort.h" -#include - -namespace Streaming { - -} // end of namespace Streaming Index: /anches/ppalmers-streaming/src/libstreaming/AmdtpPortInfo.h =================================================================== --- /branches/ppalmers-streaming/src/libstreaming/AmdtpPortInfo.h (revision 554) +++ (revision ) @@ -1,75 +1,0 @@ -/* - * Copyright (C) 2005-2007 by Pieter Palmers - * - * This file is part of FFADO - * FFADO = Free Firewire (pro-)audio drivers for linux - * - * FFADO is based upon FreeBoB. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1, as published by the Free Software Foundation; - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#ifndef __FFADO_AMDTPPORTINFO__ -#define __FFADO_AMDTPPORTINFO__ - -#include "../debugmodule/debugmodule.h" -#include - -namespace Streaming { -/*! -\brief Class containing the stream information for an AMDTP channel - - Contains the information that maps the port to an AMDTP stream position (i.e. channel) - this allows the AMDTP stream demultiplexer to find the channel associated - to this port. - -*/ -class AmdtpPortInfo { - -public: - /** - * Sometimes a channel can have multiple formats, depending on the - * device configuration (e.g. an SPDIF port could be plain audio in 24bit integer - * or AC3 passthrough in IEC compliant frames.) - * - * This kind of enum allows to discriminate these formats when decoding - * If all channels always have the same format, you won't be needing this - */ - enum E_Formats { - E_MBLA, ///< multibit linear audio - E_Midi, ///< midi - E_SPDIF,///< IEC.... format - }; - - AmdtpPortInfo( int position, int location, enum E_Formats format) - : m_position(position), m_location(location), m_format(format) - {}; - virtual ~AmdtpPortInfo() {}; - - - int getLocation() {return m_location;}; - int getPosition() {return m_position;}; - enum E_Formats getFormat() {return m_format;}; - -protected: - int m_position; - int m_location; - enum E_Formats m_format; - -}; - -} // end of namespace Streaming - -#endif /* __FFADO_AMDTPPORTINFO__ */