void GlissandoSegment::layout() { QRectF r = QRectF(0.0, 0.0, pos2().x(), pos2().y()).normalized(); qreal lw = spatium() * glissando()->lineWidth().val() * .5; setbbox(r.adjusted(-lw, -lw, lw, lw)); adjustReadPos(); }
void GlissandoSegment::draw(QPainter* painter) const { painter->save(); qreal _spatium = spatium(); QPen pen(glissando()->curColor()); pen.setWidthF(glissando()->lineWidth().val() * spatium()); pen.setCapStyle(Qt::RoundCap); painter->setPen(pen); // painter->drawLine(QPointF(), pos2()); // DEBUG // rotate painter so that the line become horizontal qreal w = pos2().x(); qreal h = pos2().y(); qreal l = sqrt(w * w + h * h); qreal wi = asin(-h / l) * 180.0 / M_PI; qreal scale = painter->worldTransform().m11(); painter->rotate(-wi); if (glissando()->glissandoType() == Glissando::Type::STRAIGHT) { painter->drawLine(QLineF(0.0, 0.0, l, 0.0)); } else if (glissando()->glissandoType() == Glissando::Type::WAVY) { QRectF b = symBbox(SymId::wiggleTrill); qreal w = symAdvance(SymId::wiggleTrill); int n = (int)(l / w); // always round down (truncate) to avoid overlap qreal x = (l - n*w) * 0.5; // centre line in available space std::vector<SymId> ids; for (int i = 0; i < n; ++i) ids.push_back(SymId::wiggleTrill); // this is very ugly but fix #68846 for now // bool tmp = MScore::pdfPrinting; // MScore::pdfPrinting = true; score()->scoreFont()->draw(ids, painter, magS(), QPointF(x, -(b.y() + b.height()*0.5) ), scale); // MScore::pdfPrinting = tmp; } if (glissando()->showText()) { const TextStyle& st = score()->textStyle(TextStyleType::GLISSANDO); QRectF r = st.fontMetrics(_spatium).boundingRect(glissando()->text()); // if text longer than available space, skip it if (r.width() < l) { qreal yOffset = r.height() + r.y(); // find text descender height // raise text slightly above line and slightly more with WAVY than with STRAIGHT yOffset += _spatium * (glissando()->glissandoType() == Glissando::Type::WAVY ? 0.4 : 0.1); painter->setFont(st.font(_spatium * MScore::pixelRatio)); qreal x = (l - r.width()) * 0.5; painter->drawText(QPointF(x, -yOffset), glissando()->text()); } } painter->restore(); }
QVariant GlissandoSegment::propertyDefault(P_ID id) const { switch (id) { case P_ID::GLISS_TYPE: case P_ID::GLISS_TEXT: case P_ID::GLISS_SHOW_TEXT: case P_ID::GLISSANDO_STYLE: case P_ID::PLAY: return glissando()->propertyDefault(id); default: return LineSegment::propertyDefault(id); } }
bool GlissandoSegment::setProperty(P_ID id, const QVariant& v) { switch (id) { case P_ID::GLISS_TYPE: case P_ID::GLISS_TEXT: case P_ID::GLISS_SHOW_TEXT: case P_ID::GLISSANDO_STYLE: case P_ID::PLAY: return glissando()->setProperty(id, v); default: return LineSegment::setProperty(id, v); } }
QVariant GlissandoSegment::getProperty(P_ID id) const { switch (id) { // route properties of the whole Glissando element to it case P_ID::GLISS_TYPE: case P_ID::GLISS_TEXT: case P_ID::GLISS_SHOW_TEXT: case P_ID::GLISSANDO_STYLE: case P_ID::PLAY: return glissando()->getProperty(id); default: return LineSegment::getProperty(id); } }
void GlissandoSegment::draw(QPainter* painter) const { painter->save(); qreal _spatium = spatium(); QPen pen(glissando()->curColor()); pen.setWidthF(glissando()->lineWidth().val() * spatium()); pen.setCapStyle(Qt::RoundCap); painter->setPen(pen); // painter->drawLine(QPointF(), pos2()); // DEBUG // rotate painter so that the line become horizontal qreal w = pos2().x(); qreal h = pos2().y(); qreal l = sqrt(w * w + h * h); qreal wi = asin(-h / l) * 180.0 / M_PI; painter->rotate(-wi); if (glissando()->glissandoType() == Glissando::Type::STRAIGHT) { painter->drawLine(QLineF(0.0, 0.0, l, 0.0)); } else if (glissando()->glissandoType() == Glissando::Type::WAVY) { QRectF b = symBbox(SymId::wiggleTrill); // qreal h = symHeight(SymId::wiggleTrill); // DEBUG qreal w = symWidth(SymId::wiggleTrill); int n = (int)(l / w); // always round down (truncate) to avoid overlap qreal x = (l - n*w) * 0.5; // centre line in available space drawSymbol(SymId::wiggleTrill, painter, QPointF(x, b.height()*.70), n); } if (glissando()->showText()) { const TextStyle& st = score()->textStyle(TextStyleType::GLISSANDO); QFont f = st.fontPx(_spatium); QRectF r = QFontMetricsF(f).boundingRect(glissando()->text()); // if text longer than available space, skip it if (r.width() < l) { qreal yOffset = r.height() + r.y(); // find text descender height // raise text slightly above line and slightly more with WAVY than with STRAIGHT yOffset += _spatium * (glissando()->glissandoType() == Glissando::Type::WAVY ? 0.5 : 0.1); painter->setFont(f); qreal x = (l - r.width()) * 0.5; painter->drawText(QPointF(x, -yOffset), glissando()->text()); } } painter->restore(); }
// Progress module by one tick static void play_module() { uint8_t ch, fx, fxp; uint8_t temp_b; uint16_t temp_w; fxm_t *p_fxm; dma_t *p_dma; bool pattern_jump = false; uint8_t ix_period; uint8_t ix_sample; uint8_t *p_ptn; // Advance tick if( ++tick == speed ) tick = 0; // Handle row delay if( delay ) { if( tick == 0 ) delay--; return; } // Advance playback if( tick == 0 ) { if( ++ix_row == 64 ) { ix_row = 0; if( ++ix_order == ROM_READB( &p_mod->order_count ) ) ix_order = 0; } // Forced order/row if( ix_nextorder != 0xFF ) { ix_order = ix_nextorder; ix_nextorder = 0xFF; } if( ix_nextrow != 0xFF ) { ix_row = ix_nextrow; ix_nextrow = 0xFF; } } // Set up pointers p_ptn = ( ( uint8_t* )p_mod ) + sizeof( ptm_t ) + ( ROM_READB( &p_mod->order[ ix_order ] ) << 10 ) + ( ix_row << 4 ); p_fxm = fxm; p_dma = dma; for( ch = 0; ch != 4; ch++ ) { // Deconstruct cell (what the hell were they smoking?) temp_b = ROM_READB( p_ptn++ ); // sample.msb and period.msb temp_w = ( temp_b & 0x0F ) << 8; ix_sample = temp_b & 0xF0; temp_w |= ROM_READB( p_ptn++ ); // period.lsb temp_b = ROM_READB( p_ptn++ ); // sample.lsb and effect ix_sample |= HI4( temp_b ); fx = LO4( temp_b ) << 4; fxp = ROM_READB( p_ptn++ ); // parameters if( fx == 0xE0 ) { fx |= HI4( fxp ); // extended parameters fxp &= 0x0F; } ix_period = 0x7F; // period index if( temp_w ) { for( temp_b = 0; temp_b != 36; temp_b++ ) { if( ROM_READW( &period_tbl[ 0 ][ temp_b ] ) == temp_w ) { ix_period = temp_b; break; } } } // General effect parameter memory // NOTE: Unsure if this is true to the original ProTracker, but alot of // modern players and trackers do implement this functionality. if( fx == 0x10 || fx == 0x20 || fx == 0xE1 || fx == 0xE2 || fx == 0x50 || fx == 0x60 || fx == 0xA0 ) { if( fxp ) { p_fxm->param = fxp; } else { fxp = p_fxm->param; } } if( tick == ( fx == 0xED ? fxp : 0 ) ) { if( ix_sample != 0 ) { // Cell has sample temp_b = ix_sample - 1; p_fxm->sample = temp_b; p_fxm->volume = sample[ temp_b ].volume; // Reset volume p_dma->volume = sample[ temp_b ].volume; } // Set tuning if( fx == 0xE5 ) sample[ p_fxm->sample ].tuning = fxp; // Reset oscillators if( ( p_fxm->vibr.mode & 0x4 ) == 0x0 ) p_fxm->vibr.offset = 0; if( ( p_fxm->trem.mode & 0x4 ) == 0x0 ) p_fxm->trem.offset = 0; if( ix_period != 0x7F ) { // Cell has note if( fx == 0x30 || fx == 0x50 ) { // Tone-portamento effect setup p_fxm->port_target = ROM_READW( &period_tbl[ sample[ ix_sample ].tuning ][ ix_period ] ); } else { // Start note temp_b = p_fxm->sample; note_start( p_dma, temp_b, ix_period, ( fx == 0x90 ? fxp : 0 ) ); // Set required effect memory parameters p_fxm->period = ROM_READW( &period_tbl[ sample[ temp_b ].tuning ][ ix_period ] ); } } // Effects processed when tick = 0 switch( fx ) { case 0x30: // Portamento if( fxp ) p_fxm->port_speed = fxp; break; case 0xB0: // Jump to pattern ix_nextorder = ( fxp >= ROM_READB( &p_mod->order_count ) ? 0x00 : fxp ); ix_nextrow = 0; pattern_jump = true; break; case 0xC0: // Set volume p_fxm->volume = MIN( fxp, 0x40 ); p_dma->volume = p_fxm->volume; break; case 0xD0: // Jump to row fxp = HI4( fxp ) * 10 + LO4( fxp ); if( !pattern_jump ) ix_nextorder = ( ( ix_order + 1 ) >= ROM_READB( &p_mod->order_count ) ? 0x00 : ix_order + 1 ); pattern_jump = true; ix_nextrow = ( fxp > 63 ? 0 : fxp ); break; case 0xF0: // Set speed if( fxp > 0x20 ) { cia = SAMPLE_RATE / ( ( 24 * fxp ) / 60 ); } else { speed = fxp; } break; case 0x40: // Vibrato if( fxp ) p_fxm->vibr.fxp = fxp; break; case 0x70: // Tremolo if( fxp ) p_fxm->trem.fxp = fxp; break; case 0xE1: // Fine slide up p_fxm->period = MAX( p_fxm->period - fxp, PERIOD_MIN ); p_dma->rate = SYS_FREQ / p_fxm->period; break; case 0xE2: // Fine slide down p_fxm->period = MIN( p_fxm->period + fxp, PERIOD_MAX ); p_dma->rate = SYS_FREQ / p_fxm->period; break; case 0xE3: // Glissando control p_fxm->glissando = ( fxp != 0 ); break; case 0xE4: // Set vibrato waveform p_fxm->vibr.mode = fxp; break; case 0xE6: // Loop-back (advanced looping) if( fxp == 0x0 ) { p_fxm->loop_order = ix_order; p_fxm->loop_row = ix_row; } else { p_fxm->loop_count = ( p_fxm->loop_count ? p_fxm->loop_count - 1 : fxp ); if( p_fxm->loop_count ) { ix_nextorder = p_fxm->loop_order; ix_nextrow = p_fxm->loop_row; } } break; case 0xE7: // Set tremolo waveform p_fxm->trem.mode = fxp; break; case 0xEA: // Fine volume slide up p_fxm->volume = MIN( p_fxm->volume + fxp, 0x40 ); p_dma->volume = p_fxm->volume; break; case 0xEB: // Fine volume slide down p_fxm->volume = MAX( p_fxm->volume - fxp, 0 ); p_dma->volume = p_fxm->volume; break; case 0xEE: // Delay delay = fxp; break; } } else { // Effects processed when tick > 0 switch( fx ) { case 0x10: // Slide up p_fxm->period = MAX( p_fxm->period - fxp, PERIOD_MIN ); p_dma->rate = SYS_FREQ / p_fxm->period; break; case 0x20: // Slide down p_fxm->period = MIN( p_fxm->period + fxp, PERIOD_MAX ); p_dma->rate = SYS_FREQ / p_fxm->period; break; case 0xE9: // Retrigger note temp_b = tick; while( temp_b >= fxp ) temp_b -= fxp; if( temp_b == 0 ) note_start( p_dma, p_fxm->sample, ix_period, ( fx == 0x90 ? fxp : 0 ) ); break; case 0xEC: // Note cut if( fxp == tick ) p_dma->volume = 0x00; break; default: // Multi-effect processing // Portamento if( fx == 0x30 || fx == 0x50 ) { if( p_fxm->period < p_fxm->port_target ) p_fxm->period = MIN( p_fxm->period + p_fxm->port_speed, p_fxm->port_target ); else p_fxm->period = MAX( p_fxm->period - p_fxm->port_speed, p_fxm->port_target ); if( p_fxm->glissando ) p_dma->rate = SYS_FREQ / glissando( ch ); else p_dma->rate = SYS_FREQ / p_fxm->period; } // Volume slide if( fx == 0x50 || fx == 0x60 || fx == 0xA0 ) { if( ( fxp & 0xF0 ) == 0 ) p_fxm->volume -= ( LO4( fxp ) ); if( ( fxp & 0x0F ) == 0 ) p_fxm->volume += ( HI4( fxp ) ); p_fxm->volume = MAX( MIN( p_fxm->volume, 0x40 ), 0 ); p_dma->volume = p_fxm->volume; } } } // Normal play and arpeggio if( fx == 0x00 ) { temp_b = tick; while( temp_b > 2 ) temp_b -= 2; if( temp_b == 0 ) { // Reset p_dma->rate = SYS_FREQ / p_fxm->period; } else if( fxp ) { // Arpeggio p_dma->rate = SYS_FREQ / arpeggio( ch, ( temp_b == 1 ? HI4( fxp ) : LO4( fxp ) ) ); } } else if( fx == 0x40 || fx == 0x60 ) { // Vibrato p_dma->rate = SYS_FREQ / ( p_fxm->period + do_osc( &p_fxm->vibr ) ); } else if( fx == 0x70 ) { // Tremolo temp_b = p_fxm->volume + do_osc( &p_fxm->trem ); p_dma->volume = MAX( MIN( temp_b, 0x40 ), 0 ); } p_fxm++; p_dma++; } }