/*! \file display.hcc * * \section generic Message build up information and more * * \section project Project information. * Project Graphic Equalizer\n * \author O.M. Schinagl * \date 20041011 * \version 0.1 * * \section copyright Copyright * Copyright ©2004 Koninklijke Philips Electronics N.V. All rights reserved * * \section history Change history * 20041011: O.M. Schinagl\n Initial version * ********************************************************************/ /* * Set the Clock rate for this domain. 25.175 Mhz is required for the Video output. */ #define PAL_TARGET_CLOCK_RATE 25175000 /******** System Includes *************/ #include #include "pal_master.hch" /******** Application Includes ********/ #include "configuration.hch" #include "audio.hch" #include "eventhandler_shared.hch" #include "mouse_shared.hch" #include "smartmedia_shared.hch" #include "display_shared.hch" #include "display.hch" #if HAVE_DEBUG #include "debug.hch" #endif /* * Channel to notify others when new mousedata is available. If so * Then mousedata struct is updated with shared data. */ chan unsigned 1 maskupdate_notification; /* */ static rom unsigned 20 images[32] = { ADDRESS_ABOUT_TOP_FONTYS_START, ADDRESS_ABOUT_TOP_TASS_START, ADDRESS_ABOUT_TOP_TRANSFER_START, ADDRESS_ABOUT_TOP_CELOXICA_START, ADDRESS_ABOUT_TOP_DETAILS_START, ADDRESS_ABOUT_BOTTOM_START, ADDRESS_SKIN_START, ADDRESS_HELP_START, ADDRESS_HELP_START, ADDRESS_GRAPH_START, ADDRESS_AUDIO_PLAYER_START }; static rom unsigned 5 image_indexes [5] = {0,IMAGE_SKIN, IMAGE_HELP, 0, IMAGE_ABOUT}; /*! \fn void display_main(skindata_t *skindata, audiodata_t *audiodata, events_t *events, mousedata_t *mousedata) * * \brief This routine handles all drawing of pixels. It never returns! * * \param *skindata struct with all skin information. * \param *audiodata struct with (i)fft data to be drawn. * \param *events struct with all events. * \param *mousedata struct with coordinates to current. * X en Y. * * \return Never Returns. * \retval void */ inline void display_main(skindata_t *skindata, audiodata_t *audiodata, events_t *events, mousedata_t *mousedata) { /* * Setup macro's RAM/Video handles and to coordinate pixel writing. */ macro expr CLOCKRATE = PAL_ACTUAL_CLOCK_RATE; macro expr VIDEOOUT = PalVideoOutOptimalCT(CLOCKRATE); macro expr RAM_BANK0 = PalPL2RAMCT(0); macro expr DW = PalPL2RAMGetMaxDataWidthCT(); macro expr AW = PalPL2RAMGetMaxAddressWidthCT(); macro expr VISIBLEX = PalVideoOutGetVisibleX(VIDEOOUT, CLOCKRATE); macro expr VISIBLEY = PalVideoOutGetVisibleY(VIDEOOUT); macro expr TOTALX = PalVideoOutGetTotalX(VIDEOOUT, CLOCKRATE); macro expr TOTALY = PalVideoOutGetTotalY(VIDEOOUT); macro expr SCANX = PalVideoOutGetX(VIDEOOUT); macro expr SCANY = PalVideoOutGetY(VIDEOOUT); unsigned AW addresses[32]; unsigned DW pixeldata; unsigned 24 visual_graph_color; unsigned 5 address_index; /* * The passed button_state tells us if the button is active, then we * the button is 'on' and we draw it inverted. Otherwise we draw the * area of the button normally. */ macro proc draw_button(button_state) { if (button_state) { PalVideoOutWrite(VIDEOOUT, ~PIXEL); } else { PalVideoOutWrite(VIDEOOUT, PIXEL); } } /* * Prime Rendering Pipeline to start where the skin starts. */ PalPL2RAMSetReadAddress(RAM_BANK0, images[events->image]); /* * Run the following tasks indefinatly and in parallel */ while (TRUE) { par { /* * Before starting this loop we allready set the the * address. Therefor we can start reading the * previously set address and prepare the next address * for the next cycle. */ PalPL2RAMRead(RAM_BANK0, &pixeldata); PalPL2RAMSetReadAddress(RAM_BANK0, (events->mode[3]) ? (images[events->image] +(0 @ (addresses[address_index] \\ 2))) : (images[events->image] +addresses[address_index])); //print_hex_value(0@MASK); /* * When displaying the visual the pixeldata read * from the RAM-bank needs to be interpreted differntly, * since the visual-image is not stored as a MRGB value, * but as a MMMM-value. (M = mask) */ // if (IMAGE_GRAPH == events->image) { if (events->mode[3]) { par { switch (addresses[MASK_AREA_BACKGROUND] <- 2) { case 3: visual_graph_color = ((unsigned 8)(0 @ audiodata->fft_info.read[pixeldata[31:24]]) << 1) @ ((unsigned 8)(0 @ audiodata->fft_info.read[pixeldata[31:24]]) << -1) @ ((unsigned 8)(0 @ audiodata->fft_info.read[pixeldata[31:24]]) << 0); break; case 0: visual_graph_color = ((unsigned 8)(0 @ audiodata->fft_info.read[pixeldata[23:16]]) << 1) @ ((unsigned 8)(0 @ audiodata->fft_info.read[pixeldata[23:16]]) << -1) @ ((unsigned 8)(0 @ audiodata->fft_info.read[pixeldata[23:16]]) << 0); break; case 1: visual_graph_color = ((unsigned 8)(0 @ audiodata->fft_info.read[pixeldata[15:8]]) << 1) @ ((unsigned 8)(0 @ audiodata->fft_info.read[pixeldata[15:8]]) << -1) @ ((unsigned 8)(0 @ audiodata->fft_info.read[pixeldata[15:8]]) << 0); break; case 2: visual_graph_color = ((unsigned 8)(0 @ audiodata->fft_info.read[pixeldata[7:0]]) << 1) @ ((unsigned 8)(0 @ audiodata->fft_info.read[pixeldata[7:0]]) << -1) @ ((unsigned 8)(0 @ audiodata->fft_info.read[pixeldata[7:0]]) << 0); break; default: delay; break; } PalVideoOutWrite(VIDEOOUT, 0 @ visual_graph_color); } } else { /* * Determin what to draw where here. Every case has an * if else statement comparing wether to draw something * special or the background. Every specific drawing * obviously only happens in the masked area. */ switch (MASK) { /* */ case AREA_WAVEFORM: if (SCANY == 0 @ skindata->waveform.bottom -(0 @ (audiodata->ifft_info.read[((SCANX -(0 @ skindata->waveform.left)) <-8)]))) { PalVideoOutWrite(VIDEOOUT, skindata->waveform.color_primary); } else { PalVideoOutWrite(VIDEOOUT, PIXEL); } break; /* * Volume control over the Y-axis. */ case AREA_VOLUME_YAXIS: /* * The volume_position stores * the highest point of our * bar. Every pixel after this * point is drawn. */ if (SCANY >= 0 @ events->volume_position) { PalVideoOutWrite(VIDEOOUT, skindata->volume.color_primary); } else { PalVideoOutWrite(VIDEOOUT, PIXEL); } break; /* * Inputgain control over the Y-axis. */ case AREA_INPUTGAIN_YAXIS: /* * The inputgain_position * stores the highest point of * our bar. Every pixel after * this point is drawn. */ if (SCANY >= 0 @ events->inputgain_position) { PalVideoOutWrite(VIDEOOUT, (events->locked_gain) ? DISABLED : (events->saturated ? skindata->inputgain.color_secondary : skindata->inputgain.color_primary)); } else { PalVideoOutWrite(VIDEOOUT, PIXEL); } break; /* * Spectrum Analyzer */ case AREA_SPECTRUM_ANALYZER: /* * We draw every pixel that is smaller TODO */ if ((SCANY >= (0 @ skindata->spectrum.bottom) -(0 @ audiodata->fft_info.read[(SCANX -(0 @ skindata->spectrum.left))[9:2]])) && ((SCANX -(0 @ skindata->spectrum.left)) <- 2)) { PalVideoOutWrite(VIDEOOUT, PIXEL_SPECTRUM); } else { PalVideoOutWrite(VIDEOOUT, PIXEL); } break; /* * Since all buttons are drawn equally, either * we draw them normally or we inverse them, we * can handle them almost equally. */ case BUTTON_PRESET_1: /* fall through */ case BUTTON_PRESET_2: /* fall through */ case BUTTON_PRESET_3: /* fall through */ case BUTTON_PRESET_4: /* fall through */ case BUTTON_PRESET_5: /* fall through */ case BUTTON_PRESET_6: /* * The active preset tells us what * button is currently enabled. We must * however not forget to add the preset * button offset to possibly match it * with the current mask. */ draw_button(((events->active_preset +BUTTON_PRESET_1) <- 8)== MASK); break; case BUTTON_PRECISE: /* fall through */ case BUTTON_CONVEX_HALF: /* fall through */ case BUTTON_CONVEX_FULL: /* * equalizer mode tells us what button * is currently enabled. By adding the * equalizer mode button offset we can * safley check wether it matches our * mask. */ draw_button(((0 @ events->equalizer_mode) +BUTTON_PRECISE) == MASK); break; case BUTTON_LOG: /* * */ draw_button(audiodata->display_log); break; case BUTTON_DEMO: draw_button(events->button_demo_state); break; case BUTTON_RESET: draw_button(events->button_reset_state); break; case BUTTON_HELP: draw_button(events->image == IMAGE_HELP); break; case BUTTON_ABOUT: draw_button(events->image == IMAGE_ABOUT); break; case AREA_ABOUT_TOP: /* Check wether the application is in about mode. if (IMAGE_ABOUT == events->image) { */ if (events->mode[2]) { //draw current about top window par { events->image = events->image_about; address_index = MASK_AREA_ABOUT_TOP; PalVideoOutWrite(VIDEOOUT, PIXEL); } } else { PalVideoOutWrite(VIDEOOUT, PIXEL); } break; case BUTTON_URL_FONTYS: /* fall through */ case BUTTON_URL_TASS: /* fall through */ case BUTTON_URL_TRANSFER: /* fall through */ case BUTTON_URL_CELOXICA: /* fall through */ case BUTTON_URL_DETAILS: /* fall through */ case AREA_ABOUT_BOTTOM: /* Check wether the application is in about mode. if (IMAGE_ABOUT == events->image) { */ if (events->mode[2]) { //draw current about bottom window par { events->image = IMAGE_ABOUT_BOTTOM; address_index = MASK_AREA_ABOUT_BOTTOM; PalVideoOutWrite(VIDEOOUT, PIXEL); } } else { PalVideoOutWrite(VIDEOOUT, PIXEL); } break; case BUTTON_PREV_TRACK: draw_button(events->prev_track_state); break; case BUTTON_PLAY: draw_button(audiodata->player_state== PLAYING); break; case BUTTON_PAUSE: draw_button(audiodata->player_state == PAUSED); break; case BUTTON_STOP: draw_button(audiodata->player_state == STOPPED); break; case BUTTON_NEXT_TRACK: draw_button(events->next_track_state); break; case BUTTON_REPEAT: draw_button(audiodata->player_mode); break; case AREA_AUDIO_PLAYER: /* Check wether the application is in audioplayer mode. */ //print_hex_value(0@MASK); if (events->mode[4]) { par { events->image = IMAGE_AUDIO_PLAYER; address_index = MASK_AREA_AUDIO_PLAYER; PalVideoOutWrite(VIDEOOUT, PIXEL); } } else { PalVideoOutWrite(VIDEOOUT, PIXEL); } break; case AREA_MASK_END: par { events->image = image_indexes[events->mode <- 3]; address_index = MASK_AREA_BACKGROUND; PalVideoOutWrite(VIDEOOUT, PIXEL); } break; /* * The default case is split up into two parts * actually. This is because we have 128 bands * for the equalizer and thus as many mask * entries. Since we don't want 128 identical * cases we check wether the equalizer mask is * currently active and if so draw it. If this * is not the case we simply draw the * background. */ default: /* (MASK <= AREA_EQUALIZER_MAX) && (AREA_EQUALIZER_MIN <= MASK) */ if ((AREA_EQUALIZER_MIN <= MASK) && (!events->locked_display)) { if ((SCANY == 0 @ events->equalizer_display[(MASK -AREA_EQUALIZER_MIN) <- 7]) || ((SCANY +1) == 0 @ events->equalizer_display[(MASK -AREA_EQUALIZER_MIN) <- 7])) { PalVideoOutWrite(VIDEOOUT, skindata->equalizer.color_primary); } else { PalVideoOutWrite(VIDEOOUT, PIXEL); } } else { PalVideoOutWrite(VIDEOOUT, PIXEL); } break; } } /* * We compare our current X and Y scan positions of the * output to the x and y data of the mouse. When those * are equal we set the current mask to the mask stored * in memory at that location. We then know what mask * is to be used for events. */ if (MOUSE_UPDATED == mousedata->status) { if ((SCANX == 0 @ mousedata->x) && (SCANY == 0 @ mousedata->y)) { par { events->mask = MASK; mousedata->status = MOUSE_NOT_UPDATED; maskupdate_notification ! MOUSE_UPDATED; } } else { delay; } } else { delay; } /* * The current position of the screen can lay in an * area called the blanking area. We don't have data * for this area as it is not drawn. We therefor have * to determin wether we are beyond the visible area of * the screen, but before the end of the total width of * the screen. Our pipeline consists of 5 total stages. * Therefor we have to substract 5 pixels. */ if ((SCANX > (VISIBLEX - 5)) && (SCANX <= (TOTALX - 5))) { par { if (SCANY == (VISIBLEY +1)) { par { address_index++; addresses[address_index] = 0; } } else { delay; } /* * We are in the blanking area of the screen. * If we are on the last line, and thus last * pixel we reset our address counter. */ if (SCANY == (TOTALY -1)) { /* * Reset our draw address counter to 0. */ par { address_index = 0; addresses[MASK_AREA_BACKGROUND] = 0; } } else { /* * We have reached the end of the * visible line, but not the end of * the screen. Therefore do nothing. */ delay; } } } else { /* * Increase the memory counter for each pixel * drawn thus keeping the memory location in * sync with the current pixel position. */ par { if (address_index != MASK_AREA_BACKGROUND) { addresses[address_index]++; } else { delay; } addresses[MASK_AREA_BACKGROUND]++; } } } } } /* --- display_main() --- */ /*! \fn void reload_equalizer(events_t *events, unsigned 4 *equalizer_levels) * \brief This function copies the supplied equalizer values to the array * used for displaying equalizer data. * * \param *events events struct * \param *equalizer_levels pointer to 128 entries where equalizer * is to be copied from. * * \return void * \retval void */ void reload_equalizer(events_t *events, unsigned 4 *equalizer_levels) { unsigned 7 equalizer_band; events->locked_display = TRUE; delay; do { events->equalizer_display[equalizer_band] = equalizer_table_inv[equalizer_levels[equalizer_band]]; equalizer_band++; } while (equalizer_band); events->locked_display = FALSE; } /* --- reload_equalizer() --- */