/* * Copyright 2008, The Android Open Source Project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "AudioPlugin.h" #include <fcntl.h> #include <sys/stat.h> #include <sys/time.h> #include <time.h> #include <math.h> #include <string.h> extern NPNetscapeFuncs* browser; extern ANPLogInterfaceV0 gLogI; extern ANPCanvasInterfaceV0 gCanvasI; extern ANPPaintInterfaceV0 gPaintI; extern ANPAudioTrackInterfaceV0 gSoundI; extern ANPTypefaceInterfaceV0 gTypefaceI; static void inval(NPP instance) { browser->invalidaterect(instance, NULL); } static uint16_t rnd16(float x, int inset) { int ix = (int)roundf(x) + inset; if (ix < 0) { ix = 0; } return static_cast<uint16_t>(ix); } static void inval(NPP instance, const ANPRectF& r, bool doAA) { const int inset = doAA ? -1 : 0; PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata); NPRect inval; inval.left = rnd16(r.left, inset); inval.top = rnd16(r.top, inset); inval.right = rnd16(r.right, -inset); inval.bottom = rnd16(r.bottom, -inset); browser->invalidaterect(instance, &inval); } static void audioCallback(ANPAudioEvent evt, void* user, ANPAudioBuffer* buffer) { switch (evt) { case kMoreData_ANPAudioEvent: { SoundPlay* play = reinterpret_cast<SoundPlay*>(user); size_t amount = fread(buffer->bufferData, 1, buffer->size, play->file); buffer->size = amount; if (amount == 0) { gSoundI.stop(play->track); fclose(play->file); play->file = NULL; // TODO need to notify our main thread to delete the track now } if (play->fileSize > 0) { // TODO we need to properly update the progress value play->progress = 1; inval(play->instance); } break; } default: break; } } /////////////////////////////////////////////////////////////////////////////// AudioPlugin::AudioPlugin(NPP inst) : SubPlugin(inst) { const char path[] = "/sdcard/sample.raw"; // open a file stream FILE* f = fopen(path, "r"); gLogI.log(kDebug_ANPLogType, "--- path %s FILE %p", path, f); // setup our private audio struct's default values m_soundPlay = new SoundPlay; m_soundPlay->instance = inst; m_soundPlay->progress = 0; m_soundPlay->fileSize = 0; m_soundPlay->file = f; m_soundPlay->track = NULL; // create the audio track if (f) { m_soundPlay->track = gSoundI.newTrack(44100, kPCM16Bit_ANPSampleFormat, 2, audioCallback, m_soundPlay); if (!m_soundPlay->track) { fclose(f); m_soundPlay->file = NULL; } } // get the audio file's size int fileDescriptor = open(path, O_RDONLY); struct stat fileStatus; if(fileDescriptor <= 0) { gLogI.log(kError_ANPLogType, "fopen error"); } else if (fstat(fileDescriptor, &fileStatus) != 0) { gLogI.log(kDebug_ANPLogType, "File Size: %d", fileStatus.st_size); m_soundPlay->fileSize = fileStatus.st_size; } else { gLogI.log(kError_ANPLogType, "fstat error"); } // configure the UI elements m_activeTouch = false; memset(&m_trackRect, 0, sizeof(m_trackRect)); memset(&m_playRect, 0, sizeof(m_playRect)); memset(&m_pauseRect, 0, sizeof(m_pauseRect)); memset(&m_stopRect, 0, sizeof(m_stopRect)); m_paintTrack = gPaintI.newPaint(); gPaintI.setFlags(m_paintTrack, gPaintI.getFlags(m_paintTrack) | kAntiAlias_ANPPaintFlag); gPaintI.setColor(m_paintTrack, 0xFFC0C0C0); m_paintRect = gPaintI.newPaint(); gPaintI.setFlags(m_paintRect, gPaintI.getFlags(m_paintRect) | kAntiAlias_ANPPaintFlag); gPaintI.setColor(m_paintRect, 0xFFA8A8A8); m_paintText = gPaintI.newPaint(); gPaintI.setFlags(m_paintText, gPaintI.getFlags(m_paintText) | kAntiAlias_ANPPaintFlag); gPaintI.setColor(m_paintText, 0xFF2F4F4F); gPaintI.setTextSize(m_paintText, 18); m_paintTrackProgress = gPaintI.newPaint(); gPaintI.setFlags(m_paintTrackProgress, gPaintI.getFlags(m_paintTrackProgress) | kAntiAlias_ANPPaintFlag); gPaintI.setColor(m_paintTrackProgress, 0xFF545454); m_paintActiveRect = gPaintI.newPaint(); gPaintI.setFlags(m_paintActiveRect, gPaintI.getFlags(m_paintActiveRect) | kAntiAlias_ANPPaintFlag); gPaintI.setColor(m_paintActiveRect, 0xFF545454); ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle); gPaintI.setTypeface(m_paintText, tf); gTypefaceI.unref(tf); //register for touch events ANPEventFlags flags = kTouch_ANPEventFlag; NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags); if (err != NPERR_NO_ERROR) { gLogI.log(kError_ANPLogType, "Error selecting input events."); } } AudioPlugin::~AudioPlugin() { gPaintI.deletePaint(m_paintTrack); gPaintI.deletePaint(m_paintRect); gPaintI.deletePaint(m_paintText); gPaintI.deletePaint(m_paintTrackProgress); gPaintI.deletePaint(m_paintActiveRect); if(m_soundPlay->track) gSoundI.deleteTrack(m_soundPlay->track); delete m_soundPlay; } bool AudioPlugin::supportsDrawingModel(ANPDrawingModel model) { return (model == kBitmap_ANPDrawingModel); } void AudioPlugin::drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip) { ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap); ANPRectF clipR; clipR.left = clip.left; clipR.top = clip.top; clipR.right = clip.right; clipR.bottom = clip.bottom; gCanvasI.clipRect(canvas, &clipR); draw(canvas); gCanvasI.deleteCanvas(canvas); } void AudioPlugin::draw(ANPCanvas* canvas) { PluginObject *obj = (PluginObject*) this->inst()->pdata; gLogI.log(kError_ANPLogType, "Drawing"); const float trackHeight = 30; const float buttonWidth = 60; const float buttonHeight = 30; const int W = obj->window->width; const int H = obj->window->height; // color the plugin canvas gCanvasI.drawColor(canvas, 0xFFCDCDCD); // get font metrics ANPFontMetrics fontMetrics; gPaintI.getFontMetrics(m_paintText, &fontMetrics); // draw the track box (1 px from the edge) m_trackRect.left = 1; m_trackRect.top = 1; m_trackRect.right = W - 2; m_trackRect.bottom = 1 + trackHeight; gCanvasI.drawRect(canvas, &m_trackRect, m_paintTrack); // draw the progress bar if (m_soundPlay->progress > 0) { // TODO need to draw progress bar to cover the proper percentage of the track bar gCanvasI.drawRect(canvas, &m_trackRect, m_paintTrackProgress); } // draw the play box (under track box) m_playRect.left = m_trackRect.left + 5; m_playRect.top = m_trackRect.bottom + 10; m_playRect.right = m_playRect.left + buttonWidth; m_playRect.bottom = m_playRect.top + buttonHeight; gCanvasI.drawRect(canvas, &m_playRect, getPaint(&m_playRect)); // draw the play box (under track box) const char playText[] = "Play"; gCanvasI.drawText(canvas, playText, sizeof(playText)-1, m_playRect.left + 5, m_playRect.top - fontMetrics.fTop, m_paintText); // draw the pause box (under track box) m_pauseRect.left = m_playRect.right + 20; m_pauseRect.top = m_trackRect.bottom + 10; m_pauseRect.right = m_pauseRect.left + buttonWidth; m_pauseRect.bottom = m_pauseRect.top + buttonHeight; gCanvasI.drawRect(canvas, &m_pauseRect, getPaint(&m_pauseRect)); // draw the text in the pause box const char pauseText[] = "Pause"; gCanvasI.drawText(canvas, pauseText, sizeof(pauseText)-1, m_pauseRect.left + 5, m_pauseRect.top - fontMetrics.fTop, m_paintText); // draw the stop box (under track box) m_stopRect.left = m_pauseRect.right + 20; m_stopRect.top = m_trackRect.bottom + 10; m_stopRect.right = m_stopRect.left + buttonWidth; m_stopRect.bottom = m_stopRect.top + buttonHeight; gCanvasI.drawRect(canvas, &m_stopRect, getPaint(&m_stopRect)); // draw the text in the pause box const char stopText[] = "Stop"; gCanvasI.drawText(canvas, stopText, sizeof(stopText)-1, m_stopRect.left + 5, m_stopRect.top - fontMetrics.fTop, m_paintText); } ANPPaint* AudioPlugin::getPaint(ANPRectF* input) { return (input == m_activeRect) ? m_paintActiveRect : m_paintRect; } int16_t AudioPlugin::handleEvent(const ANPEvent* evt) { NPP instance = this->inst(); switch (evt->eventType) { case kDraw_ANPEventType: switch (evt->data.draw.model) { case kBitmap_ANPDrawingModel: drawPlugin(evt->data.draw.data.bitmap, evt->data.draw.clip); return 1; default: break; // unknown drawing model } case kTouch_ANPEventType: { int x = evt->data.touch.x; int y = evt->data.touch.y; if (kDown_ANPTouchAction == evt->data.touch.action) { m_activeTouchRect = validTouch(x,y); if(m_activeTouchRect) { m_activeTouch = true; return 1; } } else if (kUp_ANPTouchAction == evt->data.touch.action && m_activeTouch) { handleTouch(x, y); m_activeTouch = false; return 1; } else if (kCancel_ANPTouchAction == evt->data.touch.action) { m_activeTouch = false; } break; } default: break; } return 0; // unknown or unhandled event } void AudioPlugin::invalActiveRect() { } ANPRectF* AudioPlugin::validTouch(int x, int y) { if (m_playRect.left && x < m_playRect.right && y > m_playRect.top && y < m_playRect.bottom) return &m_playRect; else if (m_pauseRect.left && x < m_pauseRect.right && y > m_pauseRect.top && y < m_pauseRect.bottom) return &m_pauseRect; else if (x > m_stopRect.left && x < m_stopRect.right && y > m_stopRect.top && y < m_stopRect.bottom) return &m_stopRect; else return NULL; } void AudioPlugin::handleTouch(int x, int y) { NPP instance = this->inst(); // if the track is null then return if (NULL == m_soundPlay->track) { gLogI.log(kError_ANPLogType, "---- %p unable to create track", instance); return; } // check to make sure the currentRect matches the activeRect ANPRectF* currentRect = validTouch(x,y); if (m_activeTouchRect != currentRect) return; if (currentRect == &m_playRect) { gLogI.log(kDebug_ANPLogType, "---- %p starting track (%d)", m_soundPlay->track, gSoundI.isStopped(m_soundPlay->track)); if (gSoundI.isStopped(m_soundPlay->track)) { gSoundI.start(m_soundPlay->track); } } else if (currentRect == &m_pauseRect) { gLogI.log(kDebug_ANPLogType, "---- %p pausing track (%d)", m_soundPlay->track, gSoundI.isStopped(m_soundPlay->track)); if (!gSoundI.isStopped(m_soundPlay->track)) { gSoundI.pause(m_soundPlay->track); } } else if (currentRect == &m_stopRect) { gLogI.log(kDebug_ANPLogType, "---- %p stopping track (%d)", m_soundPlay->track, gSoundI.isStopped(m_soundPlay->track)); if (!gSoundI.isStopped(m_soundPlay->track)) { gSoundI.stop(m_soundPlay->track); } if (m_soundPlay->file) { fseek(m_soundPlay->file, 0, SEEK_SET); } } else { return; } // set the currentRect to be the activeRect m_activeRect = currentRect; inval(instance); }