blob: 58d340cb35fef1ba04ce072163f11a73a641324a [file] [log] [blame]
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -04001/*
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
Derek Sollenberger21f39912009-07-09 09:19:39 -040028#include <fcntl.h>
29#include <sys/stat.h>
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -040030#include <sys/time.h>
31#include <time.h>
32#include <math.h>
33#include <string.h>
34
35extern NPNetscapeFuncs* browser;
36extern ANPLogInterfaceV0 gLogI;
37extern ANPCanvasInterfaceV0 gCanvasI;
38extern ANPPaintInterfaceV0 gPaintI;
39extern ANPAudioTrackInterfaceV0 gSoundI;
40extern ANPTypefaceInterfaceV0 gTypefaceI;
41
42
43static void inval(NPP instance) {
44 browser->invalidaterect(instance, NULL);
45}
46
47static 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
55static 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
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -040067static 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 }
Derek Sollenberger21f39912009-07-09 09:19:39 -040079
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
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -040087 break;
88 }
89 default:
90 break;
91 }
92}
93
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -040094///////////////////////////////////////////////////////////////////////////////
95
96AudioPlugin::AudioPlugin(NPP inst) : SubPlugin(inst) {
97
Derek Sollenberger21f39912009-07-09 09:19:39 -040098 const char path[] = "/sdcard/sample.raw";
99
100 // open a file stream
101 FILE* f = fopen(path, "r");
102 gLogI.log(inst, 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(inst, kError_ANPLogType, "fopen error");
127 }
128 else if (fstat(fileDescriptor, &fileStatus) != 0) {
129 gLogI.log(inst, kDebug_ANPLogType, "File Size: %d", fileStatus.st_size);
130 m_soundPlay->fileSize = fileStatus.st_size;
131 } else {
132 gLogI.log(inst, kError_ANPLogType, "fstat error");
133 }
134
135 // configure the UI elements
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400136 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
Derek Sollenberger21f39912009-07-09 09:19:39 -0400156 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
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400164 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(inst, kError_ANPLogType, "Error selecting input events.");
173 }
174}
175
176AudioPlugin::~AudioPlugin() {
177 gPaintI.deletePaint(m_paintTrack);
178 gPaintI.deletePaint(m_paintRect);
179 gPaintI.deletePaint(m_paintText);
Derek Sollenberger21f39912009-07-09 09:19:39 -0400180 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
187bool AudioPlugin::supportsDrawingModel(ANPDrawingModel model) {
188 return (model == kBitmap_ANPDrawingModel);
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400189}
190
Derek Sollenbergerc0f26572009-07-16 11:38:02 -0400191void 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
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400205void AudioPlugin::draw(ANPCanvas* canvas) {
206 NPP instance = this->inst();
207 PluginObject *obj = (PluginObject*) instance->pdata;
208
Derek Sollenberger21f39912009-07-09 09:19:39 -0400209 gLogI.log(instance, kError_ANPLogType, "Drawing");
210
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400211 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)
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400225 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);
Derek Sollenberger21f39912009-07-09 09:19:39 -0400230
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 }
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400236
237 // draw the play box (under track box)
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400238 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;
Derek Sollenberger21f39912009-07-09 09:19:39 -0400242 gCanvasI.drawRect(canvas, &m_playRect, getPaint(&m_playRect));
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400243 // 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);
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400247
248 // draw the pause box (under track box)
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400249 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;
Derek Sollenberger21f39912009-07-09 09:19:39 -0400253 gCanvasI.drawRect(canvas, &m_pauseRect, getPaint(&m_pauseRect));
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400254 // 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);
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400258
259 // draw the stop box (under track box)
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400260 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;
Derek Sollenberger21f39912009-07-09 09:19:39 -0400264 gCanvasI.drawRect(canvas, &m_stopRect, getPaint(&m_stopRect));
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400265 // 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);
Derek Sollenberger21f39912009-07-09 09:19:39 -0400269}
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400270
Derek Sollenberger21f39912009-07-09 09:19:39 -0400271ANPPaint* AudioPlugin::getPaint(ANPRectF* input) {
272 return (input == m_activeRect) ? m_paintActiveRect : m_paintRect;
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400273}
274
275int16 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:
Derek Sollenbergerc0f26572009-07-16 11:38:02 -0400282 drawPlugin(evt->data.draw.data.bitmap, evt->data.draw.clip);
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400283 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
Derek Sollenberger21f39912009-07-09 09:19:39 -0400293 m_activeTouchRect = validTouch(x,y);
294 if(m_activeTouchRect) {
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400295 m_activeTouch = true;
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400296 return 1;
297 }
298
299 } else if (kUp_ANPTouchAction == evt->data.touch.action && m_activeTouch) {
300 handleTouch(x, y);
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400301 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
Derek Sollenberger21f39912009-07-09 09:19:39 -0400314void AudioPlugin::invalActiveRect() {
315
316}
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400317
318ANPRectF* 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
330void AudioPlugin::handleTouch(int x, int y) {
331 NPP instance = this->inst();
332
Derek Sollenberger21f39912009-07-09 09:19:39 -0400333 // if the track is null then return
334 if (NULL == m_soundPlay->track) {
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400335 gLogI.log(instance, 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);
Derek Sollenberger21f39912009-07-09 09:19:39 -0400342 if (m_activeTouchRect != currentRect)
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400343 return;
344
Derek Sollenberger21f39912009-07-09 09:19:39 -0400345 if (currentRect == &m_playRect) {
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400346
347 gLogI.log(instance, kDebug_ANPLogType, "---- %p starting track (%d)",
Derek Sollenberger21f39912009-07-09 09:19:39 -0400348 m_soundPlay->track, gSoundI.isStopped(m_soundPlay->track));
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400349
Derek Sollenberger21f39912009-07-09 09:19:39 -0400350 if (gSoundI.isStopped(m_soundPlay->track)) {
351 gSoundI.start(m_soundPlay->track);
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400352 }
353 }
Derek Sollenberger21f39912009-07-09 09:19:39 -0400354 else if (currentRect == &m_pauseRect) {
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400355
356 gLogI.log(instance, kDebug_ANPLogType, "---- %p pausing track (%d)",
Derek Sollenberger21f39912009-07-09 09:19:39 -0400357 m_soundPlay->track, gSoundI.isStopped(m_soundPlay->track));
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400358
Derek Sollenberger21f39912009-07-09 09:19:39 -0400359 if (!gSoundI.isStopped(m_soundPlay->track)) {
360 gSoundI.pause(m_soundPlay->track);
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400361 }
362 }
Derek Sollenberger21f39912009-07-09 09:19:39 -0400363 else if (currentRect == &m_stopRect) {
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400364
365 gLogI.log(instance, kDebug_ANPLogType, "---- %p stopping track (%d)",
Derek Sollenberger21f39912009-07-09 09:19:39 -0400366 m_soundPlay->track, gSoundI.isStopped(m_soundPlay->track));
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400367
Derek Sollenberger21f39912009-07-09 09:19:39 -0400368 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);
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400373 }
374 }
Derek Sollenberger21f39912009-07-09 09:19:39 -0400375 else {
376 return;
377 }
378
379 // set the currentRect to be the activeRect
380 m_activeRect = currentRect;
381 inval(instance);
Derek Sollenbergerf42e2f42009-06-26 11:42:46 -0400382}