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: hxcodec.c,v $
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 static void *hres;
00142 
00143 typedef struct _MP3PLAYERINFO {
00144     HMP3Decoder mpi_mp3dec;
00145     MP3FrameInfo mpi_frameinfo;
00146 } MP3PLAYERINFO;
00147 
00148 static MP3PLAYERINFO mpi;
00149 
00150 static short pi_pcmbuf[MAX_NCHAN * MAX_NGRAN * MAX_NSAMP];
00151 
00152 static int first_frame;
00153 static int samprate;
00154 
00164 static int HelixPlayerSetVolume(NUTDEVICE *dev, int left, int right)
00165 {
00166     HXDCB *dcb = (HXDCB *)dev->dev_dcb;
00167 
00168     /* Honor limits. */
00169     left = left > AUDIO_DAC_MAX_GAIN ? AUDIO_DAC_MAX_GAIN : left;
00170     left = left < AUDIO_DAC_MIN_GAIN ? AUDIO_DAC_MIN_GAIN : left;
00171     right = right > AUDIO_DAC_MAX_GAIN ? AUDIO_DAC_MAX_GAIN : right;
00172     right = right < AUDIO_DAC_MIN_GAIN ? AUDIO_DAC_MIN_GAIN : right;
00173 
00174     Tlv320DacSetVolume(left, right);
00175 
00176     dcb->dcb_lvol = left;
00177     dcb->dcb_rvol = right;
00178 
00179     return 0;
00180 }
00181 
00182 static int DecodeFrame(uint8_t *buf, int len)
00183 {
00184     int rc = len;
00185     int skip;
00186 
00187     while (len > 2 * MAINBUF_SIZE) {
00188         if ((skip = MP3FindSyncWord(buf, len)) < 0) {
00189             return -1;
00190         }
00191         if (skip) {
00192             len -= skip;
00193             buf += skip;
00194         } else {
00195             if (MP3Decode(mpi.mpi_mp3dec, &buf, &len, pi_pcmbuf, 0)) {
00196                 return -1;
00197             }
00198             if (first_frame) {
00199                 MP3GetLastFrameInfo(mpi.mpi_mp3dec, &mpi.mpi_frameinfo);
00200                 if (mpi.mpi_frameinfo.nChans == 1) {
00201                     samprate = mpi.mpi_frameinfo.samprate / 2;
00202                 }
00203                 else if (mpi.mpi_frameinfo.nChans == 2) {
00204                     samprate = mpi.mpi_frameinfo.samprate;
00205                 }
00206                 else {
00207                     return -1;
00208                 }
00209                 if (mpi.mpi_frameinfo.samprate < 8000 || samprate > DAC_OUTPUT_RATE) {
00210                     return -1;
00211                 }
00212                 if (mpi.mpi_frameinfo.bitsPerSample != 16) {
00213                     return -1;
00214                 }
00215 #ifdef HXCODEC0_RESAMPLER
00216                 /* If needed, initialize resampler. */
00217                 if (samprate != DAC_OUTPUT_RATE) {
00218                     if ((hres = RAInitResamplerHermite(samprate, DAC_OUTPUT_RATE, mpi.mpi_frameinfo.nChans)) == NULL) {
00219                         return -1;
00220                     }
00221                     rs_maxout = RAGetMaxOutputHermite(mpi.mpi_frameinfo.outputSamps, hres);
00222                     if ((rs_pcmbuf = malloc(rs_maxout * 2)) == NULL) {
00223                         return -1;
00224                     }
00225                 }
00226 #endif
00227                 first_frame = 0;
00228             }
00229             if (Tlv320DacWrite(pi_pcmbuf, mpi.mpi_frameinfo.outputSamps)) {
00230                 return -1;
00231             }
00232             break;
00233         }
00234     }
00235     return rc - len;
00236 }
00237 
00238 /*
00239  * Initialize the stream output buffer with a given size.
00240  *
00241  * \param dev Specifies the audio codec device.
00242  */
00243 static int HelixPlayerBufferInit(NUTDEVICE *dev, uint32_t size)
00244 {
00245     HXDCB *dcb = (HXDCB *)dev->dev_dcb;
00246 
00247     /* Make sure that the decoder is idle. */
00248     if (dcb->dcb_pbstat != CODEC_STATUS_IDLE) {
00249         return -1;
00250     }
00251     /* Set new buffer size. */
00252     if (NutSegBufInit((size_t)size) == NULL) {
00253         return -1;
00254     }
00255     /* Set watermark defaults. */
00256     dcb->dcb_pbwlo = NutSegBufAvailable() / 3;
00257     dcb->dcb_pbwhi = dcb->dcb_pbwlo * 2;
00258 
00259     return 0;
00260 }
00261 
00262 /*
00263  * VLSI decoder feeding thread.
00264  */
00265 THREAD(FeederThread, arg)
00266 {
00267     uint8_t *bp;
00268     uint8_t *backbuf = NULL;
00269     int backcnt = 0;
00270     size_t avail;
00271     int filled;
00272     NUTDEVICE *dev = (NUTDEVICE *)arg;
00273     HXDCB *dcb = (HXDCB *)dev->dev_dcb;
00274 
00275     /* We are a high priority thread. */
00276     NutThreadSetPriority(7);
00277     for (;;) {
00278         /* 
00279         ** Idle mode processing. 
00280         */
00281         if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00282             /* Wait for a request or a buffer update. */
00283             NutEventWait(&dcb->dcb_feedme, NUT_WAIT_INFINITE);
00284             /* Check if we should change to play mode. This will happen
00285             ** if we receive a play request or if the buffer contents
00286             ** reaches the high watermark. */
00287             if ((dcb->dcb_scmd & HXREQ_PLAY) != 0 || NutSegBufUsed() >=  dcb->dcb_pbwhi) {
00288                 dcb->dcb_scmd &= ~(HXREQ_PLAY | HXREQ_CANCEL);
00289                 dcb->dcb_pbstat = CODEC_STATUS_PLAYING;
00290                 HelixPlayerSetVolume(dev, dcb->dcb_lvol, dcb->dcb_rvol);
00291             }
00292         }
00293 
00294         /* 
00295         ** Play mode processing. 
00296         */
00297         if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
00298             /* Process cancel requests. */
00299             if (dcb->dcb_scmd & HXREQ_CANCEL) {
00300                 dcb->dcb_scmd &= ~HXREQ_CANCEL;
00301                 NutSegBufReset();
00302             }
00303             if (dcb->dcb_scmd & HXREQ_VOLUPD) {
00304                 HelixPlayerSetVolume(dev, dcb->dcb_lvol, dcb->dcb_rvol);
00305                 dcb->dcb_scmd &= ~HXREQ_VOLUPD;
00306             }
00307             /* Retrieve new data from the input buffer. */
00308             bp = (uint8_t *) NutSegBufReadRequest(&avail);
00309             if (backcnt && backcnt + avail >= 4 * MAINBUF_SIZE) {
00310                 memcpy(&backbuf[backcnt], bp, 4 * MAINBUF_SIZE - backcnt);
00311                 filled = DecodeFrame(backbuf, 4 * MAINBUF_SIZE);
00312                 if (filled < 0) {
00313                     backcnt = 0;
00314                     free(backbuf);
00315                 } else if (filled < backcnt) {
00316                     bp = backbuf;
00317                     backcnt -= filled;
00318                     backbuf = malloc(4 * MAINBUF_SIZE);
00319                     memcpy(backbuf, &bp[filled], backcnt);
00320                     free(bp);
00321                 } else {
00322                     if (filled > backcnt) {
00323                         NutSegBufReadLast(filled - backcnt);
00324                     }
00325                     backcnt = 0;
00326                     free(backbuf);
00327                 }
00328             } else if (avail > 2 * MAINBUF_SIZE) {
00329                 filled = DecodeFrame(bp, avail);
00330                 if (filled < 0) {
00331                     NutSegBufReadLast(1);
00332                 } else if (filled) {
00333                     NutSegBufReadLast(filled);
00334                 }
00335                 NutEventPost(&dcb->dcb_bufque);
00336             }
00337             else if (NutSegBufUsed() == 0) {
00338                 /* Finally we reached idle mode. */
00339                 dcb->dcb_pbstat = CODEC_STATUS_IDLE;
00340                 NutEventPost(&dcb->dcb_bufque);
00341             } else if (avail && backcnt == 0) {
00342                 backcnt = avail;
00343                 backbuf = malloc(4 * MAINBUF_SIZE);
00344                 memcpy(backbuf, bp, backcnt);
00345                 NutSegBufReadLast(backcnt);
00346                 NutEventPost(&dcb->dcb_bufque);
00347             } else {
00348                 /* Wait for a request or a buffer update. */
00349                 NutEventWait(&dcb->dcb_feedme, 1000);
00350             }
00351         }
00352     }
00353 }
00354 
00374 static int HelixIOCtl(NUTDEVICE * dev, int req, void *conf)
00375 {
00376     int rc = 0;
00377     uint32_t *lvp = (uint32_t *) conf;
00378     int *ivp = (int *) conf;
00379     HXDCB *dcb = (HXDCB *)dev->dev_dcb;
00380 
00381     switch (req) {
00382     case AUDIO_PLAY:
00383         if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00384             /* Immediately start playing. */
00385             dcb->dcb_scmd |= HXREQ_PLAY;
00386         }
00387         NutEventPost(&dcb->dcb_feedme);
00388         break;
00389     case AUDIO_CANCEL:
00390         if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
00391             /* Immediately stop playing and discard buffer. */
00392             dcb->dcb_scmd |= HXREQ_CANCEL;
00393         }
00394         NutEventPost(&dcb->dcb_feedme);
00395         break;
00396     case AUDIO_GET_STATUS:
00397         *ivp = dcb->dcb_pbstat;
00398         break;
00399     case AUDIO_GET_PLAYGAIN:
00400         *ivp = dcb->dcb_rvol > dcb->dcb_lvol ? dcb->dcb_rvol : dcb->dcb_lvol;
00401         break;
00402     case AUDIO_SET_PLAYGAIN:
00403         dcb->dcb_lvol = *ivp;
00404         dcb->dcb_rvol = *ivp;
00405         dcb->dcb_scmd |= HXREQ_VOLUPD;
00406         break;
00407     case AUDIO_GET_PBSIZE:
00408         *lvp = NutSegBufAvailable() + NutSegBufUsed();
00409         break;
00410     case AUDIO_SET_PBSIZE:
00411         rc = HelixPlayerBufferInit(dev, *lvp);
00412         break;
00413     case AUDIO_GET_PBLEVEL:
00414         *lvp = NutSegBufUsed();
00415         break;
00416     case AUDIO_GET_PBWLOW:
00417         *lvp = dcb->dcb_pbwlo;
00418         break;
00419     case AUDIO_SET_PBWLOW:
00420         dcb->dcb_pbwlo = *lvp;
00421         break;
00422     case AUDIO_GET_PBWHIGH:
00423         *lvp = dcb->dcb_pbwhi;
00424         break;
00425     case AUDIO_SET_PBWHIGH:
00426         dcb->dcb_pbwhi = *lvp;
00427         break;
00428     case AUDIO_SETWRITETIMEOUT:
00429         dcb->dcb_wtmo = *lvp;
00430         break;
00431     case AUDIO_GETWRITETIMEOUT:
00432         *lvp = dcb->dcb_wtmo;
00433         break;
00434     default:
00435         rc = -1;
00436         break;
00437     }
00438     return rc;
00439 }
00440 
00453 static int HelixPlayerFlush(NUTDEVICE *dev, uint32_t tmo)
00454 {
00455     int rc = 0;
00456     HXDCB *dcb = dev->dev_dcb;
00457 
00458     for (;;) {
00459         /* Keep the player running if the buffer contains data. */
00460         if (NutSegBufUsed()) {
00461             HelixIOCtl(dev, AUDIO_PLAY, NULL);
00462         }
00463         /* No data in buffer. If idle, then we are done. */
00464         else if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00465             /* No data and player is idle. */
00466             break;
00467         }
00468         /* Wait for a new buffer update. */
00469         rc = NutEventWait(&dcb->dcb_bufque, tmo);
00470         if (rc) {
00471             break;
00472         }
00473     }
00474     return rc;
00475 }
00476 
00485 static int HelixWrite(NUTFILE * nfp, CONST void *data, int len)
00486 {
00487     int rc = 0;
00488     uint8_t *bp;
00489     CONST uint8_t *dp;
00490     size_t rbytes;
00491     HXDCB *dcb = nfp->nf_dev->dev_dcb;
00492 
00493     dcb->dcb_wtmo = 3000; //DEBUG
00494     /* Flush buffer if data pointer is a NULL pointer. */
00495     if (data == NULL || len == 0) {
00496         return HelixPlayerFlush(nfp->nf_dev, dcb->dcb_wtmo);
00497     }
00498     dp = data;
00499     while (len) {
00500         bp = (uint8_t *)NutSegBufWriteRequest(&rbytes);
00501         if (rbytes == 0) {
00502             /* No buffer space, wait for change. */
00503             if (NutEventWait(&dcb->dcb_bufque, dcb->dcb_wtmo)) {
00504                 /* Write timeout. */
00505                 break;
00506             }
00507         } else {
00508             if (rbytes > len) {
00509                 rbytes = len;
00510             }
00511             memcpy(bp, dp, rbytes);
00512             NutSegBufWriteLast(rbytes);
00513             NutEventPost(&dcb->dcb_feedme);
00514             len -= rbytes;
00515             rc += rbytes;
00516             dp += rbytes;
00517         }
00518     }
00519     return rc;
00520 }
00521 
00522 #ifdef __HARVARD_ARCH__
00523 
00545 static int HelixWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
00546 {
00547     return -1;
00548 }
00549 #endif
00550 
00556 static NUTFILE *HelixOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00557 {
00558     NUTFILE *nfp;
00559 
00560     if ((mpi.mpi_mp3dec = MP3InitDecoder()) == NULL) {
00561         return NUTFILE_EOF;
00562     }
00563 
00564     first_frame = 1;
00565 
00566     NutSleep(2);
00567 
00568     nfp = malloc(sizeof(NUTFILE));
00569     nfp->nf_next = NULL;
00570     nfp->nf_dev = dev;
00571     nfp->nf_fcb = NULL;
00572 
00573     NutSegBufReset();
00574 
00575     return nfp;
00576 }
00577 
00581 static int HelixClose(NUTFILE * nfp)
00582 {
00583     HXDCB *dcb = nfp->nf_dev->dev_dcb;
00584     int rc = HelixPlayerFlush(nfp->nf_dev, dcb->dcb_wtmo);
00585 
00586     if (nfp) {
00587         if (mpi.mpi_mp3dec) {
00588             MP3FreeDecoder(mpi.mpi_mp3dec);
00589             mpi.mpi_mp3dec = NULL;
00590         }
00591         free(nfp);
00592     }
00593     return rc;
00594 }
00595 
00596 /*
00597  * Called via dev_init pointer when the device is registered.
00598  */
00599 static int HelixInit(NUTDEVICE * dev)
00600 {
00601     size_t avail;
00602 
00603     Tlv320DacInit(DAC_OUTPUT_RATE);
00604 
00605     dcb.dcb_rvol = -32;
00606     dcb.dcb_lvol = -32;
00607     Tlv320DacSetVolume(dcb.dcb_lvol, dcb.dcb_rvol);
00608 
00609     /*
00610     ** Initialize the decoder stream buffer.
00611     */
00612 #ifdef HXCODEC0_OUTPUT_BUFSIZ
00613     avail = HXCODEC0_OUTPUT_BUFSIZ;
00614 #else
00615     avail = NutHeapAvailable() / 2;
00616     if (avail > HXCODEC0_MAX_OUTPUT_BUFSIZ) {
00617         avail = HXCODEC0_MAX_OUTPUT_BUFSIZ;
00618     }
00619 #endif
00620     if (HelixPlayerBufferInit(dev, avail)) {
00621         return -1;
00622     }
00623 
00624     /* Start the feeder thread. */
00625     if (NutThreadCreate(dev->dev_name, FeederThread, dev, 
00626         (NUT_THREAD_HXCODEC0STACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD) == 0) {
00627         return -1;
00628     }
00629     return 0;
00630 }
00631 
00642 NUTDEVICE devHelixCodec = {
00643     0,              /* Pointer to next device, dev_next. */
00644     {'a', 'u', 'd', 'i', 'o', '0', 0, 0, 0},    /* Unique device name, dev_name. */
00645     IFTYP_CHAR,     /* Type of device, dev_type. */
00646     0,              /* Base address, dev_base (not used). */
00647     0,              /* First interrupt number, dev_irq (not used). */
00648     0,              /* Interface control block, dev_icb (not used). */
00649     &dcb,           /* Driver control block, dev_dcb. */
00650     HelixInit,         /* Driver initialization routine, dev_init. */
00651     HelixIOCtl,        /* Driver specific control function, dev_ioctl. */
00652     NULL,           /* Read from device, dev_read. */
00653     HelixWrite,        /* Write to device, dev_write. */
00654 #ifdef __HARVARD_ARCH__
00655     HelixWrite_P,      /* Write data from program space to device, dev_write_P. */
00656 #endif
00657     HelixOpen,         /* Open a device or file, dev_open. */
00658     HelixClose,        /* Close a device or file, dev_close. */
00659     NULL            /* Request file size, dev_size. */
00660 };
00661 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/