Nut/OS  4.10.3
API Reference
hxcodec.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008-2009 by egnite GmbH.
00003  * Copyright (C) 2001-2007 by egnite Software GmbH.
00004  *
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  *
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  * 3. Neither the name of the copyright holders nor the names of
00017  *    contributors may be used to endorse or promote products derived
00018  *    from this software without specific prior written permission.
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00027  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00028  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00029  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00030  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00031  * SUCH DAMAGE.
00032  *
00033  * For additional information see http://www.ethernut.de/
00034  */
00035 
00036 /*
00037  * $Log$
00038  * Revision 1.4  2009/02/13 14:46:19  haraldkipp
00039  * Using the VS10xx driver as a template for redesign.
00040  *
00041  * Revision 1.3  2009/01/17 11:26:46  haraldkipp
00042  * Getting rid of two remaining BSD types in favor of stdint.
00043  * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
00044  *
00045  * Revision 1.2  2008/10/23 08:54:07  haraldkipp
00046  * Include the correct header file.
00047  *
00048  * Revision 1.1  2008/10/05 16:56:15  haraldkipp
00049  * Added Helix audio device.
00050  *
00051  */
00052 
00053 #include <sys/atom.h>
00054 #include <sys/event.h>
00055 #include <sys/timer.h>
00056 #include <sys/heap.h>
00057 
00058 #include <cfg/arch/gpio.h>
00059 #include <cfg/audio.h>
00060 #include <dev/irqreg.h>
00061 
00062 #include <contrib/hxmp3/mp3dec.h>
00063 #include <contrib/hxmp3/hermite.h>
00064 #include <dev/hxcodec.h>
00065 #include <dev/tlv320dac.h>
00066 
00067 #include <sys/bankmem.h>
00068 
00069 #include <stdlib.h>
00070 #include <stddef.h>
00071 #include <string.h>
00072 #include <memdebug.h>
00073 
00074 #if 0
00075 /* Use for local debugging. */
00076 #define NUTDEBUG
00077 #include <stdio.h>
00078 #endif
00079 
00080 #ifndef NUT_THREAD_HXCODEC0STACK
00081 /* arm-elf-gcc optimized code used 540 bytes. */
00082 #define NUT_THREAD_HXCODEC0STACK    768
00083 #endif
00084 
00085 #ifndef HXCODEC0_MAX_OUTPUT_BUFSIZ
00086 
00087 #define HXCODEC0_MAX_OUTPUT_BUFSIZ  16384
00088 #endif
00089 
00090 #ifndef DAC_OUTPUT_RATE
00091 #if defined (AT91SAM9260_EK)
00092 #define DAC_OUTPUT_RATE     44100
00093 #else
00094 #define DAC_OUTPUT_RATE     8000
00095 #endif
00096 #endif
00097 
00098 #ifndef MP3_BUFSIZ
00099 #if defined (AT91SAM9260_EK)
00100 #define MP3_BUFSIZ          1048576
00101 #else
00102 #define MP3_BUFSIZ          (4 * MAINBUF_SIZE)
00103 #endif
00104 #endif
00105 
00106 #ifdef HXCODEC0_RESAMPLER
00107 static int rs_maxout;
00108 static short *rs_pcmbuf;
00109 #endif
00110 
00114 #define HXREQ_PLAY      0x00000001
00115 
00116 #define HXREQ_CANCEL    0x00000002
00117 
00118 #define HXREQ_VOLUPD    0x00000004
00119 
00120 #define HXREQ_BEEP      0x00000008
00121 
00127 
00128 typedef struct _HXDCB {
00129     int dcb_pbstat;     
00130     uint32_t dcb_scmd;    
00131     int dcb_rvol;           
00132     int dcb_lvol;           
00133     uint32_t dcb_pbwlo;   
00134     uint32_t dcb_pbwhi;   
00135     uint32_t dcb_wtmo;      
00136     HANDLE dcb_bufque;      
00137     HANDLE dcb_feedme;      
00138 } HXDCB;
00139 
00140 static HXDCB dcb;
00141 
00142 typedef struct _MP3PLAYERINFO {
00143     HMP3Decoder mpi_mp3dec;
00144     MP3FrameInfo mpi_frameinfo;
00145 } MP3PLAYERINFO;
00146 
00147 static MP3PLAYERINFO mpi;
00148 
00149 static short pi_pcmbuf[MAX_NCHAN * MAX_NGRAN * MAX_NSAMP];
00150 
00151 static int first_frame;
00152 static int samprate;
00153 
00163 static int HelixPlayerSetVolume(NUTDEVICE *dev, int left, int right)
00164 {
00165     HXDCB *dcb = (HXDCB *)dev->dev_dcb;
00166 
00167     /* Honor limits. */
00168     left = left > AUDIO_DAC_MAX_GAIN ? AUDIO_DAC_MAX_GAIN : left;
00169     left = left < AUDIO_DAC_MIN_GAIN ? AUDIO_DAC_MIN_GAIN : left;
00170     right = right > AUDIO_DAC_MAX_GAIN ? AUDIO_DAC_MAX_GAIN : right;
00171     right = right < AUDIO_DAC_MIN_GAIN ? AUDIO_DAC_MIN_GAIN : right;
00172 
00173     Tlv320DacSetVolume(left, right);
00174 
00175     dcb->dcb_lvol = left;
00176     dcb->dcb_rvol = right;
00177 
00178     return 0;
00179 }
00180 
00181 static int DecodeFrame(uint8_t *buf, int len)
00182 {
00183     int rc = len;
00184     int skip;
00185 
00186     while (len > 2 * MAINBUF_SIZE) {
00187         if ((skip = MP3FindSyncWord(buf, len)) < 0) {
00188                     return -1;
00189             }
00190         if (skip) {
00191             len -= skip;
00192             buf += skip;
00193         } else {
00194             if (MP3Decode(mpi.mpi_mp3dec, &buf, &len, pi_pcmbuf, 0)) {
00195                 return -1;
00196             }
00197             if (first_frame) {
00198                 MP3GetLastFrameInfo(mpi.mpi_mp3dec, &mpi.mpi_frameinfo);
00199                 if (mpi.mpi_frameinfo.nChans == 1) {
00200                     samprate = mpi.mpi_frameinfo.samprate / 2;
00201                 }
00202                 else if (mpi.mpi_frameinfo.nChans == 2) {
00203                     samprate = mpi.mpi_frameinfo.samprate;
00204                 }
00205                 else {
00206                     return -1;
00207                 }
00208                 if (mpi.mpi_frameinfo.samprate < 8000 || samprate > DAC_OUTPUT_RATE) {
00209                     return -1;
00210                 }
00211                 if (mpi.mpi_frameinfo.bitsPerSample != 16) {
00212                     return -1;
00213                 }
00214 #ifdef HXCODEC0_RESAMPLER
00215                 /* If needed, initialize resampler. */
00216                 if (samprate != DAC_OUTPUT_RATE) {
00217                     void *hres;
00218 
00219                     if ((hres = RAInitResamplerHermite(samprate, DAC_OUTPUT_RATE, mpi.mpi_frameinfo.nChans)) == NULL) {
00220                         return -1;
00221                     }
00222                     rs_maxout = RAGetMaxOutputHermite(mpi.mpi_frameinfo.outputSamps, hres);
00223                     if ((rs_pcmbuf = malloc(rs_maxout * 2)) == NULL) {
00224                         return -1;
00225                     }
00226                 }
00227 #endif
00228                 first_frame = 0;
00229             }
00230             if (Tlv320DacWrite(pi_pcmbuf, mpi.mpi_frameinfo.outputSamps)) {
00231                 return -1;
00232             }
00233             break;
00234         }
00235     }
00236     return rc - len;
00237 }
00238 
00239 /*
00240  * Initialize the stream output buffer with a given size.
00241  *
00242  * \param dev Specifies the audio codec device.
00243  */
00244 static int HelixPlayerBufferInit(NUTDEVICE *dev, uint32_t size)
00245 {
00246     HXDCB *dcb = (HXDCB *)dev->dev_dcb;
00247 
00248     /* Make sure that the decoder is idle. */
00249     if (dcb->dcb_pbstat != CODEC_STATUS_IDLE) {
00250         return -1;
00251     }
00252     /* Set new buffer size. */
00253     if (NutSegBufInit((size_t)size) == NULL) {
00254         return -1;
00255     }
00256     /* Set watermark defaults. */
00257     dcb->dcb_pbwlo = NutSegBufAvailable() / 3;
00258     dcb->dcb_pbwhi = dcb->dcb_pbwlo * 2;
00259 
00260     return 0;
00261 }
00262 
00263 /*
00264  * VLSI decoder feeding thread.
00265  */
00266 THREAD(FeederThread, arg)
00267 {
00268     uint8_t *bp;
00269     uint8_t *backbuf = NULL;
00270     int backcnt = 0;
00271     size_t avail;
00272     int filled;
00273     NUTDEVICE *dev = (NUTDEVICE *)arg;
00274     HXDCB *dcb = (HXDCB *)dev->dev_dcb;
00275 
00276     /* We are a high priority thread. */
00277     NutThreadSetPriority(7);
00278     for (;;) {
00279         /* 
00280         ** Idle mode processing. 
00281         */
00282         if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00283             /* Wait for a request or a buffer update. */
00284             NutEventWait(&dcb->dcb_feedme, NUT_WAIT_INFINITE);
00285             /* Check if we should change to play mode. This will happen
00286             ** if we receive a play request or if the buffer contents
00287             ** reaches the high watermark. */
00288             if ((dcb->dcb_scmd & HXREQ_PLAY) != 0 || NutSegBufUsed() >=  dcb->dcb_pbwhi) {
00289                 dcb->dcb_scmd &= ~(HXREQ_PLAY | HXREQ_CANCEL);
00290                 dcb->dcb_pbstat = CODEC_STATUS_PLAYING;
00291                 HelixPlayerSetVolume(dev, dcb->dcb_lvol, dcb->dcb_rvol);
00292             }
00293         }
00294 
00295         /* 
00296         ** Play mode processing. 
00297         */
00298         if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
00299             /* Process cancel requests. */
00300             if (dcb->dcb_scmd & HXREQ_CANCEL) {
00301                 dcb->dcb_scmd &= ~HXREQ_CANCEL;
00302                 NutSegBufReset();
00303             }
00304             if (dcb->dcb_scmd & HXREQ_VOLUPD) {
00305                 HelixPlayerSetVolume(dev, dcb->dcb_lvol, dcb->dcb_rvol);
00306                 dcb->dcb_scmd &= ~HXREQ_VOLUPD;
00307             }
00308             /* Retrieve new data from the input buffer. */
00309             bp = (uint8_t *) NutSegBufReadRequest(&avail);
00310             if (backcnt && backcnt + avail >= 4 * MAINBUF_SIZE) {
00311                 memcpy(&backbuf[backcnt], bp, 4 * MAINBUF_SIZE - backcnt);
00312                 filled = DecodeFrame(backbuf, 4 * MAINBUF_SIZE);
00313                 if (filled < 0) {
00314                     backcnt = 0;
00315                     free(backbuf);
00316                 } else if (filled < backcnt) {
00317                     bp = backbuf;
00318                     backcnt -= filled;
00319                     backbuf = malloc(4 * MAINBUF_SIZE);
00320                     memcpy(backbuf, &bp[filled], backcnt);
00321                     free(bp);
00322                 } else {
00323                     if (filled > backcnt) {
00324                         NutSegBufReadLast(filled - backcnt);
00325                     }
00326                     backcnt = 0;
00327                     free(backbuf);
00328                 }
00329             } else if (avail > 2 * MAINBUF_SIZE) {
00330                 filled = DecodeFrame(bp, avail);
00331                 if (filled < 0) {
00332                     NutSegBufReadLast(1);
00333                 } else if (filled) {
00334                     NutSegBufReadLast(filled);
00335                 }
00336                 NutEventPost(&dcb->dcb_bufque);
00337             }
00338             else if (NutSegBufUsed() == 0) {
00339                 /* Finally we reached idle mode. */
00340                 dcb->dcb_pbstat = CODEC_STATUS_IDLE;
00341                 NutEventPost(&dcb->dcb_bufque);
00342             } else if (avail && backcnt == 0) {
00343                 backcnt = avail;
00344                 backbuf = malloc(4 * MAINBUF_SIZE);
00345                 memcpy(backbuf, bp, backcnt);
00346                 NutSegBufReadLast(backcnt);
00347                 NutEventPost(&dcb->dcb_bufque);
00348             } else {
00349                 /* Wait for a request or a buffer update. */
00350                 NutEventWait(&dcb->dcb_feedme, 1000);
00351             }
00352         }
00353     }
00354 }
00355 
00375 static int HelixIOCtl(NUTDEVICE * dev, int req, void *conf)
00376 {
00377     int rc = 0;
00378     uint32_t *lvp = (uint32_t *) conf;
00379     int *ivp = (int *) conf;
00380     HXDCB *dcb = (HXDCB *)dev->dev_dcb;
00381 
00382     switch (req) {
00383     case AUDIO_PLAY:
00384         if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00385             /* Immediately start playing. */
00386             dcb->dcb_scmd |= HXREQ_PLAY;
00387         }
00388         NutEventPost(&dcb->dcb_feedme);
00389         break;
00390     case AUDIO_CANCEL:
00391         if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
00392             /* Immediately stop playing and discard buffer. */
00393             dcb->dcb_scmd |= HXREQ_CANCEL;
00394         }
00395         NutEventPost(&dcb->dcb_feedme);
00396         break;
00397     case AUDIO_GET_STATUS:
00398         *ivp = dcb->dcb_pbstat;
00399         break;
00400     case AUDIO_GET_PLAYGAIN:
00401         *ivp = dcb->dcb_rvol > dcb->dcb_lvol ? dcb->dcb_rvol : dcb->dcb_lvol;
00402         break;
00403     case AUDIO_SET_PLAYGAIN:
00404         dcb->dcb_lvol = *ivp;
00405         dcb->dcb_rvol = *ivp;
00406         dcb->dcb_scmd |= HXREQ_VOLUPD;
00407         break;
00408     case AUDIO_GET_PBSIZE:
00409         *lvp = NutSegBufAvailable() + NutSegBufUsed();
00410         break;
00411     case AUDIO_SET_PBSIZE:
00412         rc = HelixPlayerBufferInit(dev, *lvp);
00413         break;
00414     case AUDIO_GET_PBLEVEL:
00415         *lvp = NutSegBufUsed();
00416         break;
00417     case AUDIO_GET_PBWLOW:
00418         *lvp = dcb->dcb_pbwlo;
00419         break;
00420     case AUDIO_SET_PBWLOW:
00421         dcb->dcb_pbwlo = *lvp;
00422         break;
00423     case AUDIO_GET_PBWHIGH:
00424         *lvp = dcb->dcb_pbwhi;
00425         break;
00426     case AUDIO_SET_PBWHIGH:
00427         dcb->dcb_pbwhi = *lvp;
00428         break;
00429     case AUDIO_SETWRITETIMEOUT:
00430         dcb->dcb_wtmo = *lvp;
00431         break;
00432     case AUDIO_GETWRITETIMEOUT:
00433         *lvp = dcb->dcb_wtmo;
00434         break;
00435     default:
00436         rc = -1;
00437         break;
00438     }
00439     return rc;
00440 }
00441 
00454 static int HelixPlayerFlush(NUTDEVICE *dev, uint32_t tmo)
00455 {
00456     int rc = 0;
00457     HXDCB *dcb = dev->dev_dcb;
00458 
00459     for (;;) {
00460         /* Keep the player running if the buffer contains data. */
00461         if (NutSegBufUsed()) {
00462             HelixIOCtl(dev, AUDIO_PLAY, NULL);
00463         }
00464         /* No data in buffer. If idle, then we are done. */
00465         else if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00466             /* No data and player is idle. */
00467             break;
00468         }
00469         /* Wait for a new buffer update. */
00470         rc = NutEventWait(&dcb->dcb_bufque, tmo);
00471         if (rc) {
00472             break;
00473         }
00474     }
00475     return rc;
00476 }
00477 
00486 static int HelixWrite(NUTFILE * nfp, CONST void *data, int len)
00487 {
00488     int rc = 0;
00489     uint8_t *bp;
00490     CONST uint8_t *dp;
00491     size_t rbytes;
00492     HXDCB *dcb = nfp->nf_dev->dev_dcb;
00493 
00494     dcb->dcb_wtmo = 3000; //DEBUG
00495     /* Flush buffer if data pointer is a NULL pointer. */
00496     if (data == NULL || len == 0) {
00497         return HelixPlayerFlush(nfp->nf_dev, dcb->dcb_wtmo);
00498     }
00499     dp = data;
00500     while (len) {
00501         bp = (uint8_t *)NutSegBufWriteRequest(&rbytes);
00502         if (rbytes == 0) {
00503             /* No buffer space, wait for change. */
00504             if (NutEventWait(&dcb->dcb_bufque, dcb->dcb_wtmo)) {
00505                 /* Write timeout. */
00506                 break;
00507             }
00508         } else {
00509             if (rbytes > len) {
00510                 rbytes = len;
00511             }
00512             memcpy(bp, dp, rbytes);
00513             NutSegBufWriteLast(rbytes);
00514             NutEventPost(&dcb->dcb_feedme);
00515             len -= rbytes;
00516             rc += rbytes;
00517             dp += rbytes;
00518         }
00519     }
00520     return rc;
00521 }
00522 
00523 #ifdef __HARVARD_ARCH__
00524 
00546 static int HelixWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
00547 {
00548     return -1;
00549 }
00550 #endif
00551 
00557 static NUTFILE *HelixOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00558 {
00559     NUTFILE *nfp;
00560 
00561     if ((mpi.mpi_mp3dec = MP3InitDecoder()) == NULL) {
00562         return NUTFILE_EOF;
00563     }
00564 
00565     first_frame = 1;
00566 
00567     NutSleep(2);
00568 
00569     nfp = malloc(sizeof(NUTFILE));
00570     nfp->nf_next = NULL;
00571     nfp->nf_dev = dev;
00572     nfp->nf_fcb = NULL;
00573 
00574     NutSegBufReset();
00575 
00576     return nfp;
00577 }
00578 
00582 static int HelixClose(NUTFILE * nfp)
00583 {
00584     HXDCB *dcb = nfp->nf_dev->dev_dcb;
00585     int rc = HelixPlayerFlush(nfp->nf_dev, dcb->dcb_wtmo);
00586 
00587     if (nfp) {
00588         if (mpi.mpi_mp3dec) {
00589             MP3FreeDecoder(mpi.mpi_mp3dec);
00590             mpi.mpi_mp3dec = NULL;
00591         }
00592         free(nfp);
00593     }
00594     return rc;
00595 }
00596 
00597 /*
00598  * Called via dev_init pointer when the device is registered.
00599  */
00600 static int HelixInit(NUTDEVICE * dev)
00601 {
00602     size_t avail;
00603 
00604     Tlv320DacInit(DAC_OUTPUT_RATE);
00605 
00606     dcb.dcb_rvol = -32;
00607     dcb.dcb_lvol = -32;
00608     Tlv320DacSetVolume(dcb.dcb_lvol, dcb.dcb_rvol);
00609 
00610     /*
00611     ** Initialize the decoder stream buffer.
00612     */
00613 #ifdef HXCODEC0_OUTPUT_BUFSIZ
00614     avail = HXCODEC0_OUTPUT_BUFSIZ;
00615 #else
00616     avail = NutHeapAvailable() / 2;
00617     if (avail > HXCODEC0_MAX_OUTPUT_BUFSIZ) {
00618         avail = HXCODEC0_MAX_OUTPUT_BUFSIZ;
00619     }
00620 #endif
00621     if (HelixPlayerBufferInit(dev, avail)) {
00622         return -1;
00623     }
00624 
00625     /* Start the feeder thread. */
00626     if (NutThreadCreate(dev->dev_name, FeederThread, dev, 
00627         (NUT_THREAD_HXCODEC0STACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD) == 0) {
00628         return -1;
00629     }
00630     return 0;
00631 }
00632 
00643 NUTDEVICE devHelixCodec = {
00644     0,              /* Pointer to next device, dev_next. */
00645     {'a', 'u', 'd', 'i', 'o', '0', 0, 0, 0},    /* Unique device name, dev_name. */
00646     IFTYP_CHAR,     /* Type of device, dev_type. */
00647     0,              /* Base address, dev_base (not used). */
00648     0,              /* First interrupt number, dev_irq (not used). */
00649     0,              /* Interface control block, dev_icb (not used). */
00650     &dcb,           /* Driver control block, dev_dcb. */
00651     HelixInit,         /* Driver initialization routine, dev_init. */
00652     HelixIOCtl,        /* Driver specific control function, dev_ioctl. */
00653     NULL,           /* Read from device, dev_read. */
00654     HelixWrite,        /* Write to device, dev_write. */
00655 #ifdef __HARVARD_ARCH__
00656     HelixWrite_P,      /* Write data from program space to device, dev_write_P. */
00657 #endif
00658     HelixOpen,         /* Open a device or file, dev_open. */
00659     HelixClose,        /* Close a device or file, dev_close. */
00660     NULL            /* Request file size, dev_size. */
00661 };
00662