shithub: dumb

Download patch

ref: 153721b1c9f4cde62fa17d6aef56f782b37384bf
parent: 99111038210a07af485be94a6e85dc43faa10eff
author: Chris Moeller <kode54@gmail.com>
date: Mon Jun 3 14:41:31 EDT 2013

- Implemented loop-accurate time position reporting into DUMB
- Implemented get samples stopping short on loops

--- a/dumb/include/dumb.h
+++ b/dumb/include/dumb.h
@@ -585,6 +585,10 @@
 	sample_t *samples
 );
 
+typedef long (*DUH_SIGRENDERER_GET_POSITION)(
+	sigrenderer_t *sigrenderer
+);
+
 typedef void (*DUH_END_SIGRENDERER)(sigrenderer_t *sigrenderer);
 
 typedef void (*DUH_UNLOAD_SIGDATA)(sigdata_t *sigdata);
@@ -600,6 +604,7 @@
 	DUH_SIGRENDERER_SET_SIGPARAM       sigrenderer_set_sigparam;
 	DUH_SIGRENDERER_GENERATE_SAMPLES   sigrenderer_generate_samples;
 	DUH_SIGRENDERER_GET_CURRENT_SAMPLE sigrenderer_get_current_sample;
+	DUH_SIGRENDERER_GET_POSITION       sigrenderer_get_position;
 	DUH_END_SIGRENDERER                end_sigrenderer;
 	DUH_UNLOAD_SIGDATA                 unload_sigdata;
 }
--- a/dumb/include/internal/it.h
+++ b/dumb/include/internal/it.h
@@ -33,6 +33,7 @@
 #include <stddef.h>
 
 #include "barray.h"
+#include "tarray.h"
 
 
 /** TO DO: THINK ABOUT THE FOLLOWING:
@@ -722,6 +723,21 @@
 #ifdef BIT_ARRAY_BULLSHIT
 	/* bit array, which rows are played, only checked by pattern break or loop commands */
 	void * played;
+
+	/*
+	   Loop indicator for internal processes, may also be useful for external processes 
+	   0 - Not looped
+	   1 - Looped
+	  -1 - Continued past loop
+	 */
+	int looped;
+
+	/*
+	   Kept until looped
+	*/
+	LONG_LONG time_played;
+
+	void * row_timekeeper;
 #endif
 
 	long gvz_time;
--- /dev/null
+++ b/dumb/include/internal/tarray.h
@@ -1,0 +1,21 @@
+#ifndef _T_ARRAY_H_
+#define _T_ARRAY_H_
+
+#include <stdlib.h>
+
+#include "../dumb.h"
+
+void * timekeeping_array_create(size_t size);
+void timekeeping_array_destroy(void * array);
+void * timekeeping_array_dup(void * array);
+
+void timekeeping_array_reset(void * array, size_t loop_start);
+
+void timekeeping_array_push(void * array, size_t index, LONG_LONG time);
+void timekeeping_array_bump(void * array, size_t index);
+
+unsigned int timekeeping_array_get_count(void * array, size_t index);
+
+LONG_LONG timekeeping_array_get_item(void * array, size_t index);
+
+#endif
--- a/dumb/prj/dumb/dumb.pro
+++ b/dumb/prj/dumb/dumb.pro
@@ -36,6 +36,7 @@
     ../../src/helpers/clickrem.c \
     ../../src/helpers/blip_buf.c \
     ../../src/helpers/barray.c \
+    ../../src/helpers/tarray.c \
     ../../src/it/xmeffect.c \
     ../../src/it/readxm2.c \
     ../../src/it/readxm.c \
@@ -108,6 +109,7 @@
     ../../include/internal/dumb.h \
     ../../include/internal/blip_buf.h \
     ../../include/internal/barray.h \
+    ../../include/internal/tarray.h \
     ../../include/internal/aldumb.h \
     ../../include/internal/fir_resampler.h \
     ../../include/internal/stack_alloc.h \
--- a/dumb/src/core/rendsig.c
+++ b/dumb/src/core/rendsig.c
@@ -143,7 +143,15 @@
 
 long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer)
 {
-	return sigrenderer ? sigrenderer->pos : -1;
+	DUH_SIGRENDERER_GET_POSITION proc;
+
+	if (!sigrenderer) return -1;
+
+	proc = sigrenderer->desc->sigrenderer_get_position;
+	if (proc)
+		return (*proc)(sigrenderer->sigrenderer);
+	else
+		return sigrenderer->pos;
 }
 
 
--- /dev/null
+++ b/dumb/src/helpers/tarray.c
@@ -1,0 +1,176 @@
+#include "internal/tarray.h"
+
+#include <string.h>
+
+	/*
+	   Structures which contain the play times of each pattern and row combination in the song,
+	   not guaranteed to be valid for the whole song until the loop status is no longer zero.
+	   The initial count and restart count will both be zero on song start, then both will be
+	   incremented until the song loops. Restart count will be reset to zero on loop for all
+	   rows which have a time equal to or greater than the loop start point, so time keeping
+	   functions will know which timestamp the song is currently located at.
+
+	   Timestamp lists are guaranteed to be allocated in blocks of 16 timestamps at a time.
+	*/
+
+	/*
+	   We don't need full timekeeping because the player loop only wants the first play time
+	   of the loop start order/row. We also don't really want full timekeeping because it
+	   involves a lot of memory allocations, which is also slow.
+	*/
+
+#undef FULL_TIMEKEEPING
+
+typedef struct DUMB_IT_ROW_TIME
+{
+	unsigned int count, restart_count;
+#ifndef FULL_TIMEKEEPING
+	LONG_LONG first_time;
+#else
+	LONG_LONG * times;
+#endif
+} DUMB_IT_ROW_TIME;
+
+void * timekeeping_array_create(size_t size)
+{
+	size_t * _size = (size_t *) calloc( 1, sizeof(size_t) + sizeof(DUMB_IT_ROW_TIME) * size );
+	if ( _size ) {
+		*_size = size;
+	}
+	return _size;
+}
+
+void timekeeping_array_destroy(void * array)
+{
+	size_t i;
+	size_t * size = (size_t *) array;
+	DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1);
+
+#ifdef FULL_TIMEKEEPING
+	for (i = 0; i < *size; i++) {
+		if (s[i].times) free(s[i].times);
+	}
+#endif
+
+	free(size);
+}
+
+void * timekeeping_array_dup(void * array)
+{
+	size_t i;
+	size_t * size = (size_t *) array;
+	DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1);
+	size_t * new_size = (size_t *) calloc( 1, sizeof(size_t) + sizeof(DUMB_IT_ROW_TIME) * *size );
+	if ( new_size ) {
+		DUMB_IT_ROW_TIME * new_s = (DUMB_IT_ROW_TIME *)(new_size + 1);
+
+		*new_size = *size;
+
+		for (i = 0; i < *size; i++) {
+			new_s[i].count = s[i].count;
+			new_s[i].restart_count = s[i].restart_count;
+
+#ifndef FULL_TIMEKEEPING
+			new_s[i].first_time = s[i].first_time;
+#else
+			if ( s[i].times ) {
+				size_t time_count = ( s[i].count + 15 ) & ~15;
+				new_s[i].times = (LONG_LONG *) malloc( sizeof(LONG_LONG) * time_count );
+				if ( new_s[i].times == (void *)0 ) {
+					timekeeping_array_destroy( new_size );
+					return (void *) 0;
+				}
+				memcpy( new_s[i].times, s[i].times, sizeof(LONG_LONG) * s[i].count );
+			}
+#endif
+		}
+	}
+
+	return new_size;
+}
+
+void timekeeping_array_reset(void * array, size_t loop_start)
+{
+	size_t i;
+	size_t * size = (size_t *) array;
+	DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1);
+
+	DUMB_IT_ROW_TIME * s_loop_start = s + loop_start;
+	LONG_LONG loop_start_time;
+
+	if ( loop_start >= *size || s_loop_start->count < 1 ) return;
+
+#ifndef FULL_TIMEKEEPING
+	loop_start_time = s_loop_start->first_time;
+#else
+	loop_start_time = s_loop_start->times[0];
+#endif
+
+	for ( i = 0; i < *size; i++ ) {
+#ifndef FULL_TIMEKEEPING
+		if ( s[i].count && s[i].first_time >= loop_start_time ) {
+#else
+		if ( s[i].count && s[i].times[0] >= loop_start_time ) {
+#endif
+			s[i].restart_count = 0;
+		}
+	}
+}
+
+void timekeeping_array_push(void * array, size_t index, LONG_LONG time)
+{
+	size_t i;
+	size_t * size = (size_t *) array;
+	DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1);
+	size_t time_count;
+
+	if (index >= *size) return;
+
+#ifndef FULL_TIMEKEEPING
+	if ( !s[index].count++ )
+		s[index].first_time = time;
+#else
+	time_count = ( s[index].count + 16 ) & ~15;
+
+	s[index].times = (LONG_LONG *) realloc( s[index].times, sizeof(LONG_LONG) * time_count );
+
+	s[index].times[s[index].count++] = time;
+#endif
+}
+
+void timekeeping_array_bump(void * array, size_t index)
+{
+	size_t i;
+	size_t * size = (size_t *) array;
+	DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1);
+
+	if (index >= *size) return;
+
+	s[index].restart_count++;
+}
+
+unsigned int timekeeping_array_get_count(void * array, size_t index)
+{
+	size_t i;
+	size_t * size = (size_t *) array;
+	DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1);
+
+	if (index >= *size) return 0;
+
+	return s[index].count;
+}
+
+LONG_LONG timekeeping_array_get_item(void * array, size_t index)
+{
+	size_t i;
+	size_t * size = (size_t *) array;
+	DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1);
+
+	if (index >= *size || s[index].restart_count >= s[index].count) return 0;
+
+#ifndef FULL_TIMEKEEPING
+	return s[index].first_time;
+#else
+	return s[index].times[s[index].restart_count];
+#endif
+}
--- a/dumb/src/it/itrender.c
+++ b/dumb/src/it/itrender.c
@@ -379,6 +379,8 @@
 
 #ifdef BIT_ARRAY_BULLSHIT
 	dst->played = bit_array_dup(src->played);
+
+	dst->row_timekeeper = timekeeping_array_dup(src->row_timekeeper);
 #endif
 
 	dst->gvz_time = src->gvz_time;
@@ -2196,6 +2198,9 @@
 					bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row);
 #endif
 					sigrenderer->speed = 0;
+#ifdef BIT_ARRAY_BULLSHIT
+					sigrenderer->looped = 1;
+#endif
 					if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data))
 						return 1;
 				}
@@ -4235,6 +4240,10 @@
 				*/
 #endif
 				bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row);
+				if (sigrenderer->looped == 0) {
+					timekeeping_array_push(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row, sigrenderer->time_played);
+				}
+				timekeeping_array_bump(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row);
 				{
 					int n;
 					for (n = 0; n < DUMB_IT_N_CHANNELS; n++)
@@ -4349,6 +4358,9 @@
 					&& bit_array_test(sigrenderer->played, sigrenderer->processorder * 256 + sigrenderer->processrow)
 #endif
 					) {
+#ifdef BIT_ARRAY_BULLSHIT
+					sigrenderer->looped = 1;
+#endif
 					if (sigrenderer->callbacks->loop) {
 						if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data))
 							return 1;
@@ -4444,6 +4456,9 @@
 			sigrenderer->gvz_time += (int)(t >> 16);
 			sigrenderer->gvz_sub_time = (int)t & 65535;
 			if (sigrenderer->gvz_time >= 65536 * 12) {
+#ifdef BIT_ARRAY_BULLSHIT
+				sigrenderer->looped = 1;
+#endif
 				if ((*sigrenderer->callbacks->global_volume_zero)(sigrenderer->callbacks->global_volume_zero_data))
 					return 1;
 			}
@@ -5575,6 +5590,10 @@
 
 #ifdef BIT_ARRAY_BULLSHIT
 	sigrenderer->played = bit_array_create(sigdata->n_orders * 256);
+
+	sigrenderer->looped = 0;
+	sigrenderer->time_played = 0;
+	sigrenderer->row_timekeeper = timekeeping_array_create(sigdata->n_orders * 256);
 #endif
 
 	sigrenderer->gvz_time = 0;
@@ -5758,7 +5777,7 @@
 	long pos;
 	int dt;
 	long todo;
-	LONG_LONG t;
+	LONG_LONG time_left, t;
 
 	if (sigrenderer->order < 0) return 0; // problematic
 
@@ -5771,7 +5790,8 @@
 	if (!samples) volume = 0;
 
 	for (;;) {
-		todo = (long)((((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left) / dt);
+		time_left = ((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left;
+		todo = (long)(time_left / dt);
 
 		if (todo >= size)
 			break;
@@ -5785,11 +5805,25 @@
 		sigrenderer->sub_time_left = (long)t & 65535;
 		sigrenderer->time_left += (long)(t >> 16);
 
+#ifdef BIT_ARRAY_BULLSHIT
+		sigrenderer->time_played += time_left;
+#endif
+
 		if (process_tick(sigrenderer)) {
 			sigrenderer->order = -1;
 			sigrenderer->row = -1;
 			return pos;
 		}
+
+#ifdef BIT_ARRAY_BULLSHIT
+		if (sigrenderer->looped == 1) {
+			sigrenderer->looped = -1;
+			size = 0;
+			timekeeping_array_reset(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row);
+			sigrenderer->time_played = timekeeping_array_get_item(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row);
+			break;
+		}
+#endif
 	}
 
 	render(sigrenderer, volume, delta, pos, size, samples);
@@ -5800,6 +5834,10 @@
 	sigrenderer->sub_time_left = (long)t & 65535;
 	sigrenderer->time_left += (long)(t >> 16);
 
+#ifdef BIT_ARRAY_BULLSHIT
+	sigrenderer->time_played += (LONG_LONG)size * dt;
+#endif
+
 	if (samples)
 		dumb_remove_clicks_array(sigrenderer->n_channels, sigrenderer->click_remover, samples, pos, 512.0f / delta);
 
@@ -5843,6 +5881,8 @@
 
 #ifdef BIT_ARRAY_BULLSHIT
 		bit_array_destroy(sigrenderer->played);
+
+		timekeeping_array_destroy(sigrenderer->row_timekeeper);
 #endif
 
 		free(vsigrenderer);
@@ -5851,6 +5891,17 @@
 
 
 
+#ifdef BIT_ARRAY_BULLSHIT
+static long it_sigrenderer_get_position(sigrenderer_t *vsigrenderer)
+{
+	DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer;
+
+	return sigrenderer->time_played >> 16;
+}
+#endif
+
+
+
 DUH_SIGTYPE_DESC _dumb_sigtype_it = {
 	SIGTYPE_IT,
 	NULL,
@@ -5858,6 +5909,11 @@
 	NULL,
 	&it_sigrenderer_get_samples,
 	&it_sigrenderer_get_current_sample,
+#ifdef BIT_ARRAY_BULLSHIT
+	&it_sigrenderer_get_position,
+#else
+	NULL,
+#endif
 	&_dumb_it_end_sigrenderer,
 	&_dumb_it_unload_sigdata
 };
--- a/dumb/vc6/dumb/dumb.vcxproj
+++ b/dumb/vc6/dumb/dumb.vcxproj
@@ -116,6 +116,7 @@
     <ClCompile Include="..\..\src\helpers\sampbuf.c" />
     <ClCompile Include="..\..\src\helpers\silence.c" />
     <ClCompile Include="..\..\src\helpers\stdfile.c" />
+    <ClCompile Include="..\..\src\helpers\tarray.c" />
     <ClCompile Include="..\..\src\it\itmisc.c" />
     <ClCompile Include="..\..\src\it\itorder.c" />
     <ClCompile Include="..\..\src\it\itrender.c" />
@@ -207,6 +208,7 @@
     <ClInclude Include="..\..\include\internal\lpc.h" />
     <ClInclude Include="..\..\include\internal\riff.h" />
     <ClInclude Include="..\..\include\internal\stack_alloc.h" />
+    <ClInclude Include="..\..\include\internal\tarray.h" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
--- a/dumb/vc6/dumb/dumb.vcxproj.filters
+++ b/dumb/vc6/dumb/dumb.vcxproj.filters
@@ -285,6 +285,9 @@
     <ClCompile Include="..\..\src\helpers\lanczos_resampler.c">
       <Filter>src\helpers</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\helpers\tarray.c">
+      <Filter>src\helpers</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\include\dumb.h">
@@ -315,6 +318,9 @@
       <Filter>include\internal</Filter>
     </ClInclude>
     <ClInclude Include="..\..\include\internal\lanczos_resampler.h">
+      <Filter>include\internal</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\internal\tarray.h">
       <Filter>include\internal</Filter>
     </ClInclude>
   </ItemGroup>