root/trunk/libffado/src/libstreaming/motu/MotuTransmitStreamProcessor.cpp

Revision 864, 22.6 kB (checked in by ppalmers, 15 years ago)

update license to GPLv2 or GPLv3 instead of GPLv2 or any later version. Update copyrights to reflect the new year

Line 
1 /*
2  * Copyright (C) 2005-2008 by Jonathan Woithe
3  * Copyright (C) 2005-2008 by Pieter Palmers
4  *
5  * This file is part of FFADO
6  * FFADO = Free Firewire (pro-)audio drivers for linux
7  *
8  * FFADO is based upon FreeBoB.
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 2 of the License, or
13  * (at your option) version 3 of the License.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  *
23  */
24
25 #include "config.h"
26
27 #include "MotuTransmitStreamProcessor.h"
28 #include "MotuPort.h"
29 #include "../StreamProcessorManager.h"
30 #include "devicemanager.h"
31
32 #include "libieee1394/ieee1394service.h"
33 #include "libieee1394/IsoHandlerManager.h"
34 #include "libieee1394/cycletimer.h"
35
36 #include <netinet/in.h>
37 #include <assert.h>
38
39 // Set to 1 to enable the generation of a 1 kHz test tone in analog output 1
40 #define TESTTONE 1
41
42 #if TESTTONE
43 #include <math.h>
44 #endif
45
46 namespace Streaming
47 {
48
49 // A macro to extract specific bits from a native endian quadlet
50 #define get_bits(_d,_start,_len) (((_d)>>((_start)-(_len)+1)) & ((1<<(_len))-1))
51
52 // Convert a full timestamp into an SPH timestamp as required by the MOTU
53 static inline uint32_t fullTicksToSph(int64_t timestamp) {
54     return TICKS_TO_CYCLE_TIMER(timestamp) & 0x1ffffff;
55 }
56
57 /* transmit */
58 MotuTransmitStreamProcessor::MotuTransmitStreamProcessor(FFADODevice &parent, unsigned int event_size )
59         : StreamProcessor(parent, ePT_Transmit )
60         , m_event_size( event_size )
61         , m_tx_dbc( 0 )
62 {}
63
64 unsigned int
65 MotuTransmitStreamProcessor::getMaxPacketSize() {
66     int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate();
67     return framerate<=48000?616:(framerate<=96000?1032:1160);
68 }
69
70 unsigned int
71 MotuTransmitStreamProcessor::getNominalFramesPerPacket() {
72     int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate();
73     return framerate<=48000?8:(framerate<=96000?16:32);
74 }
75
76 enum StreamProcessor::eChildReturnValue
77 MotuTransmitStreamProcessor::generatePacketHeader (
78     unsigned char *data, unsigned int *length,
79     unsigned char *tag, unsigned char *sy,
80     int cycle, unsigned int dropped, unsigned int max_length )
81 {
82     // The number of events per packet expected by the MOTU is solely
83     // dependent on the current sample rate.  An 'event' is one sample from
84     // all channels plus possibly other midi and control data.
85     signed n_events = getNominalFramesPerPacket();
86
87     // Do housekeeping expected for all packets sent to the MOTU, even
88     // for packets containing no audio data.
89     *sy = 0x00;
90     *tag = 1;      // All MOTU packets have a CIP-like header
91     *length = n_events*m_event_size + 8;
92
93     signed int fc;
94     uint64_t presentation_time;
95     unsigned int presentation_cycle;
96     int cycles_until_presentation;
97
98     uint64_t transmit_at_time;
99     unsigned int transmit_at_cycle;
100     int cycles_until_transmit;
101
102     // FIXME: should become a define
103     // the absolute minimum number of cycles we want to transmit
104     // a packet ahead of the presentation time. The nominal time
105     // the packet is transmitted ahead of the presentation time is
106     // given by MOTU_TRANSMIT_TRANSFER_DELAY (in ticks), but in case we
107     // are too late for that, this constant defines how late we can
108     // be.
109     const int min_cycles_before_presentation = 1;
110     // FIXME: should become a define
111     // the absolute maximum number of cycles we want to transmit
112     // a packet ahead of the ideal transmit time. The nominal time
113     // the packet is transmitted ahead of the presentation time is
114     // given by MOTU_TRANSMIT_TRANSFER_DELAY (in ticks), but we can send
115     // packets early if we want to. (not completely according to spec)
116     const int max_cycles_to_transmit_early = 2;
117
118     debugOutput ( DEBUG_LEVEL_ULTRA_VERBOSE, "Try for cycle %d\n", cycle );
119     // check whether the packet buffer has packets for us to send.
120     // the base timestamp is the one of the next sample in the buffer
121     ffado_timestamp_t ts_head_tmp;
122     m_data_buffer->getBufferHeadTimestamp ( &ts_head_tmp, &fc ); // thread safe
123
124     // the timestamp gives us the time at which we want the sample block
125     // to be output by the device
126     presentation_time = ( uint64_t ) ts_head_tmp;
127     m_last_timestamp = presentation_time;
128
129     // now we calculate the time when we have to transmit the sample block
130     transmit_at_time = substractTicks ( presentation_time, MOTU_TRANSMIT_TRANSFER_DELAY );
131
132     // calculate the cycle this block should be presented in
133     // (this is just a virtual calculation since at that time it should
134     //  already be in the device's buffer)
135     presentation_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( presentation_time ) );
136
137     // calculate the cycle this block should be transmitted in
138     transmit_at_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( transmit_at_time ) );
139
140     // we can check whether this cycle is within the 'window' we have
141     // to send this packet.
142     // first calculate the number of cycles left before presentation time
143     cycles_until_presentation = diffCycles ( presentation_cycle, cycle );
144
145     // we can check whether this cycle is within the 'window' we have
146     // to send this packet.
147     // first calculate the number of cycles left before presentation time
148     cycles_until_transmit = diffCycles ( transmit_at_cycle, cycle );
149
150     if (dropped) {
151         debugOutput ( DEBUG_LEVEL_VERBOSE,
152                     "Gen HDR: CY=%04u, TC=%04u, CUT=%04d, TST=%011llu (%04u), TSP=%011llu (%04u)\n",
153                     cycle,
154                     transmit_at_cycle, cycles_until_transmit,
155                     transmit_at_time, ( unsigned int ) TICKS_TO_CYCLES ( transmit_at_time ),
156                     presentation_time, ( unsigned int ) TICKS_TO_CYCLES ( presentation_time ) );
157     }
158     // two different options:
159     // 1) there are not enough frames for one packet
160     //      => determine wether this is a problem, since we might still
161     //         have some time to send it
162     // 2) there are enough packets
163     //      => determine whether we have to send them in this packet
164     if ( fc < ( signed int ) getNominalFramesPerPacket() )
165     {
166         // not enough frames in the buffer,
167
168         // we can still postpone the queueing of the packets
169         // if we are far enough ahead of the presentation time
170         if ( cycles_until_presentation <= min_cycles_before_presentation )
171         {
172             debugOutput ( DEBUG_LEVEL_VERBOSE,
173                         "Insufficient frames (P): N=%02d, CY=%04u, TC=%04u, CUT=%04d\n",
174                         fc, cycle, transmit_at_cycle, cycles_until_transmit );
175             // we are too late
176             return eCRV_XRun;
177         }
178         else
179         {
180             debugOutput ( DEBUG_LEVEL_VERY_VERBOSE,
181                         "Insufficient frames (NP): N=%02d, CY=%04u, TC=%04u, CUT=%04d\n",
182                         fc, cycle, transmit_at_cycle, cycles_until_transmit );
183             // there is still time left to send the packet
184             // we want the system to give this packet another go at a later time instant
185             return eCRV_Again;
186         }
187     }
188     else
189     {
190         // there are enough frames, so check the time they are intended for
191         // all frames have a certain 'time window' in which they can be sent
192         // this corresponds to the range of the timestamp mechanism:
193         // we can send a packet 15 cycles in advance of the 'presentation time'
194         // in theory we can send the packet up till one cycle before the presentation time,
195         // however this is not very smart.
196
197         // There are 3 options:
198         // 1) the frame block is too early
199         //      => send an empty packet
200         // 2) the frame block is within the window
201         //      => send it
202         // 3) the frame block is too late
203         //      => discard (and raise xrun?)
204         //         get next block of frames and repeat
205
206         if(cycles_until_transmit < 0)
207         {
208             // we are too late
209             debugOutput(DEBUG_LEVEL_VERBOSE,
210                         "Too late: CY=%04u, TC=%04u, CUT=%04d, TSP=%011llu (%04u)\n",
211                         cycle,
212                         transmit_at_cycle, cycles_until_transmit,
213                         presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time) );
214
215             // however, if we can send this sufficiently before the presentation
216             // time, it could be harmless.
217             // NOTE: dangerous since the device has no way of reporting that it didn't get
218             //       this packet on time.
219             if(cycles_until_presentation >= min_cycles_before_presentation)
220             {
221                 // we are not that late and can still try to transmit the packet
222                 m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp);
223                 if (m_tx_dbc > 0xff)
224                     m_tx_dbc -= 0x100;
225                 return eCRV_Packet;
226             }
227             else   // definitely too late
228             {
229                 return eCRV_XRun;
230             }
231         }
232         else if(cycles_until_transmit <= max_cycles_to_transmit_early)
233         {
234             // it's time send the packet
235             m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp);
236             if (m_tx_dbc > 0xff)
237                 m_tx_dbc -= 0x100;
238             return eCRV_Packet;
239         }
240         else
241         {
242             debugOutput ( DEBUG_LEVEL_VERY_VERBOSE,
243                         "Too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011llu (%04u), TSP=%011llu (%04u)\n",
244                         cycle,
245                         transmit_at_cycle, cycles_until_transmit,
246                         transmit_at_time, ( unsigned int ) TICKS_TO_CYCLES ( transmit_at_time ),
247                         presentation_time, ( unsigned int ) TICKS_TO_CYCLES ( presentation_time ) );
248 #ifdef DEBUG
249             if ( cycles_until_transmit > max_cycles_to_transmit_early + 1 )
250             {
251                 debugOutput ( DEBUG_LEVEL_VERY_VERBOSE,
252                             "Way too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011llu (%04u), TSP=%011llu (%04u)\n",
253                             cycle,
254                             transmit_at_cycle, cycles_until_transmit,
255                             transmit_at_time, ( unsigned int ) TICKS_TO_CYCLES ( transmit_at_time ),
256                             presentation_time, ( unsigned int ) TICKS_TO_CYCLES ( presentation_time ) );
257             }
258 #endif
259             // we are too early, send only an empty packet
260             return eCRV_EmptyPacket;
261         }
262     }
263     return eCRV_Invalid;
264 }
265
266 enum StreamProcessor::eChildReturnValue
267 MotuTransmitStreamProcessor::generatePacketData (
268     unsigned char *data, unsigned int *length,
269     unsigned char *tag, unsigned char *sy,
270     int cycle, unsigned int dropped, unsigned int max_length )
271 {
272     quadlet_t *quadlet = (quadlet_t *)data;
273     quadlet += 2; // skip the header
274     // Size of a single data frame in quadlets
275     unsigned dbs = m_event_size / 4;
276
277     // The number of events per packet expected by the MOTU is solely
278     // dependent on the current sample rate.  An 'event' is one sample from
279     // all channels plus possibly other midi and control data.
280     signed n_events = getNominalFramesPerPacket();
281
282     if (m_data_buffer->readFrames(n_events, (char *)(data + 8))) {
283         float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getActualRate();
284
285 #if TESTTONE
286         // FIXME: remove this hacked in 1 kHz test signal to
287         // analog-1 when testing is complete.
288         signed int i, int_tpf = (int)ticks_per_frame;
289         unsigned char *sample = data+8+16;
290         for (i=0; i<n_events; i++, sample+=m_event_size) {
291             static signed int a_cx = 0;
292             // Each sample is 3 bytes with MSB in lowest address (ie:
293             // network byte order).  After byte order swap, the 24-bit
294             // MSB is in the second byte of val.
295             signed int val = htonl((int)(0x7fffff*sin((1000.0*2.0*M_PI/24576000.0)*a_cx)));
296             memcpy(sample,((char *)&val)+1,3);
297             if ((a_cx+=int_tpf) >= 24576000) {
298                 a_cx -= 24576000;
299             }
300         }
301 #endif
302
303         // Set up each frames's SPH.
304         for (int i=0; i < n_events; i++, quadlet += dbs) {
305 //FIXME: not sure which is best for the MOTU
306 //            int64_t ts_frame = addTicks(ts, (unsigned int)(i * ticks_per_frame));
307             int64_t ts_frame = addTicks(m_last_timestamp, (unsigned int)(i * ticks_per_frame));
308             *quadlet = htonl(fullTicksToSph(ts_frame));
309         }
310
311         return eCRV_OK;
312     }
313     else return eCRV_XRun;
314
315 }
316
317 enum StreamProcessor::eChildReturnValue
318 MotuTransmitStreamProcessor::generateEmptyPacketHeader (
319     unsigned char *data, unsigned int *length,
320     unsigned char *tag, unsigned char *sy,
321     int cycle, unsigned int dropped, unsigned int max_length )
322 {
323     debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "XMIT EMPTY: CY=%04u, TSP=%011llu (%04u)\n",
324                 cycle, m_last_timestamp, ( unsigned int ) TICKS_TO_CYCLES ( m_last_timestamp ) );
325
326     // Do housekeeping expected for all packets sent to the MOTU, even
327     // for packets containing no audio data.
328     *sy = 0x00;
329     *tag = 1;      // All MOTU packets have a CIP-like header
330     *length = 8;
331
332     m_tx_dbc += fillNoDataPacketHeader ( (quadlet_t *)data, length );
333     return eCRV_OK;
334 }
335
336 enum StreamProcessor::eChildReturnValue
337 MotuTransmitStreamProcessor::generateEmptyPacketData (
338     unsigned char *data, unsigned int *length,
339     unsigned char *tag, unsigned char *sy,
340     int cycle, unsigned int dropped, unsigned int max_length )
341 {
342     return eCRV_OK; // no need to do anything
343 }
344
345 enum StreamProcessor::eChildReturnValue
346 MotuTransmitStreamProcessor::generateSilentPacketHeader (
347     unsigned char *data, unsigned int *length,
348     unsigned char *tag, unsigned char *sy,
349     int cycle, unsigned int dropped, unsigned int max_length )
350 {
351     debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "XMIT SILENT: CY=%04u, TSP=%011llu (%04u)\n",
352                 cycle, m_last_timestamp, ( unsigned int ) TICKS_TO_CYCLES ( m_last_timestamp ) );
353
354     // Do housekeeping expected for all packets sent to the MOTU, even
355     // for packets containing no audio data.
356     *sy = 0x00;
357     *tag = 1;      // All MOTU packets have a CIP-like header
358     *length = 8;
359
360     m_tx_dbc += fillNoDataPacketHeader ( (quadlet_t *)data, length );
361     return eCRV_Packet;
362 }
363
364 enum StreamProcessor::eChildReturnValue
365 MotuTransmitStreamProcessor::generateSilentPacketData (
366     unsigned char *data, unsigned int *length,
367     unsigned char *tag, unsigned char *sy,
368     int cycle, unsigned int dropped, unsigned int max_length )
369 {
370     return eCRV_OK; // no need to do anything
371 }
372
373 unsigned int MotuTransmitStreamProcessor::fillDataPacketHeader (
374     quadlet_t *data, unsigned int* length,
375     uint32_t ts )
376 {
377     quadlet_t *quadlet = (quadlet_t *)data;
378     // Size of a single data frame in quadlets
379     unsigned dbs = m_event_size / 4;
380
381     // The number of events per packet expected by the MOTU is solely
382     // dependent on the current sample rate.  An 'event' is one sample from
383     // all channels plus possibly other midi and control data.
384     signed n_events = getNominalFramesPerPacket();
385
386     // construct the packet CIP-like header.  Even if this is a data-less
387     // packet the dbs field is still set as if there were data blocks
388     // present.  For data-less packets the dbc is the same as the previously
389     // transmitted block.
390     *quadlet = htonl(0x00000400 | ((m_Parent.get1394Service().getLocalNodeId()&0x3f)<<24) | m_tx_dbc | (dbs<<16));
391     quadlet++;
392     *quadlet = htonl(0x8222ffff);
393     quadlet++;
394     return n_events;
395 }
396
397 unsigned int MotuTransmitStreamProcessor::fillNoDataPacketHeader (
398     quadlet_t *data, unsigned int* length )
399 {
400     quadlet_t *quadlet = (quadlet_t *)data;
401     // Size of a single data frame in quadlets
402     unsigned dbs = m_event_size / 4;
403     // construct the packet CIP-like header.  Even if this is a data-less
404     // packet the dbs field is still set as if there were data blocks
405     // present.  For data-less packets the dbc is the same as the previously
406     // transmitted block.
407     *quadlet = htonl(0x00000400 | ((m_Parent.get1394Service().getLocalNodeId()&0x3f)<<24) | m_tx_dbc | (dbs<<16));
408     quadlet++;
409     *quadlet = htonl(0x8222ffff);
410     quadlet++;
411     *length = 8;
412     return 0;
413 }
414
415 bool MotuTransmitStreamProcessor::prepareChild()
416 {
417     debugOutput ( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this );
418     return true;
419 }
420
421 /*
422 * compose the event streams for the packets from the port buffers
423 */
424 bool MotuTransmitStreamProcessor::processWriteBlock(char *data,
425                        unsigned int nevents, unsigned int offset) {
426     bool no_problem=true;
427     unsigned int i;
428
429     // FIXME: ensure the MIDI and control streams are all zeroed until
430     // such time as they are fully implemented.
431     for (i=0; i<nevents; i++) {
432         memset(data+4+i*m_event_size, 0x00, 6);
433     }
434
435     for ( PortVectorIterator it = m_Ports.begin();
436       it != m_Ports.end();
437       ++it ) {
438         // If this port is disabled, don't process it
439         if((*it)->isDisabled()) {continue;};
440
441         Port *port=(*it);
442
443         switch(port->getPortType()) {
444
445         case Port::E_Audio:
446             if (encodePortToMotuEvents(static_cast<MotuAudioPort *>(*it), (quadlet_t *)data, offset, nevents)) {
447                 debugWarning("Could not encode port %s to Motu events",(*it)->getName().c_str());
448                 no_problem=false;
449             }
450             break;
451         case Port::E_Midi:
452 //             if (encodePortToMotuMidiEvents(static_cast<MotuMidiPort *>(*it), (quadlet_t *)data, offset, nevents)) {
453 //                 debugWarning("Could not encode port %s to Midi events",(*it)->getName().c_str());
454 //                 no_problem=false;
455 //             }
456             break;
457         default: // ignore
458             break;
459         }
460     }
461     return no_problem;
462 }
463
464 bool
465 MotuTransmitStreamProcessor::transmitSilenceBlock(char *data,
466                        unsigned int nevents, unsigned int offset) {
467     // This is the same as the non-silence version, except that is
468     // doesn't read from the port buffers.
469     bool no_problem = true;
470     for ( PortVectorIterator it = m_Ports.begin();
471       it != m_Ports.end();
472       ++it ) {
473         Port *port=(*it);
474
475         switch(port->getPortType()) {
476
477         case Port::E_Audio:
478             if (encodeSilencePortToMotuEvents(static_cast<MotuAudioPort *>(*it), (quadlet_t *)data, offset, nevents)) {
479                 debugWarning("Could not encode port %s to MBLA events",(*it)->getName().c_str());
480                 no_problem = false;
481             }
482             break;
483         case Port::E_Midi:
484 //             if (encodeSilencePortToMotuMidiEvents(static_cast<MotuMidiPort *>(*it), (quadlet_t *)data, offset, nevents)) {
485 //                 debugWarning("Could not encode port %s to Midi events",(*it)->getName().c_str());
486 //                 no_problem = false;
487 //             }
488             break;
489         default: // ignore
490             break;
491         }
492     }
493     return no_problem;
494 }
495
496 int MotuTransmitStreamProcessor::encodePortToMotuEvents(MotuAudioPort *p, quadlet_t *data,
497                        unsigned int offset, unsigned int nevents) {
498 // Encodes nevents worth of data from the given port into the given buffer.  The
499 // format of the buffer is precisely that which will be sent to the MOTU.
500 // The basic idea:
501 //   iterate over the ports
502 //     * get port buffer address
503 //     * loop over events
504 //         - pick right sample in event based upon PortInfo
505 //         - convert sample from Port format (E_Int24, E_Float, ..) to MOTU
506 //           native format
507 //
508 // We include the ability to start the transfer from the given offset within
509 // the port (expressed in frames) so the 'efficient' transfer method can be
510 // utilised.
511
512     unsigned int j=0;
513
514     // Use char here since the target address won't necessarily be
515     // aligned; use of an unaligned quadlet_t may cause issues on certain
516     // architectures.  Besides, the target (data going directly to the MOTU)
517     // isn't structured in quadlets anyway; it mainly consists of packed
518     // 24-bit integers.
519     unsigned char *target;
520     target = (unsigned char *)data + p->getPosition();
521
522     switch(m_StreamProcessorManager.getAudioDataType()) {
523         default:
524         case StreamProcessorManager::eADT_Int24:
525             {
526                 quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress());
527
528                 assert(nevents + offset <= p->getBufferSize());
529
530                 // Offset is in frames, but each port is only a single
531                 // channel, so the number of frames is the same as the
532                 // number of quadlets to offset (assuming the port buffer
533                 // uses one quadlet per sample, which is the case currently).
534                 buffer+=offset;
535
536                 for(j = 0; j < nevents; j += 1) { // Decode nsamples
537                     *target = (*buffer >> 16) & 0xff;
538                     *(target+1) = (*buffer >> 8) & 0xff;
539                     *(target+2) = (*buffer) & 0xff;
540
541                     buffer++;
542                     target+=m_event_size;
543                 }
544             }
545             break;
546         case StreamProcessorManager::eADT_Float:
547             {
548                 const float multiplier = (float)(0x7FFFFF);
549                 float *buffer=(float *)(p->getBufferAddress());
550
551                 assert(nevents + offset <= p->getBufferSize());
552
553                 buffer+=offset;
554
555                 for(j = 0; j < nevents; j += 1) { // decode max nsamples
556                     unsigned int v = (int)(*buffer * multiplier);
557                     *target = (v >> 16) & 0xff;
558                     *(target+1) = (v >> 8) & 0xff;
559                     *(target+2) = v & 0xff;
560
561                     buffer++;
562                     target+=m_event_size;
563                 }
564             }
565             break;
566     }
567
568     return 0;
569 }
570
571 int MotuTransmitStreamProcessor::encodeSilencePortToMotuEvents(MotuAudioPort *p, quadlet_t *data,
572                        unsigned int offset, unsigned int nevents) {
573     unsigned int j=0;
574     unsigned char *target = (unsigned char *)data + p->getPosition();
575
576     switch (m_StreamProcessorManager.getAudioDataType()) {
577     default:
578         case StreamProcessorManager::eADT_Int24:
579         case StreamProcessorManager::eADT_Float:
580         for (j = 0; j < nevents; j++) {
581             *target = *(target+1) = *(target+2) = 0;
582             target += m_event_size;
583         }
584         break;
585     }
586
587     return 0;
588 }
589
590 } // end of namespace Streaming
Note: See TracBrowser for help on using the browser.