diff --git a/include/eia608.h b/include/eia608.h index 8bdcf2a389..b73a142673 100644 --- a/include/eia608.h +++ b/include/eia608.h @@ -57,14 +57,12 @@ typedef enum { /** @brief Caption display parameters */ typedef struct { - eia608_channel_t cc; ///< Caption channel to transmit on (default: EIA608_CC1) int row; ///< The row to display the caption on, range 1-15 (default: 11) bool underline; ///< Enable underline for the caption (default: false) - bool hidden; ///< Prepare the caption but don't display it (default: false) } eia608_captionparms_t; /** @brief Calculate EIA608 parity for 7-bit value */ -#define EIA608_PARITY(b) (((1 ^ ((b)>>0) ^ ((b)>>1) ^ ((b)>>2) ^ ((b)>>3) ^ ((b)>>4) ^ ((b)>>5) ^ ((b)>>6)) & 1) << 7) +#define EIA608_PARITY(b) ((((1 ^ ((b)>>0) ^ ((b)>>1) ^ ((b)>>2) ^ ((b)>>3) ^ ((b)>>4) ^ ((b)>>5) ^ ((b)>>6)) & 1) << 7) | (b)) ///@cond #define EIA608_CTRL(c) ((EIA608_PARITY((c) & 0xFF)) | (EIA608_PARITY((c) >> 8) << 8)) @@ -198,9 +196,16 @@ void eia608_write_ctrl_raw(eia608_ctrl_t ctrl); * @brief Emit a caption, with automatic positioning on the screen. * * EIA-608 captions are divided into maximum 4 lines of 32 characters each. - * This function will automatically word-wrap the caption into the lines if + * This function will automatically word-wrap the caption into multiple lines if * necessary, but also respects any embedded newlines in the caption. * + * The caption will not be displayed but just prepared. You need + * to call #eia608_caption_show to actually display it. This allows for perfect + * syncing as preparing a caption can take a non trivial amount of time + * (about 1 second every 60 characters), so to avoid avoid the captions always + * a bit late, you can prepare the next sentence in advance and then show it + * when needed. + * * By default, the caption will be displayed centered, at the bottom of the * screen, using white on black colors. You can change these parameters by * setting fields in the \p parms structure. @@ -218,28 +223,25 @@ void eia608_write_ctrl_raw(eia608_ctrl_t ctrl); * this function to display a caption before the previous one has been * displayed, the new caption will replace the old one. * + * @param cc Channel in which the caption must be prepared. * @param utf8_str The UTF-8 string to display - * @param duration_secs The duration in seconds to display the caption * @param parms Optiona parameters to control the caption * (use NULL for default values) * * @see Wikipedia article on EIA-608: https://en.wikipedia.org/wiki/EIA-608 */ -void eia608_caption(const char *utf8_str, float duration_secs, eia608_captionparms_t *parms); +void eia608_caption_prepare(eia608_channel_t cc, const char *utf8_str, eia608_captionparms_t *parms); /** * @brief Show a caption that was previously prepared with #eia608_caption * - * By default, #eia608_caption will prepare the caption and display it right - * away. Transmitting a caption can take a non negligible amount of time - * (it is about 1 second every 60 characters). If you need perfect syncing, - * you can first prepare the caption and keep it hidden, by calling - * #eia608_caption with the \p hidden parameter set to true in \p parms. - * Then, when you are ready to display the caption, call this function. + * This shows a caption that was prepared by #eia608_caption_prepare. The caption + * will be displayed on the screen for the specified duration, and then hidden. * * @param cc Channel in which the caption was prepared. + * @param duration_secs The duration in seconds to display the caption */ -void eia608_caption_show(eia608_channel_t cc); +void eia608_caption_show(eia608_channel_t cc, float duration_secs); #ifdef __cplusplus } diff --git a/src/eia608.c b/src/eia608.c index 5f9582e583..831427e58a 100644 --- a/src/eia608.c +++ b/src/eia608.c @@ -16,9 +16,6 @@ #include #include -#define EIA608_NOP 0x8080 ///< NOP filler (already including parity) -#define EIA608_EDM 0x942C ///< Erase Display Memory - #define SIG_ON 0x808080FF ///< IRE 50 (50% intensity) #define SIG_OFF 0x00000000 ///< IRE 0 @@ -76,8 +73,9 @@ static void linebuffer_init(surface_t *lb) static void linebuffer_write(surface_t *lb, uint16_t data) { uint32_t *buffer = lb->buffer; - buffer += SIGW_BLANK * SIGW_SCALE; // skip the blank part + buffer += SIGW_BLANK; // skip the blank part buffer += SIGW_LEADIN * SIGW_SCALE; // skip the leadin part + data = (data << 8) | (data >> 8); for (int i=0; i<16; i++) { for (int j=0; j> 8) << 8); - ring_buffer[rb_wpos] = (data >> 8) | (data << 8); + ring_buffer[rb_wpos] = data; rb_wpos = next; return true; } @@ -422,7 +429,7 @@ static uint32_t eia608_encode_char(const char **utf8_str) return 0; } -void eia608_caption(const char *utf8_str, float duration_secs, eia608_captionparms_t *parms) +void eia608_caption_prepare(eia608_channel_t cc, const char *utf8_str, eia608_captionparms_t *parms) { // EIA-608 POP-ON mode can only hold up to three lines of 32 characters each enum { MAX_ROWS = 4 }; @@ -430,7 +437,6 @@ void eia608_caption(const char *utf8_str, float duration_secs, eia608_captionpar eia608_captionparms_t _default = {0}; if (parms == NULL) parms = &_default; - eia608_channel_t cc = parms->cc ? parms->cc : EIA608_CC1; int first_row = parms->row ? parms->row : 11; uint32_t buffer[32*MAX_ROWS+8]; @@ -441,8 +447,8 @@ void eia608_caption(const char *utf8_str, float duration_secs, eia608_captionpar // Convert the input string from UTF-8 into EIA-608 16-bit characters. for (int i = 0; i < sizeof(buffer)/2; i++) { - if (*utf8_str == 0) break; - uint16_t ch = eia608_encode_char(&utf8_str); + if (*utf8_str == 0) break; + uint32_t ch = eia608_encode_char(&utf8_str); if (ch > 0x100 && cc == EIA608_CC2) ch |= 0x8000; if (ch == 0) continue; buffer[buf_len++] = ch; @@ -468,6 +474,13 @@ void eia608_caption(const char *utf8_str, float duration_secs, eia608_captionpar wrap = ch; } + // Empty line, just skip it (or stop if we're done) + if (ch == 0) { + if (buf_idx >= buf_len) break; + buf_idx++; + continue; + } + // We need to wrap if we reached 32 chars and there are more if (ch == 32 && buf_idx+ch < buf_len) ch = wrap; @@ -486,7 +499,7 @@ void eia608_caption(const char *utf8_str, float duration_secs, eia608_captionpar // Split the token into 1, 2, or 1+2 byte values uint16_t single = 0, pair = 0; if (token >> 16) { - single = token >> 8; + single = token >> 16; pair = token & 0xffff; } else if (token >> 8) { pair = token; @@ -524,15 +537,14 @@ void eia608_caption(const char *utf8_str, float duration_secs, eia608_captionpar // Move to the next line first_row++; - utf8_str += wrap+1; - } - // Emit end caption command (swap buffers) - if (!parms->hidden) - eia608_caption_show(cc); + // Skip wrapping character + buf_idx++; + } } -void eia608_caption_show(eia608_channel_t cc) +void eia608_caption_show(eia608_channel_t cc, float duration_secs) { eia608_write_ctrl_raw(cc == EIA608_CC1 ? EIA608_CC1_EOC : EIA608_CC2_EOC); + force_clear_timer = duration_secs * 30; }