Home | History | Annotate | Download | only in audio
      1 /*
      2  * Copyright 2008, The Android Open Source Project
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *  * Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  *  * Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "AudioPlugin.h"
     27 
     28 #include <fcntl.h>
     29 #include <sys/stat.h>
     30 #include <sys/time.h>
     31 #include <time.h>
     32 #include <math.h>
     33 #include <string.h>
     34 
     35 extern NPNetscapeFuncs*         browser;
     36 extern ANPLogInterfaceV0        gLogI;
     37 extern ANPCanvasInterfaceV0     gCanvasI;
     38 extern ANPPaintInterfaceV0      gPaintI;
     39 extern ANPAudioTrackInterfaceV0 gSoundI;
     40 extern ANPTypefaceInterfaceV0   gTypefaceI;
     41 
     42 
     43 static void inval(NPP instance) {
     44     browser->invalidaterect(instance, NULL);
     45 }
     46 
     47 static uint16 rnd16(float x, int inset) {
     48     int ix = (int)roundf(x) + inset;
     49     if (ix < 0) {
     50         ix = 0;
     51     }
     52     return static_cast<uint16>(ix);
     53 }
     54 
     55 static void inval(NPP instance, const ANPRectF& r, bool doAA) {
     56     const int inset = doAA ? -1 : 0;
     57 
     58     PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
     59     NPRect inval;
     60     inval.left = rnd16(r.left, inset);
     61     inval.top = rnd16(r.top, inset);
     62     inval.right = rnd16(r.right, -inset);
     63     inval.bottom = rnd16(r.bottom, -inset);
     64     browser->invalidaterect(instance, &inval);
     65 }
     66 
     67 static void audioCallback(ANPAudioEvent evt, void* user, ANPAudioBuffer* buffer) {
     68     switch (evt) {
     69         case kMoreData_ANPAudioEvent: {
     70             SoundPlay* play = reinterpret_cast<SoundPlay*>(user);
     71             size_t amount = fread(buffer->bufferData, 1, buffer->size, play->file);
     72             buffer->size = amount;
     73             if (amount == 0) {
     74                 gSoundI.stop(play->track);
     75                 fclose(play->file);
     76                 play->file = NULL;
     77                 // TODO need to notify our main thread to delete the track now
     78             }
     79 
     80             if (play->fileSize > 0) {
     81                 // TODO we need to properly update the progress value
     82                 play->progress = 1;
     83                 inval(play->instance);
     84             }
     85 
     86 
     87             break;
     88         }
     89         default:
     90             break;
     91     }
     92 }
     93 
     94 ///////////////////////////////////////////////////////////////////////////////
     95 
     96 AudioPlugin::AudioPlugin(NPP inst) : SubPlugin(inst) {
     97 
     98     const char path[] = "/sdcard/sample.raw";
     99 
    100     // open a file stream
    101     FILE* f = fopen(path, "r");
    102     gLogI.log(kDebug_ANPLogType, "--- path %s FILE %p", path, f);
    103 
    104     // setup our private audio struct's default values
    105     m_soundPlay = new SoundPlay;
    106     m_soundPlay->instance = inst;
    107     m_soundPlay->progress = 0;
    108     m_soundPlay->fileSize = 0;
    109     m_soundPlay->file = f;
    110     m_soundPlay->track = NULL;
    111 
    112     // create the audio track
    113     if (f) {
    114         m_soundPlay->track = gSoundI.newTrack(44100, kPCM16Bit_ANPSampleFormat, 2, audioCallback, m_soundPlay);
    115         if (!m_soundPlay->track) {
    116             fclose(f);
    117             m_soundPlay->file = NULL;
    118         }
    119     }
    120 
    121     // get the audio file's size
    122     int fileDescriptor = open(path, O_RDONLY);
    123     struct stat fileStatus;
    124 
    125     if(fileDescriptor <= 0) {
    126         gLogI.log(kError_ANPLogType, "fopen error");
    127     }
    128     else if (fstat(fileDescriptor, &fileStatus) != 0) {
    129         gLogI.log(kDebug_ANPLogType, "File Size: %d", fileStatus.st_size);
    130         m_soundPlay->fileSize = fileStatus.st_size;
    131     } else {
    132         gLogI.log(kError_ANPLogType, "fstat error");
    133     }
    134 
    135     // configure the UI elements
    136     m_activeTouch = false;
    137 
    138     memset(&m_trackRect, 0, sizeof(m_trackRect));
    139     memset(&m_playRect,  0, sizeof(m_playRect));
    140     memset(&m_pauseRect, 0, sizeof(m_pauseRect));
    141     memset(&m_stopRect,  0, sizeof(m_stopRect));
    142 
    143     m_paintTrack = gPaintI.newPaint();
    144     gPaintI.setFlags(m_paintTrack, gPaintI.getFlags(m_paintTrack) | kAntiAlias_ANPPaintFlag);
    145     gPaintI.setColor(m_paintTrack, 0xFFC0C0C0);
    146 
    147     m_paintRect = gPaintI.newPaint();
    148     gPaintI.setFlags(m_paintRect, gPaintI.getFlags(m_paintRect) | kAntiAlias_ANPPaintFlag);
    149     gPaintI.setColor(m_paintRect, 0xFFA8A8A8);
    150 
    151     m_paintText = gPaintI.newPaint();
    152     gPaintI.setFlags(m_paintText, gPaintI.getFlags(m_paintText) | kAntiAlias_ANPPaintFlag);
    153     gPaintI.setColor(m_paintText, 0xFF2F4F4F);
    154     gPaintI.setTextSize(m_paintText, 18);
    155 
    156     m_paintTrackProgress = gPaintI.newPaint();
    157     gPaintI.setFlags(m_paintTrackProgress, gPaintI.getFlags(m_paintTrackProgress) | kAntiAlias_ANPPaintFlag);
    158     gPaintI.setColor(m_paintTrackProgress, 0xFF545454);
    159 
    160     m_paintActiveRect = gPaintI.newPaint();
    161     gPaintI.setFlags(m_paintActiveRect, gPaintI.getFlags(m_paintActiveRect) | kAntiAlias_ANPPaintFlag);
    162     gPaintI.setColor(m_paintActiveRect, 0xFF545454);
    163 
    164     ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
    165     gPaintI.setTypeface(m_paintText, tf);
    166     gTypefaceI.unref(tf);
    167 
    168     //register for touch events
    169     ANPEventFlags flags = kTouch_ANPEventFlag;
    170     NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
    171     if (err != NPERR_NO_ERROR) {
    172         gLogI.log(kError_ANPLogType, "Error selecting input events.");
    173     }
    174 }
    175 
    176 AudioPlugin::~AudioPlugin() {
    177     gPaintI.deletePaint(m_paintTrack);
    178     gPaintI.deletePaint(m_paintRect);
    179     gPaintI.deletePaint(m_paintText);
    180     gPaintI.deletePaint(m_paintTrackProgress);
    181     gPaintI.deletePaint(m_paintActiveRect);
    182     if(m_soundPlay->track)
    183         gSoundI.deleteTrack(m_soundPlay->track);
    184     delete m_soundPlay;
    185 }
    186 
    187 bool AudioPlugin::supportsDrawingModel(ANPDrawingModel model) {
    188     return (model == kBitmap_ANPDrawingModel);
    189 }
    190 
    191 void AudioPlugin::drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip) {
    192     ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
    193 
    194     ANPRectF clipR;
    195     clipR.left = clip.left;
    196     clipR.top = clip.top;
    197     clipR.right = clip.right;
    198     clipR.bottom = clip.bottom;
    199     gCanvasI.clipRect(canvas, &clipR);
    200 
    201     draw(canvas);
    202     gCanvasI.deleteCanvas(canvas);
    203 }
    204 
    205 void AudioPlugin::draw(ANPCanvas* canvas) {
    206 
    207     PluginObject *obj = (PluginObject*) this->inst()->pdata;
    208 
    209     gLogI.log(kError_ANPLogType, "Drawing");
    210 
    211     const float trackHeight = 30;
    212     const float buttonWidth = 60;
    213     const float buttonHeight = 30;
    214     const int W = obj->window->width;
    215     const int H = obj->window->height;
    216 
    217     // color the plugin canvas
    218     gCanvasI.drawColor(canvas, 0xFFCDCDCD);
    219 
    220     // get font metrics
    221     ANPFontMetrics fontMetrics;
    222     gPaintI.getFontMetrics(m_paintText, &fontMetrics);
    223 
    224     // draw the track box (1 px from the edge)
    225     m_trackRect.left = 1;
    226     m_trackRect.top = 1;
    227     m_trackRect.right = W - 2;
    228     m_trackRect.bottom = 1 + trackHeight;
    229     gCanvasI.drawRect(canvas, &m_trackRect, m_paintTrack);
    230 
    231     // draw the progress bar
    232     if (m_soundPlay->progress > 0) {
    233         // TODO need to draw progress bar to cover the proper percentage of the track bar
    234         gCanvasI.drawRect(canvas, &m_trackRect, m_paintTrackProgress);
    235     }
    236 
    237     // draw the play box (under track box)
    238     m_playRect.left = m_trackRect.left + 5;
    239     m_playRect.top = m_trackRect.bottom + 10;
    240     m_playRect.right = m_playRect.left + buttonWidth;
    241     m_playRect.bottom = m_playRect.top + buttonHeight;
    242     gCanvasI.drawRect(canvas, &m_playRect, getPaint(&m_playRect));
    243     // draw the play box (under track box)
    244     const char playText[] = "Play";
    245     gCanvasI.drawText(canvas, playText, sizeof(playText)-1, m_playRect.left + 5,
    246                       m_playRect.top - fontMetrics.fTop, m_paintText);
    247 
    248     // draw the pause box (under track box)
    249     m_pauseRect.left = m_playRect.right + 20;
    250     m_pauseRect.top = m_trackRect.bottom + 10;
    251     m_pauseRect.right = m_pauseRect.left + buttonWidth;
    252     m_pauseRect.bottom = m_pauseRect.top + buttonHeight;
    253     gCanvasI.drawRect(canvas, &m_pauseRect, getPaint(&m_pauseRect));
    254     // draw the text in the pause box
    255     const char pauseText[] = "Pause";
    256     gCanvasI.drawText(canvas, pauseText, sizeof(pauseText)-1, m_pauseRect.left + 5,
    257                       m_pauseRect.top - fontMetrics.fTop, m_paintText);
    258 
    259     // draw the stop box (under track box)
    260     m_stopRect.left = m_pauseRect.right + 20;
    261     m_stopRect.top = m_trackRect.bottom + 10;
    262     m_stopRect.right = m_stopRect.left + buttonWidth;
    263     m_stopRect.bottom = m_stopRect.top + buttonHeight;
    264     gCanvasI.drawRect(canvas, &m_stopRect, getPaint(&m_stopRect));
    265     // draw the text in the pause box
    266     const char stopText[] = "Stop";
    267     gCanvasI.drawText(canvas, stopText, sizeof(stopText)-1, m_stopRect.left + 5,
    268                       m_stopRect.top - fontMetrics.fTop, m_paintText);
    269 }
    270 
    271 ANPPaint* AudioPlugin::getPaint(ANPRectF* input) {
    272     return (input == m_activeRect) ? m_paintActiveRect : m_paintRect;
    273 }
    274 
    275 int16 AudioPlugin::handleEvent(const ANPEvent* evt) {
    276     NPP instance = this->inst();
    277 
    278     switch (evt->eventType) {
    279         case kDraw_ANPEventType:
    280             switch (evt->data.draw.model) {
    281                 case kBitmap_ANPDrawingModel:
    282                     drawPlugin(evt->data.draw.data.bitmap, evt->data.draw.clip);
    283                     return 1;
    284                 default:
    285                     break;   // unknown drawing model
    286             }
    287 
    288         case kTouch_ANPEventType: {
    289             int x = evt->data.touch.x;
    290             int y = evt->data.touch.y;
    291             if (kDown_ANPTouchAction == evt->data.touch.action) {
    292 
    293                 m_activeTouchRect = validTouch(x,y);
    294                 if(m_activeTouchRect) {
    295                     m_activeTouch = true;
    296                     return 1;
    297                 }
    298 
    299             } else if (kUp_ANPTouchAction == evt->data.touch.action && m_activeTouch) {
    300                 handleTouch(x, y);
    301                 m_activeTouch = false;
    302                 return 1;
    303             } else if (kCancel_ANPTouchAction == evt->data.touch.action) {
    304                 m_activeTouch = false;
    305             }
    306             break;
    307         }
    308         default:
    309             break;
    310     }
    311     return 0;   // unknown or unhandled event
    312 }
    313 
    314 void AudioPlugin::invalActiveRect() {
    315 
    316 }
    317 
    318 ANPRectF* AudioPlugin::validTouch(int x, int y) {
    319 
    320     if (m_playRect.left && x < m_playRect.right && y > m_playRect.top && y < m_playRect.bottom)
    321         return &m_playRect;
    322     else if (m_pauseRect.left && x < m_pauseRect.right && y > m_pauseRect.top && y < m_pauseRect.bottom)
    323         return &m_pauseRect;
    324     else if (x > m_stopRect.left && x < m_stopRect.right && y > m_stopRect.top && y < m_stopRect.bottom)
    325         return &m_stopRect;
    326     else
    327         return NULL;
    328 }
    329 
    330 void AudioPlugin::handleTouch(int x, int y) {
    331     NPP instance = this->inst();
    332 
    333     // if the track is null then return
    334     if (NULL == m_soundPlay->track) {
    335         gLogI.log(kError_ANPLogType, "---- %p unable to create track",
    336                   instance);
    337         return;
    338     }
    339 
    340     // check to make sure the currentRect matches the activeRect
    341     ANPRectF* currentRect = validTouch(x,y);
    342     if (m_activeTouchRect != currentRect)
    343         return;
    344 
    345     if (currentRect == &m_playRect) {
    346 
    347         gLogI.log(kDebug_ANPLogType, "---- %p starting track (%d)",
    348                   m_soundPlay->track, gSoundI.isStopped(m_soundPlay->track));
    349 
    350         if (gSoundI.isStopped(m_soundPlay->track)) {
    351             gSoundI.start(m_soundPlay->track);
    352         }
    353     }
    354     else if (currentRect == &m_pauseRect) {
    355 
    356         gLogI.log(kDebug_ANPLogType, "---- %p pausing track (%d)",
    357                   m_soundPlay->track, gSoundI.isStopped(m_soundPlay->track));
    358 
    359         if (!gSoundI.isStopped(m_soundPlay->track)) {
    360             gSoundI.pause(m_soundPlay->track);
    361         }
    362     }
    363     else if (currentRect == &m_stopRect) {
    364 
    365         gLogI.log(kDebug_ANPLogType, "---- %p stopping track (%d)",
    366                   m_soundPlay->track, gSoundI.isStopped(m_soundPlay->track));
    367 
    368         if (!gSoundI.isStopped(m_soundPlay->track)) {
    369             gSoundI.stop(m_soundPlay->track);
    370         }
    371         if (m_soundPlay->file) {
    372             fseek(m_soundPlay->file, 0, SEEK_SET);
    373         }
    374     }
    375     else {
    376         return;
    377     }
    378 
    379     // set the currentRect to be the activeRect
    380     m_activeRect = currentRect;
    381     inval(instance);
    382 }
    383