void LibraryTableModel::setTableModel(int id){
    Q_UNUSED(id);
    QStringList columns;
    columns << "library."+LIBRARYTABLE_ID << "'' as preview";

    QString tableName = "library_view";

    QSqlQuery query(m_pTrackCollection->getDatabase());
    QString queryString = "CREATE TEMPORARY VIEW IF NOT EXISTS "+tableName+" AS "
            "SELECT " + columns.join(", ") +
            " FROM library INNER JOIN track_locations "
            "ON library.location = track_locations.id "
            "WHERE (" + LibraryTableModel::DEFAULT_LIBRARYFILTER + ")";
    query.prepare(queryString);
    if (!query.exec()) {
        LOG_FAILED_QUERY(query);
    }

    QStringList tableColumns;
    tableColumns << LIBRARYTABLE_ID;
    tableColumns << "preview";
    setTable(tableName, LIBRARYTABLE_ID, tableColumns,
             m_pTrackCollection->getTrackSource("default"));

    // BaseSqlTabelModel will setup the header info
    initHeaderData();

    setSearch("");
    setDefaultSort(fieldIndex("artist"), Qt::AscendingOrder);
}
void BaseExternalTrackModel::setTableModel(int id) {
    Q_UNUSED(id);
    QStringList columns;
    columns << "id";
    // TODO(XXX) preview column, needs a temporary view
    setTable(m_trackTable, columns[0], columns,
             m_pTrackCollection->getTrackSource(m_trackSource));
    setDefaultSort(fieldIndex("artist"), Qt::AscendingOrder);
    initHeaderData();
}
void BaseExternalPlaylistModel::setPlaylist(QString playlist_path) {
    QSqlQuery finder_query(m_database);
    finder_query.prepare(QString("SELECT id from %1 where name=:name").arg(m_playlistsTable));
    finder_query.bindValue(":name", playlist_path);

    if (!finder_query.exec()) {
        LOG_FAILED_QUERY(finder_query) << "Error getting id for playlist:" << playlist_path;
        return;
    }

    // TODO(XXX): Why not last-insert id?
    int playlistId = -1;
    while (finder_query.next()) {
        playlistId = finder_query.value(
                         finder_query.record().indexOf("id")).toInt();
    }

    if (playlistId == -1) {
        qDebug() << "ERROR: Could not get the playlist ID for playlist:" << playlist_path;
        return;
    }

    QString playlistViewTable = QString("%1_%2").arg(m_playlistTracksTable,
                                QString::number(playlistId));

    QStringList columns;
    columns << "track_id";
    columns << "position";

    QSqlQuery query(m_database);
    FieldEscaper f(m_database);
    QString queryString = QString(
                              "CREATE TEMPORARY VIEW IF NOT EXISTS %1 AS "
                              "SELECT %2 FROM %3 WHERE playlist_id = %4")
                          .arg(f.escapeString(playlistViewTable),
                               columns.join(","),
                               m_playlistTracksTable,
                               QString::number(playlistId));
    query.prepare(queryString);

    if (!query.exec()) {
        LOG_FAILED_QUERY(query) << "Error creating temporary view for playlist.";
        return;
    }

    setTable(playlistViewTable, columns[0], columns,
             m_pTrackCollection->getTrackSource(m_trackSource));
    setDefaultSort(fieldIndex("position"), Qt::AscendingOrder);
    initHeaderData();
    setSearch("");
}
void PlaylistTableModel::setTableModel(int playlistId) {
    //qDebug() << "PlaylistTableModel::setPlaylist" << playlistId;
    if (m_iPlaylistId == playlistId) {
        qDebug() << "Already focused on playlist " << playlistId;
        return;
    }

    m_iPlaylistId = playlistId;
    QString playlistTableName = "playlist_" + QString::number(m_iPlaylistId);
    QSqlQuery query(m_database);
    FieldEscaper escaper(m_database);

    QStringList columns;
    columns << PLAYLISTTRACKSTABLE_TRACKID + " as " + LIBRARYTABLE_ID
            << PLAYLISTTRACKSTABLE_POSITION
            << PLAYLISTTRACKSTABLE_DATETIMEADDED
            << "'' as preview";

    // We drop files that have been explicitly deleted from mixxx
    // (mixxx_deleted=0) from the view. There was a bug in <= 1.9.0 where
    // removed files were not removed from playlists, so some users will have
    // libraries where this is the case.
    QString queryString = QString("CREATE TEMPORARY VIEW IF NOT EXISTS %1 AS "
                                  "SELECT %2 FROM PlaylistTracks "
                                  "INNER JOIN library ON library.id = PlaylistTracks.track_id "
                                  "WHERE PlaylistTracks.playlist_id = %3")
                          .arg(escaper.escapeString(playlistTableName),
                               columns.join(","),
                               QString::number(playlistId));
    if (!m_showAll) {
        queryString.append(" AND library.mixxx_deleted = 0");
    }
    query.prepare(queryString);
    if (!query.exec()) {
        LOG_FAILED_QUERY(query);
    }

    columns[0] = LIBRARYTABLE_ID;
    columns[3] = "preview";
    setTable(playlistTableName, columns[0], columns,
             m_pTrackCollection->getTrackSource("default"));
    initHeaderData();
    setSearch("");
    setDefaultSort(fieldIndex(PLAYLISTTRACKSTABLE_POSITION), Qt::AscendingOrder);
    setSort(defaultSortColumn(), defaultSortOrder());

    connect(&m_playlistDao, SIGNAL(changed(int)),
            this, SLOT(playlistChanged(int)));
}
Exemple #5
0
void CrateTableModel::setTableModel(int crateId) {
    //qDebug() << "CrateTableModel::setCrate()" << crateId;
    if (crateId == m_iCrateId) {
        qDebug() << "Already focused on crate " << crateId;
        return;
    }
    m_iCrateId = crateId;

    QString tableName = QString("crate_%1").arg(m_iCrateId);
    QSqlQuery query(m_database);
    FieldEscaper escaper(m_database);
    QString filter = "library.mixxx_deleted = 0";
    QStringList columns;
    columns << "crate_tracks."+CRATETRACKSTABLE_TRACKID + " as " + LIBRARYTABLE_ID
            << "'' as preview";

    // We drop files that have been explicitly deleted from mixxx
    // (mixxx_deleted=0) from the view. There was a bug in <= 1.9.0 where
    // removed files were not removed from crates, so some users will have
    // libraries where this is the case.
    QString queryString = QString("CREATE TEMPORARY VIEW IF NOT EXISTS %1 AS "
                                  "SELECT %2 FROM %3 "
                                  "INNER JOIN library ON library.id = %3.%4 "
                                  "WHERE %3.%5 = %6 AND %7")
                          .arg(escaper.escapeString(tableName),
                               columns.join(","),
                               CRATE_TRACKS_TABLE,
                               CRATETRACKSTABLE_TRACKID,
                               CRATETRACKSTABLE_CRATEID,
                               QString::number(crateId),
                               filter);
    query.prepare(queryString);
    if (!query.exec()) {
        LOG_FAILED_QUERY(query);
    }

    columns[0] = LIBRARYTABLE_ID;
    columns[1] = "preview";
    setTable(tableName, columns[0], columns,
             m_pTrackCollection->getTrackSource("default"));
    // BaseSqlTableModel sets up the header names
    initHeaderData();
    setSearch("");
    setDefaultSort(fieldIndex("artist"), Qt::AscendingOrder);
}
Exemple #6
0
void BaseSqlTableModel::setTable(const QString& tableName,
                                 const QString& idColumn,
                                 const QStringList& tableColumns,
                                 QSharedPointer<BaseTrackCache> trackSource) {
    if (sDebug) {
        qDebug() << this << "setTable" << tableName << tableColumns << idColumn;
    }
    m_tableName = tableName;
    m_idColumn = idColumn;
    m_tableColumns = tableColumns;
    m_tableColumnsJoined = tableColumns.join(",");

    if (m_trackSource) {
        disconnect(m_trackSource.data(), SIGNAL(tracksChanged(QSet<TrackId>)),
                   this, SLOT(tracksChanged(QSet<TrackId>)));
    }
    m_trackSource = trackSource;
    if (m_trackSource) {
        // It's important that this not be a direct connection, or else the UI
        // might try to update while a cache operation is in progress, and that
        // will hit the cache again and cause dangerous reentry cycles
        // See https://bugs.launchpad.net/mixxx/+bug/1365708
        // TODO: A better fix is to have cache and trackpointers defer saving
        // and deleting, so those operations only take place at the top of
        // the call stack.
        connect(m_trackSource.data(), SIGNAL(tracksChanged(QSet<TrackId>)),
                this, SLOT(tracksChanged(QSet<TrackId>)), Qt::QueuedConnection);
    }

    // Build a map from the column names to their indices, used by fieldIndex()
    m_tableColumnCache.setColumns(m_tableColumns);

    initHeaderData();

    m_bInitialized = true;
}
Exemple #7
0
void MissingTableModel::setTableModel(int id) {
    Q_UNUSED(id);
    QSqlQuery query(m_database);
    //query.prepare("DROP VIEW " + playlistTableName);
    //query.exec();
    QString tableName("missing_songs");

    QStringList columns;
    columns << "library." + LIBRARYTABLE_ID;

    query.prepare("CREATE TEMPORARY VIEW IF NOT EXISTS " + tableName + " AS "
                  "SELECT "
                  + columns.join(",") +
                  " FROM library "
                  "INNER JOIN track_locations "
                  "ON library.location=track_locations.id "
                  "WHERE " + MissingTableModel::MISSINGFILTER);
    if (!query.exec()) {
        qDebug() << query.executedQuery() << query.lastError();
    }

    //Print out any SQL error, if there was one.
    if (query.lastError().isValid()) {
        qDebug() << __FILE__ << __LINE__ << query.lastError();
    }

    QStringList tableColumns;
    tableColumns << LIBRARYTABLE_ID;
    setTable(tableName, LIBRARYTABLE_ID, tableColumns,
             m_pTrackCollection->getTrackSource("default"));

    initHeaderData();
    setDefaultSort(fieldIndex("artist"), Qt::AscendingOrder);
    setSearch("");

}
Exemple #8
0
/*!
  \brief     Set up SBR decoder phase 1

  \return    Handle
*/
SBRDECODER openSBR (SBR_DECODER_INSTANCE &sbrDecoderInstance, int sampleRate, int samplesPerFrame, int bDownSample, int bApplyQmfLp)
{
  int i, err;
  SBR_CHANNEL *SbrChannel = &sbrDecoderInstance.SbrChannel[0];
  HANDLE_SBR_HEADER_DATA_DEC hHeaderData = &sbrDecoderInstance.sbr_header;
  HANDLE_SBR_CONCEAL_DATA hSbrConcealData = &sbrDecoderInstance.SbrConcealData;

  FLC_sub_start("openSBR");

  FloatFR_Init();  /* Not needed for a DSP implementation */

  FUNC(3);
  initHeaderData( hHeaderData,
                  sampleRate,
                  samplesPerFrame);

  LOOP(1);
  for (i = 0; i < MAXNRSBRCHANNELS; i++) {

    FUNC(5);
    PTR_INIT(1);
    err = createSbrDec (&(SbrChannel[i]),
                        hHeaderData,
                        i,
                        bApplyQmfLp,
                        sampleRate);

    BRANCH(1);
    if (err) {
      FLC_sub_end();
      return NULL;
    }
  } LOOP(1);

  for (i = 0; i < MAXNRQMFCHANNELS; i++) {

    FUNC(4);
    PTR_INIT(1);
    err = createSbrQMF (&(SbrChannel[i]),
                        hHeaderData,
                        i,
                        bDownSample);

    BRANCH(1);
    if (err) {
      FLC_sub_end();
      return NULL;
    }
  }

#ifndef MONO_ONLY
  FUNC(5);
  PTR_INIT(1); INDIRECT(3);
  err = CreatePsDec(&sbrDecoderInstance.ParametricStereoDec,
					&sbrDecoderInstance.sbr_header,
					&sbrDecoderInstance.SbrChannel->SbrDec.LppTrans,
                    SbrChannel[0].SbrDec.SynthesisQmfBank.no_col,
					sbrDecoderInstance.OverlapBuffer);

  BRANCH(1);
  if ( err) {
    FLC_sub_end();
    return NULL;
  }
 #endif /* #ifndef MONO_ONLY */

  PTR_INIT(1);
  hSbrConcealData->Bitstream = &hSbrConcealData->sbr_PrevBitstream;

  MOVE(3);
  hSbrConcealData->FrameOk = 1;
  hSbrConcealData->Bitstream->NrElements     = 1;
  hSbrConcealData->Bitstream->NrElementsCore = 1;

  LOOP(1);
  for (i = 0; i < MAXNRELEMENTS; i++) {

    MOVE(3);
    hSbrConcealData->Bitstream->sbrElement[i].ElementID     = 0;
    hSbrConcealData->Bitstream->sbrElement[i].ExtensionType = SBR_EXTENSION;
    hSbrConcealData->Bitstream->sbrElement[i].Payload       = 0;
  }

  FUNC(2); LOOP(1); PTR_INIT(1); MOVE(1); STORE(sizeof(sbrDecoderInstance.sbr_header.sbr_OverlapBuffer)/2);
  memset(sbrDecoderInstance.sbr_header.sbr_OverlapBuffer, 0, sizeof(sbrDecoderInstance.sbr_header.sbr_OverlapBuffer)/2);

  PTR_INIT(1);
  FLC_sub_end();

  return &sbrDecoderInstance;
}
Exemple #9
0
/*!
  \brief Reset SBR decoder.

  Reset should only be called if SBR has been sucessfully detected by
  an appropriate checkForPayload() function.

  \return Error code.
*/
static
SBR_ERROR sbrDecoder_ResetElement (
        HANDLE_SBRDECODER    self,
        int                  sampleRateIn,
        int                  sampleRateOut,
        int                  samplesPerFrame,
        const MP4_ELEMENT_ID elementID,
        const int            elementIndex,
        const int            overlap
        )
{
  SBR_ERROR sbrError = SBRDEC_OK;
  HANDLE_SBR_HEADER_DATA hSbrHeader;
  UINT qmfFlags = 0;

  int i, synDownsampleFac;

  /* Check in/out samplerates */
  if ( sampleRateIn < 6400
    || sampleRateIn > 48000
     )
  {
    sbrError = SBRDEC_UNSUPPORTED_CONFIG;
    goto bail;
  }

  if ( sampleRateOut > 96000 )
  {
    sbrError = SBRDEC_UNSUPPORTED_CONFIG;
    goto bail;
  }

  /* Set QMF mode flags */
  if (self->flags & SBRDEC_LOW_POWER)
    qmfFlags |= QMF_FLAG_LP;

  if (self->coreCodec == AOT_ER_AAC_ELD) {
    if (self->flags & SBRDEC_LD_MPS_QMF) {
      qmfFlags |=  QMF_FLAG_MPSLDFB;
    } else {
      qmfFlags |=  QMF_FLAG_CLDFB;
    }
  }

  /* Set downsampling factor for synthesis filter bank */
  if (sampleRateOut == 0)
  {
    /* no single rate mode */
      sampleRateOut = sampleRateIn<<1; /* In case of implicit signalling, assume dual rate SBR */
  }

  if ( sampleRateIn == sampleRateOut ) {
    synDownsampleFac = 2;
    self->flags |=  SBRDEC_DOWNSAMPLE;
  } else {
    synDownsampleFac = 1;
    self->flags &= ~SBRDEC_DOWNSAMPLE;
  }

  self->synDownsampleFac = synDownsampleFac;
  self->sampleRateOut = sampleRateOut;

  {
    int i;

    for (i = 0; i < (1)+1; i++)
    {
      hSbrHeader = &(self->sbrHeader[elementIndex][i]);

      /* init a default header such that we can at least do upsampling later */
      sbrError = initHeaderData(
              hSbrHeader,
              sampleRateIn,
              sampleRateOut,
              samplesPerFrame,
              self->flags
              );
    }
  }

  if (sbrError != SBRDEC_OK) {
    goto bail;
  }

  /* Init SBR channels going to be assigned to a SBR element */
  {
    int ch;

    for (ch=0; ch<self->pSbrElement[elementIndex]->nChannels; ch++)
    {
      /* and create sbrDec */
      sbrError = createSbrDec (self->pSbrElement[elementIndex]->pSbrChannel[ch],
                          hSbrHeader,
                         &self->pSbrElement[elementIndex]->transposerSettings,
                          synDownsampleFac,
                          qmfFlags,
                          self->flags,
                          overlap,
                          ch );

      if (sbrError != SBRDEC_OK) {
        goto bail;
      }
    }
  }

  //FDKmemclear(sbr_OverlapBuffer, sizeof(sbr_OverlapBuffer));

  if (self->numSbrElements == 1) {
    switch ( self->coreCodec ) {
    case AOT_AAC_LC:
    case AOT_SBR:
    case AOT_PS:
    case AOT_ER_AAC_SCAL:
    case AOT_DRM_AAC:
    case AOT_DRM_SURROUND:
      if (CreatePsDec ( &self->hParametricStereoDec, samplesPerFrame )) {
        sbrError = SBRDEC_CREATE_ERROR;
        goto bail;
      }
      break;
    default:
      break;
    }
  }

  /* Init frame delay slot handling */
  self->pSbrElement[elementIndex]->useFrameSlot = 0;
  for (i = 0; i < ((1)+1); i++) {
    self->pSbrElement[elementIndex]->useHeaderSlot[i] = i;
  }

bail:

  return sbrError;
}
Exemple #10
0
/**
 * \brief Render one SBR element into time domain signal.
 * \param self SBR decoder handle
 * \param timeData pointer to output buffer
 * \param interleaved flag indicating interleaved channel output
 * \param channelMapping pointer to UCHAR array where next 2 channel offsets are stored. 
 * \param elementIndex enumerating index of the SBR element to render.
 * \param numInChannels number of channels from core coder (reading stride).
 * \param numOutChannels pointer to a location to return number of output channels.
 * \param psPossible flag indicating if PS is possible or not.
 * \return SBRDEC_OK if successfull, else error code
 */
static SBR_ERROR
sbrDecoder_DecodeElement (
        HANDLE_SBRDECODER    self,
        INT_PCM             *timeData,
        const int            interleaved,
        const UCHAR         *channelMapping,
        const int            elementIndex,
        const int            numInChannels,
        int                 *numOutChannels,
        const int            psPossible
        )
{
  SBR_DECODER_ELEMENT *hSbrElement = self->pSbrElement[elementIndex];
  HANDLE_SBR_CHANNEL    *pSbrChannel = self->pSbrElement[elementIndex]->pSbrChannel;
  HANDLE_SBR_HEADER_DATA hSbrHeader = &self->sbrHeader[elementIndex][hSbrElement->useHeaderSlot[hSbrElement->useFrameSlot]];
  HANDLE_PS_DEC h_ps_d = self->hParametricStereoDec;

  /* get memory for frame data from scratch */
  SBR_FRAME_DATA *hFrameDataLeft  = &hSbrElement->pSbrChannel[0]->frameData[hSbrElement->useFrameSlot];
  SBR_FRAME_DATA *hFrameDataRight = &hSbrElement->pSbrChannel[1]->frameData[hSbrElement->useFrameSlot];

  SBR_ERROR errorStatus = SBRDEC_OK;


  INT  strideIn, strideOut, offset0, offset1;
  INT  codecFrameSize = self->codecFrameSize;

  int  stereo = (hSbrElement->elementID == ID_CPE) ? 1 : 0;
  int  numElementChannels = hSbrElement->nChannels; /* Number of channels of the current SBR element */

  if (self->flags & SBRDEC_FLUSH) {
    /* Move frame pointer to the next slot which is up to be decoded/applied next */
    hSbrElement->useFrameSlot = (hSbrElement->useFrameSlot+1) % (self->numDelayFrames+1);
    /* Update header and frame data pointer because they have already been set */
    hSbrHeader = &self->sbrHeader[elementIndex][hSbrElement->useHeaderSlot[hSbrElement->useFrameSlot]];
    hFrameDataLeft  = &hSbrElement->pSbrChannel[0]->frameData[hSbrElement->useFrameSlot];
    hFrameDataRight = &hSbrElement->pSbrChannel[1]->frameData[hSbrElement->useFrameSlot];
  }

  /* Update the header error flag */
  hSbrHeader->frameErrorFlag = hSbrElement->frameErrorFlag[hSbrElement->useFrameSlot];

  /*
     Prepare filterbank for upsampling if no valid bit stream data is available.
   */
  if ( hSbrHeader->syncState == SBR_NOT_INITIALIZED )
  {
    errorStatus = initHeaderData(
            hSbrHeader,
            self->sampleRateIn,
            self->sampleRateOut,
            codecFrameSize,
            self->flags
            );

    if (errorStatus != SBRDEC_OK) {
      return errorStatus;
    }

    hSbrHeader->syncState = UPSAMPLING;

    errorStatus = sbrDecoder_HeaderUpdate(
            self,
            hSbrHeader,
            HEADER_NOT_PRESENT,
            pSbrChannel,
            hSbrElement->nChannels
            );

    if (errorStatus != SBRDEC_OK) {
      hSbrHeader->syncState = SBR_NOT_INITIALIZED;
      return errorStatus;
    }
  }

  /* reset */
  if (hSbrHeader->status & SBRDEC_HDR_STAT_RESET) {
    int ch;
    for (ch = 0 ; ch < numElementChannels; ch++) {
      SBR_ERROR errorStatusTmp = SBRDEC_OK;

      errorStatusTmp = resetSbrDec (
             &pSbrChannel[ch]->SbrDec,
              hSbrHeader,
             &pSbrChannel[ch]->prevFrameData,
              self->flags & SBRDEC_LOW_POWER,
              self->synDownsampleFac
              );

      if (errorStatusTmp != SBRDEC_OK) {
        errorStatus = errorStatusTmp;
      }
    }
    hSbrHeader->status &= ~SBRDEC_HDR_STAT_RESET;
  }

  /* decoding */
  if ( (hSbrHeader->syncState == SBR_ACTIVE)
    || ((hSbrHeader->syncState == SBR_HEADER) && (hSbrHeader->frameErrorFlag == 0)) )
  {
    errorStatus = SBRDEC_OK;

    decodeSbrData (hSbrHeader,
                   hFrameDataLeft,
                  &pSbrChannel[0]->prevFrameData,
                   (stereo) ? hFrameDataRight : NULL,
                   (stereo) ? &pSbrChannel[1]->prevFrameData : NULL);


    /* Now we have a full parameter set and can do parameter
       based concealment instead of plain upsampling. */
    hSbrHeader->syncState = SBR_ACTIVE;
  }

  /* decode PS data if available */
  if (h_ps_d != NULL && psPossible) {
    int applyPs = 1;

    /* define which frame delay line slot to process */
    h_ps_d->processSlot = hSbrElement->useFrameSlot;

    applyPs = DecodePs(h_ps_d, hSbrHeader->frameErrorFlag);
    self->flags |= (applyPs) ? SBRDEC_PS_DECODED : 0;
  }

  /* Set strides for reading and writing */
  if (interleaved) {
    strideIn = numInChannels;
    if ( psPossible )
      strideOut = (numInChannels < 2) ? 2 : numInChannels;
    else
      strideOut = numInChannels;
    offset0 = channelMapping[0];
    offset1 = channelMapping[1];
  } else {
    strideIn  = 1;
    strideOut = 1;
    offset0 = channelMapping[0]*2*codecFrameSize;
    offset1 = channelMapping[1]*2*codecFrameSize;
  }

  /* use same buffers for left and right channel and apply PS per timeslot */
  /* Process left channel */
//FDKprintf("self->codecFrameSize %d\t%d\n",self->codecFrameSize,self->sampleRateIn);
  sbr_dec (&pSbrChannel[0]->SbrDec,
            timeData + offset0,
            timeData + offset0,
           &pSbrChannel[1]->SbrDec,
            timeData + offset1,
            strideIn,
            strideOut,
            hSbrHeader,
            hFrameDataLeft,
           &pSbrChannel[0]->prevFrameData,
            (hSbrHeader->syncState == SBR_ACTIVE),
            h_ps_d,
            self->flags
          );

  if (stereo) {
    /* Process right channel */
    sbr_dec (&pSbrChannel[1]->SbrDec,
              timeData + offset1,
              timeData + offset1,
              NULL,
              NULL,
              strideIn,
              strideOut,
              hSbrHeader,
              hFrameDataRight,
             &pSbrChannel[1]->prevFrameData,
              (hSbrHeader->syncState == SBR_ACTIVE),
              NULL,
              self->flags
            );
  }

  if (h_ps_d != NULL) {
    /* save PS status for next run */
    h_ps_d->psDecodedPrv = (self->flags & SBRDEC_PS_DECODED) ? 1 : 0 ;
  }

  if ( psPossible 
    )
  {
    FDK_ASSERT(strideOut > 1);
    if ( !(self->flags & SBRDEC_PS_DECODED) ) {
      /* A decoder which is able to decode PS has to produce a stereo output even if no PS data is availble. */
      /* So copy left channel to right channel.                                                              */
      if (interleaved) {
        INT_PCM *ptr;
        INT i;
        FDK_ASSERT(strideOut == 2);

        ptr = timeData;
        for (i = codecFrameSize; i--; ) 
        {
          INT_PCM tmp; /* This temporal variable is required because some compilers can't do *ptr++ = *ptr++ correctly. */
          tmp = *ptr++; *ptr++ = tmp;
          tmp = *ptr++; *ptr++ = tmp;
        }
      } else {
        FDKmemcpy( timeData+2*codecFrameSize, timeData, 2*codecFrameSize*sizeof(INT_PCM) );
      }
    }
    *numOutChannels = 2;  /* Output minimum two channels when PS is enabled. */
  }

  return errorStatus;
}