diff --git a/README.md b/README.md index b67d3cb..4e36f0a 100644 --- a/README.md +++ b/README.md @@ -56,8 +56,10 @@ New features since fork - **Test-driven development**. Given that 4klang was already a mature project, the first thing actually implemented was a set of regression tests to avoid breaking everything beyond any hope of repair. Done, using CTest. - - **New units**. Bit-crusher, gain, inverse gain, clip. As always, if you - don't use them, they won't be compiled into the code. + - **New units**. Bit-crusher, gain, inverse gain, clip, modulate bpm + (proper triplets!)... As always, if you don't use them, they won't be + compiled into the code. + - **Pattern length does not have to be a power of 2**. Future goals ------------ @@ -66,8 +68,8 @@ Future goals on CMake and compiles on Windows. Cross-platform NASM/YASM macros have been drafted and remain to be tested. Once the project is more mature, I will try compiling on other platforms. - - **Even more opcodes**. At least: compressor (with side-chaining), change - bpm. Maybe also equalizer. + - **Even more opcodes**. At least: compressor (with side-chaining). Maybe + also equalizer. - **Support for 64-bit targets**. - **Browser-based GUI and MIDI instrument**. Modern browsers support WebMIDI, WebAudio and, most importantly, they are cross-platform and come installed diff --git a/src/opcodes/flowcontrol.asm b/src/opcodes/flowcontrol.asm index 1275837..e31acab 100644 --- a/src/opcodes/flowcontrol.asm +++ b/src/opcodes/flowcontrol.asm @@ -49,6 +49,33 @@ EXPORT MANGLE_FUNC(su_op_advance,0) ; Stack: addr voice wrkptr valptr comptr %endif +;------------------------------------------------------------------------------- +; SPEED tick +;------------------------------------------------------------------------------- +%if SPEED_ID > -1 + +SECT_TEXT(suspeed) + +EXPORT MANGLE_FUNC(su_op_speed,0) + fsub dword [c_0_5] ; s-.5 + fadd st0, st0 ; 2*s-1 + fmul dword [c_bpmscale] ; (2*s-1)*64/24, let's call this p from now on + call MANGLE_FUNC(su_power,0) ; 2^p, this is how many ticks we should be taking + fld1 ; 1 2^p + fsubp st1, st0 ; 2^p-1, the player is advancing 1 tick by its own + fadd dword [WRK+su_speed_wrk.remainder] ; t+2^p-1, t is the remainder from previous rounds as ticks have to be rounded to 1 + push eax + fist dword [esp] ; Main stack: k=int(t+2^p-1) + fisub dword [esp] ; t+2^p-1-k, the remainder + pop eax + add dword [esp+24], eax ; add the whole ticks to song tick count, [esp+24] is the current tick in the row + fstp dword [WRK+su_speed_wrk.remainder] ; save the remainder for future + ret + +SECT_DATA(suconst) + c_bpmscale dd 2.666666666666 ; 64/24, 24 values will be double speed, so you can go from ~ 1/2.5 speed to 2.5x speed + +%endif ;------------------------------------------------------------------------------- ; Constants diff --git a/src/opcodes/flowcontrol.inc b/src/opcodes/flowcontrol.inc index e69de29..21d0552 100644 --- a/src/opcodes/flowcontrol.inc +++ b/src/opcodes/flowcontrol.inc @@ -0,0 +1,21 @@ +;------------------------------------------------------------------------------- +; SPEED related defines +;------------------------------------------------------------------------------- +%assign SPEED_ID -1 +%macro USE_SPEED 0 + %if SPEED_ID == -1 + %assign SPEED_ID CUR_ID + %assign CUR_ID CUR_ID + 2 + %xdefine OPCODES OPCODES MANGLE_FUNC(su_op_speed,0), + %xdefine NUMPARAMS NUMPARAMS 0, + %endif +%endmacro + +%macro SU_SPEED 0 + USE_SPEED + %xdefine CMDS CMDS SPEED_ID, ; there is no stereo variant I can think of +%endmacro + +struc su_speed_wrk + .remainder resd 1 +endstruc \ No newline at end of file diff --git a/src/sointu.inc b/src/sointu.inc index 2b58425..a457f2e 100644 --- a/src/sointu.inc +++ b/src/sointu.inc @@ -127,6 +127,7 @@ %define MONO 0 %define STEREO 1 +%include "opcodes/flowcontrol.inc" %include "opcodes/arithmetic.inc" %include "opcodes/effects.inc" %include "opcodes/sources.inc" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0156329..ab65b8a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -123,3 +123,4 @@ target_compile_definitions(test_envelope_16bit PUBLIC SU_USE_16BIT_OUTPUT) regression_test(test_polyphony "ENVELOPE;VCO_SINE") regression_test(test_chords "ENVELOPE;VCO_SINE") +regression_test(test_speed "ENVELOPE;VCO_SINE") diff --git a/tests/expected_output/test_speed.raw b/tests/expected_output/test_speed.raw new file mode 100644 index 0000000..b6ca883 Binary files /dev/null and b/tests/expected_output/test_speed.raw differ diff --git a/tests/test_speed.asm b/tests/test_speed.asm new file mode 100644 index 0000000..84d1958 --- /dev/null +++ b/tests/test_speed.asm @@ -0,0 +1,37 @@ +%define BPM 100 +%define USE_SECTIONS + +%include "../src/sointu.inc" + +; warning: crashes ahead. Now that the bpm could be changed and even modulated by other +; signals, there is no easy way to figure out how many ticks your song is. Either +; allocate some extra memory of the output just in case or simulate exactly how many +; samples are outputted. Here the triplets are slightly faster than the original so +; they fit the default MAX_TICKS that is calculated using the simple bpm assumption. +SU_BEGIN_PATTERNS + PATTERN 64, 0, 64, 64, 64, 0, 64, 64, 64, 0, 64, 64, 65, 0, 65, 65, + PATTERN 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ; 4-rows + PATTERN 78, 0, 54, 0, 78, 0, 54, 0, 78, 0, 54, 0, 78, 0, 54, 0, ; triplets +SU_END_PATTERNS + +SU_BEGIN_TRACKS + TRACK VOICES(1),0,0 + TRACK VOICES(1),1,2 +SU_END_TRACKS + +SU_BEGIN_PATCH + SU_BEGIN_INSTRUMENT VOICES(1) ; Instrument0 + SU_ENVELOPE MONO,ATTAC(64),DECAY(64),SUSTAIN(0),RELEASE(64),GAIN(128) + SU_ENVELOPE MONO,ATTAC(64),DECAY(64),SUSTAIN(0),RELEASE(64),GAIN(128) + SU_OSCILLAT MONO,TRANSPOSE(64),DETUNE(32),PHASE(0),COLOR(96),SHAPE(64),GAIN(128), FLAGS(TRISAW) + SU_OSCILLAT MONO,TRANSPOSE(72),DETUNE(64),PHASE(64),COLOR(64),SHAPE(96),GAIN(128), FLAGS(TRISAW) + SU_MULP STEREO + SU_OUT STEREO,GAIN(128) + SU_END_INSTRUMENT + SU_BEGIN_INSTRUMENT VOICES(1) ; Speed changer + SU_LOADNOTE MONO + SU_SPEED + SU_END_INSTRUMENT +SU_END_PATCH + +%include "../src/sointu.asm"