diff --git a/docs/libxmp.rst b/docs/libxmp.rst
index 8d04d628..4545a5aa 100644
--- a/docs/libxmp.rst
+++ b/docs/libxmp.rst
@@ -632,6 +632,22 @@ int xmp_set_position(xmp_context c, int pos)
     The new position index, ``-XMP_ERROR_INVALID`` of the new position is
     invalid or ``-XMP_ERROR_STATE`` if the player is not in playing state.
 
+.. _xmp_set_row():
+
+int xmp_set_row(xmp_context c, int row)
+````````````````````````````````````````````
+
+  Skip replay to the given row.
+
+  **Parameters:**
+    :c: the player context handle.
+
+    :row: the row to set.
+
+  **Returns:**
+    The new row, ``-XMP_ERROR_INVALID`` if the new row is invalid or
+    ``-XMP_ERROR_STATE`` if the player is not in playing state.
+
 .. _xmp_stop_module():
 
 void xmp_stop_module(xmp_context c)
diff --git a/include/xmp.h b/include/xmp.h
index 52a8257e..6bd32dc2 100644
--- a/include/xmp.h
+++ b/include/xmp.h
@@ -333,6 +333,7 @@ EXPORT char      **xmp_get_format_list (void);
 EXPORT int         xmp_next_position   (xmp_context);
 EXPORT int         xmp_prev_position   (xmp_context);
 EXPORT int         xmp_set_position    (xmp_context, int);
+EXPORT int         xmp_set_row         (xmp_context, int);
 EXPORT void        xmp_stop_module     (xmp_context);
 EXPORT void        xmp_restart_module  (xmp_context);
 EXPORT int         xmp_seek_time       (xmp_context, int);
diff --git a/libxmp.map b/libxmp.map
index a3f8e43b..61794574 100644
--- a/libxmp.map
+++ b/libxmp.map
@@ -61,5 +61,6 @@ XMP_4.4 {
   global:
     xmp_set_player;
     xmp_get_player;
+    xmp_set_row;
 } XMP_4.3;
 
diff --git a/src/control.c b/src/control.c
index 30a26482..6a70850e 100644
--- a/src/control.c
+++ b/src/control.c
@@ -174,6 +174,37 @@ int xmp_set_position(xmp_context opaque, int pos)
 	return p->pos;
 }
 
+int xmp_set_row(xmp_context opaque, int row)
+{
+	struct context_data *ctx = (struct context_data *)opaque;
+	struct player_data *p = &ctx->p;
+	struct module_data *m = &ctx->m;
+	struct xmp_module *mod = &m->mod;
+	struct flow_control *f = &p->flow;
+	int pos = p->pos;
+	int pattern = mod->xxo[pos];
+
+	if (pos < 0 || pos >= mod->len) {
+		pos = 0;
+	}
+
+	if (ctx->state < XMP_STATE_PLAYING)
+		return -XMP_ERROR_STATE;
+
+	if (row >= mod->xxp[pattern]->rows)
+		return -XMP_ERROR_INVALID;
+
+	/* See set_position. */
+	if (p->pos < 0)
+		p->pos = 0;
+	p->ord = p->pos;
+	p->row = row;
+	p->frame = -1;
+	f->num_rows = mod->xxp[mod->xxo[p->ord]]->rows;
+
+	return row;
+}
+
 void xmp_stop_module(xmp_context opaque)
 {
 	struct context_data *ctx = (struct context_data *)opaque;
@@ -240,7 +271,7 @@ int xmp_channel_mute(xmp_context opaque, int chn, int status)
 	if (chn < 0 || chn >= XMP_MAX_CHANNELS) {
 		return -XMP_ERROR_INVALID;
 	}
-	
+
 	ret = p->channel_mute[chn];
 
 	if (status >= 2) {
@@ -264,7 +295,7 @@ int xmp_channel_vol(xmp_context opaque, int chn, int vol)
 	if (chn < 0 || chn >= XMP_MAX_CHANNELS) {
 		return -XMP_ERROR_INVALID;
 	}
-	
+
 	ret = p->channel_vol[chn];
 
 	if (vol >= 0 && vol <= 100) {
