blob: 872f9795ba94ebb9ab226f4936fff10cfba0bd45 [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
28#include <stdio.h>
29#include <sys/time.h>
30#include <time.h>
31#include <math.h>
32#include <string.h>
33
34extern NPNetscapeFuncs* browser;
35extern ANPLogInterfaceV0 gLogI;
36extern ANPCanvasInterfaceV0 gCanvasI;
37extern ANPPaintInterfaceV0 gPaintI;
38extern ANPAudioTrackInterfaceV0 gSoundI;
39extern ANPTypefaceInterfaceV0 gTypefaceI;
40
41
42static void inval(NPP instance) {
43 browser->invalidaterect(instance, NULL);
44}
45
46static uint16 rnd16(float x, int inset) {
47 int ix = (int)roundf(x) + inset;
48 if (ix < 0) {
49 ix = 0;
50 }
51 return static_cast<uint16>(ix);
52}
53
54static void inval(NPP instance, const ANPRectF& r, bool doAA) {
55 const int inset = doAA ? -1 : 0;
56
57 PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
58 NPRect inval;
59 inval.left = rnd16(r.left, inset);
60 inval.top = rnd16(r.top, inset);
61 inval.right = rnd16(r.right, -inset);
62 inval.bottom = rnd16(r.bottom, -inset);
63 browser->invalidaterect(instance, &inval);
64}
65
66static void drawPlugin(SubPlugin* plugin, const ANPBitmap& bitmap, const ANPRectI& clip) {
67 ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
68
69 ANPRectF clipR;
70 clipR.left = clip.left;
71 clipR.top = clip.top;
72 clipR.right = clip.right;
73 clipR.bottom = clip.bottom;
74 gCanvasI.clipRect(canvas, &clipR);
75
76 plugin->draw(canvas);
77 gCanvasI.deleteCanvas(canvas);
78}
79
80struct SoundPlay {
81 NPP instance;
82 ANPAudioTrack* track;
83 FILE* file;
84};
85
86static void audioCallback(ANPAudioEvent evt, void* user, ANPAudioBuffer* buffer) {
87 switch (evt) {
88 case kMoreData_ANPAudioEvent: {
89 SoundPlay* play = reinterpret_cast<SoundPlay*>(user);
90 size_t amount = fread(buffer->bufferData, 1, buffer->size, play->file);
91 buffer->size = amount;
92 if (amount == 0) {
93 gSoundI.stop(play->track);
94 fclose(play->file);
95 play->file = NULL;
96 // TODO need to notify our main thread to delete the track now
97 }
98 break;
99 }
100 default:
101 break;
102 }
103}
104
105static ANPAudioTrack* createTrack(NPP instance, const char path[]) {
106 FILE* f = fopen(path, "r");
107 gLogI.log(instance, kWarning_ANPLogType, "--- path %s FILE %p", path, f);
108 if (NULL == f) {
109 return NULL;
110 }
111 SoundPlay* play = new SoundPlay;
112 play->file = f;
113 play->track = gSoundI.newTrack(44100, kPCM16Bit_ANPSampleFormat, 2, audioCallback, play);
114 if (NULL == play->track) {
115 fclose(f);
116 delete play;
117 return NULL;
118 }
119 return play->track;
120}
121
122///////////////////////////////////////////////////////////////////////////////
123
124AudioPlugin::AudioPlugin(NPP inst) : SubPlugin(inst) {
125
126 m_track = NULL;
127 m_activeTouch = false;
128
129 memset(&m_trackRect, 0, sizeof(m_trackRect));
130 memset(&m_playRect, 0, sizeof(m_playRect));
131 memset(&m_pauseRect, 0, sizeof(m_pauseRect));
132 memset(&m_stopRect, 0, sizeof(m_stopRect));
133
134 m_paintTrack = gPaintI.newPaint();
135 gPaintI.setFlags(m_paintTrack, gPaintI.getFlags(m_paintTrack) | kAntiAlias_ANPPaintFlag);
136 gPaintI.setColor(m_paintTrack, 0xFFC0C0C0);
137
138 m_paintRect = gPaintI.newPaint();
139 gPaintI.setFlags(m_paintRect, gPaintI.getFlags(m_paintRect) | kAntiAlias_ANPPaintFlag);
140 gPaintI.setColor(m_paintRect, 0xFFA8A8A8);
141
142 m_paintText = gPaintI.newPaint();
143 gPaintI.setFlags(m_paintText, gPaintI.getFlags(m_paintText) | kAntiAlias_ANPPaintFlag);
144 gPaintI.setColor(m_paintText, 0xFF2F4F4F);
145 gPaintI.setTextSize(m_paintText, 18);
146
147 ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
148 gPaintI.setTypeface(m_paintText, tf);
149 gTypefaceI.unref(tf);
150
151 //register for touch events
152 ANPEventFlags flags = kTouch_ANPEventFlag;
153 NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
154 if (err != NPERR_NO_ERROR) {
155 gLogI.log(inst, kError_ANPLogType, "Error selecting input events.");
156 }
157}
158
159AudioPlugin::~AudioPlugin() {
160 gPaintI.deletePaint(m_paintTrack);
161 gPaintI.deletePaint(m_paintRect);
162 gPaintI.deletePaint(m_paintText);
163 if(m_track)
164 gSoundI.deleteTrack(m_track);
165}
166
167void AudioPlugin::draw(ANPCanvas* canvas) {
168 NPP instance = this->inst();
169 PluginObject *obj = (PluginObject*) instance->pdata;
170
171 const float trackHeight = 30;
172 const float buttonWidth = 60;
173 const float buttonHeight = 30;
174 const int W = obj->window->width;
175 const int H = obj->window->height;
176
177 // color the plugin canvas
178 gCanvasI.drawColor(canvas, 0xFFCDCDCD);
179
180 // get font metrics
181 ANPFontMetrics fontMetrics;
182 gPaintI.getFontMetrics(m_paintText, &fontMetrics);
183
184 // draw the track box (1 px from the edge)
185 inval(instance, m_trackRect, true);
186 m_trackRect.left = 1;
187 m_trackRect.top = 1;
188 m_trackRect.right = W - 2;
189 m_trackRect.bottom = 1 + trackHeight;
190 gCanvasI.drawRect(canvas, &m_trackRect, m_paintTrack);
191 inval(instance, m_trackRect, true);
192
193 // draw the play box (under track box)
194 inval(instance, m_playRect, true);
195 m_playRect.left = m_trackRect.left + 5;
196 m_playRect.top = m_trackRect.bottom + 10;
197 m_playRect.right = m_playRect.left + buttonWidth;
198 m_playRect.bottom = m_playRect.top + buttonHeight;
199 gCanvasI.drawRect(canvas, &m_playRect, m_paintRect);
200 // draw the play box (under track box)
201 const char playText[] = "Play";
202 gCanvasI.drawText(canvas, playText, sizeof(playText)-1, m_playRect.left + 5,
203 m_playRect.top - fontMetrics.fTop, m_paintText);
204 inval(instance, m_playRect, true);
205
206 // draw the pause box (under track box)
207 inval(instance, m_pauseRect, true);
208 m_pauseRect.left = m_playRect.right + 20;
209 m_pauseRect.top = m_trackRect.bottom + 10;
210 m_pauseRect.right = m_pauseRect.left + buttonWidth;
211 m_pauseRect.bottom = m_pauseRect.top + buttonHeight;
212 gCanvasI.drawRect(canvas, &m_pauseRect, m_paintRect);
213 // draw the text in the pause box
214 const char pauseText[] = "Pause";
215 gCanvasI.drawText(canvas, pauseText, sizeof(pauseText)-1, m_pauseRect.left + 5,
216 m_pauseRect.top - fontMetrics.fTop, m_paintText);
217 inval(instance, m_pauseRect, true);
218
219 // draw the stop box (under track box)
220 inval(instance, m_stopRect, true);
221 m_stopRect.left = m_pauseRect.right + 20;
222 m_stopRect.top = m_trackRect.bottom + 10;
223 m_stopRect.right = m_stopRect.left + buttonWidth;
224 m_stopRect.bottom = m_stopRect.top + buttonHeight;
225 gCanvasI.drawRect(canvas, &m_stopRect, m_paintRect);
226 // draw the text in the pause box
227 const char stopText[] = "Stop";
228 gCanvasI.drawText(canvas, stopText, sizeof(stopText)-1, m_stopRect.left + 5,
229 m_stopRect.top - fontMetrics.fTop, m_paintText);
230 inval(instance, m_stopRect, true);
231
232}
233
234int16 AudioPlugin::handleEvent(const ANPEvent* evt) {
235 NPP instance = this->inst();
236
237 switch (evt->eventType) {
238 case kDraw_ANPEventType:
239 switch (evt->data.draw.model) {
240 case kBitmap_ANPDrawingModel:
241 drawPlugin(this, evt->data.draw.data.bitmap, evt->data.draw.clip);
242 return 1;
243 default:
244 break; // unknown drawing model
245 }
246
247 case kTouch_ANPEventType: {
248 int x = evt->data.touch.x;
249 int y = evt->data.touch.y;
250 if (kDown_ANPTouchAction == evt->data.touch.action) {
251
252 if (m_activeTouch)
253 invalActiveRect();
254
255 m_activeRect = validTouch(x,y);
256 if(m_activeRect) {
257 m_activeTouch = true;
258 // TODO color the rect
259 return 1;
260 }
261
262 } else if (kUp_ANPTouchAction == evt->data.touch.action && m_activeTouch) {
263 handleTouch(x, y);
264 invalActiveRect();
265 m_activeTouch = false;
266 return 1;
267 } else if (kCancel_ANPTouchAction == evt->data.touch.action) {
268 m_activeTouch = false;
269 }
270 break;
271 }
272 default:
273 break;
274 }
275 return 0; // unknown or unhandled event
276}
277
278void AudioPlugin::invalActiveRect() { }
279
280ANPRectF* AudioPlugin::validTouch(int x, int y) {
281
282 if (m_playRect.left && x < m_playRect.right && y > m_playRect.top && y < m_playRect.bottom)
283 return &m_playRect;
284 else if (m_pauseRect.left && x < m_pauseRect.right && y > m_pauseRect.top && y < m_pauseRect.bottom)
285 return &m_pauseRect;
286 else if (x > m_stopRect.left && x < m_stopRect.right && y > m_stopRect.top && y < m_stopRect.bottom)
287 return &m_stopRect;
288 else
289 return NULL;
290}
291
292void AudioPlugin::handleTouch(int x, int y) {
293 NPP instance = this->inst();
294
295 if (NULL == m_track) {
296 m_track = createTrack(instance, "/sdcard/sample.raw");
297 }
298
299 // if the track is still null then return
300 if(NULL == m_track) {
301 gLogI.log(instance, kError_ANPLogType, "---- %p unable to create track",
302 instance);
303 return;
304 }
305
306 // check to make sure the currentRect matches the activeRect
307 ANPRectF* currentRect = validTouch(x,y);
308 if(m_activeRect != currentRect)
309 return;
310
311 if (m_activeRect == &m_playRect) {
312
313 gLogI.log(instance, kDebug_ANPLogType, "---- %p starting track (%d)",
314 m_track, gSoundI.isStopped(m_track));
315
316 if (gSoundI.isStopped(m_track)) {
317 gSoundI.start(m_track);
318 }
319 }
320 else if (m_activeRect == &m_pauseRect) {
321
322 gLogI.log(instance, kDebug_ANPLogType, "---- %p pausing track (%d)",
323 m_track, gSoundI.isStopped(m_track));
324
325 if (!gSoundI.isStopped(m_track)) {
326 gSoundI.pause(m_track);
327 }
328 }
329 else if (m_activeRect == &m_stopRect) {
330
331 gLogI.log(instance, kDebug_ANPLogType, "---- %p stopping track (%d)",
332 m_track, gSoundI.isStopped(m_track));
333
334 if (!gSoundI.isStopped(m_track)) {
335 gSoundI.stop(m_track);
336 }
337 }
338}