From de5588751dfb3764949c413db7896dbc7126128b Mon Sep 17 00:00:00 2001 From: Wei-ju Wu Date: Tue, 29 Nov 2016 15:40:07 -0800 Subject: [PATCH] added sound effects playing --- hardware/Makefile | 7 ++- hardware/common.c | 10 ++-- hardware/laser.raw8 | Bin 0 -> 25074 bytes hardware/playfield1.c | 6 ++- hardware/soundfx.c | 119 ++++++++++++++++++++++++++++++++++++++++++ utils/png2image.py | 2 + 6 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 hardware/laser.raw8 create mode 100644 hardware/soundfx.c diff --git a/hardware/Makefile b/hardware/Makefile index 693f689..21a7d05 100644 --- a/hardware/Makefile +++ b/hardware/Makefile @@ -1,10 +1,10 @@ CC=vc +kick13 CFLAGS=-c99 -I$(NDK_INC) -DDEBUG -O2 -all: startup sprites playfield1 scoopex1 +all: startup sprites playfield1 scoopex1 soundfx clean: - rm -f *.o startup sprites playfield1 scoopex2 + rm -f *.o startup sprites playfield1 scoopex2 soundfx startup: startup.o common.o $(CC) $(CFLAGS) $^ -lamiga -lauto -o $@ @@ -18,3 +18,6 @@ playfield1: playfield1.o common.o scoopex1: scoopex1.c $(CC) $(CFLAGS) $^ -lamiga -lauto -o $@ +soundfx: soundfx.o common.o + $(CC) $(CFLAGS) $^ -lamiga -lauto -o $@ + diff --git a/hardware/common.c b/hardware/common.c index 34b51cb..2dc0b06 100644 --- a/hardware/common.c +++ b/hardware/common.c @@ -17,7 +17,7 @@ static ULONG oldresolution; struct ExecBase **exec_base_ptr = (struct ExecBase **) (4L); -static void ApplySpriteFix(void) +static void apply_sprite_fix(void) { if (wbscreen = LockPubScreen(WB_SCREEN_NAME)) { struct TagItem video_control_tags[] = { @@ -36,7 +36,7 @@ static void ApplySpriteFix(void) } } -static void UnapplySpriteFix(void) +static void unapply_sprite_fix(void) { if (wbscreen) { struct TagItem video_control_tags[] = { @@ -60,11 +60,9 @@ BOOL init_display(void) // Kickstart > 3.0: fix sprite bug if (lib_version >= 39) { - ApplySpriteFix(); + apply_sprite_fix(); is_pal = (((struct GfxBase *) GfxBase)->DisplayFlags & PAL) == PAL; } else { - // Note: FS-UAE reports 0 this, so essentially, there is no information - // for 1.x int vblank_freq = (*exec_base_ptr)->VBlankFrequency; printf("Gfx Lib version: %u, Vertical Blank Frequency: %d\n", lib_version, vblank_freq); is_pal = vblank_freq == VFREQ_PAL; @@ -76,7 +74,7 @@ void reset_display(void) { struct View *current_view = ((struct GfxBase *) GfxBase)->ActiView; UWORD lib_version = GfxBase->LibNode.lib_Version; - if (lib_version >= 39) UnapplySpriteFix(); + if (lib_version >= 39) unapply_sprite_fix(); LoadView(current_view); WaitTOF(); WaitTOF(); diff --git a/hardware/laser.raw8 b/hardware/laser.raw8 new file mode 100644 index 0000000000000000000000000000000000000000..330ba4f43689f8eddca0661d2998d16cf6a7b8b6 GIT binary patch literal 25074 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 literal 0 HcmV?d00001 diff --git a/hardware/playfield1.c b/hardware/playfield1.c index 77d52fd..24fec08 100644 --- a/hardware/playfield1.c +++ b/hardware/playfield1.c @@ -5,7 +5,7 @@ #include "common.h" #define NUM_BITPLANES 5 -#define INTERLEAVED +//#define INTERLEAVED #ifdef USE_PAL @@ -57,6 +57,7 @@ static UWORD __chip coplist[] = { COP_MOVE(BPL4PTL, 0), COP_MOVE(BPL5PTH, 0), COP_MOVE(BPL5PTL, 0), + COP_MOVE(COLOR00, 0), COP_WAIT_END, COP_WAIT_END }; @@ -87,6 +88,7 @@ int main(int argc, char **argv) custom.diwstrt = DIWSTRT_VALUE; custom.diwstop = DIWSTOP_VALUE; + // should be done in copper list, because workbench copper list could overwrite it for (int i = 0; i < NUM_COLORS; i++) { custom.color[i] = image_colors[i]; } @@ -95,6 +97,7 @@ int main(int argc, char **argv) #ifdef INTERLEAVED // in interleaved mode, each plane's *rows* are organized // consecutively one after another + printf("interleaved\n"); for (int i = 0; i < NUM_BITPLANES; i++) { ULONG addr = (ULONG) &(image_data[i * 20]); coplist[i * 4 + 1] = (addr >> 16) & 0xffff; @@ -103,6 +106,7 @@ int main(int argc, char **argv) #else // in non-interleaved mode, each plane's data is placed // consecutively one after another + printf("non-interleaved\n"); for (int i = 0; i < NUM_BITPLANES; i++) { ULONG addr = (ULONG) &(image_data[i * 20 * NUM_RASTER_LINES]); coplist[i * 4 + 1] = (addr >> 16) & 0xffff; diff --git a/hardware/soundfx.c b/hardware/soundfx.c new file mode 100644 index 0000000..a9de82f --- /dev/null +++ b/hardware/soundfx.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include + +#include "common.h" + +/* + * This is essentially the code for startup.asm from + * "Amiga Demo Coders Reference Manual" translated to C. + * + * This allows a program which operates directly on the hardware + * to play nice with the operating system and ensure a clean exit + * to Workbench. + * A great starting point to use as a template for demos and games. + */ +extern struct Custom custom; +char VERSION_STRING[] = "\0$VER: startup 1.0 (21.09.2016)\0"; + +static UWORD __chip coplist_pal[] = { + COP_MOVE(BPLCON0, BPLCON0_COMPOSITE_COLOR), + COP_MOVE(COLOR00, 0x000), + 0x8107, 0xfffe, // wait for $8107,$fffe + COP_MOVE(COLOR00, 0xf00), + 0xd607, 0xfffe, // wait for $d607,$fffe + COP_MOVE(COLOR00, 0xff0), + COP_WAIT_END, + COP_WAIT_END +}; +static UWORD __chip coplist_ntsc[] = { + COP_MOVE(BPLCON0, BPLCON0_COMPOSITE_COLOR), + COP_MOVE(COLOR00, 0x000), + 0x6e07, 0xfffe, // wait for $6e07,$fffe + COP_MOVE(COLOR00, 0xf00), + 0xb007, 0xfffe, // wait for $b007,$fffe + COP_MOVE(COLOR00, 0xff0), + COP_WAIT_END, + COP_WAIT_END +}; + +// Assuming a 14k sample rate +#define SOUND_DATA_SIZE (25074) +// 0-64 +#define MAX_VOLUME (64) +#define SAMPLE_PERIOD (256) + +static UBYTE __chip sound_data[SOUND_DATA_SIZE]; + +void start_channel(int channel) +{ + int channel_bits = 0; + switch (channel) { + case 0: + channel_bits = DMAF_AUD0; + break; + case 1: + channel_bits = DMAF_AUD1; + break; + case 2: + channel_bits = DMAF_AUD2; + break; + case 3: + channel_bits = DMAF_AUD3; + break; + default: + break; + } + custom.dmacon = (DMAF_SETCLR | channel_bits | DMAF_MASTER); +} + +void stop_channel(int channel) +{ + int channel_bits = 0; + switch (channel) { + case 0: + channel_bits = DMAF_AUD0; + break; + case 1: + channel_bits = DMAF_AUD1; + break; + case 2: + channel_bits = DMAF_AUD2; + break; + case 3: + channel_bits = DMAF_AUD3; + break; + default: + break; + } + custom.dmacon = channel_bits; +} + +int main(int argc, char **argv) +{ + struct Task *current_task = FindTask(NULL); + BYTE old_prio = SetTaskPri(current_task, TASK_PRIORITY); + BOOL is_pal = init_display(); + FILE *fp = fopen("laser.raw8", "rb"); + int bytes_read = fread(sound_data, sizeof(UBYTE), SOUND_DATA_SIZE, fp); + custom.aud[0].ac_ptr = (UWORD *) sound_data; + custom.aud[0].ac_len = SOUND_DATA_SIZE / 2; + custom.aud[0].ac_vol = MAX_VOLUME; + custom.aud[0].ac_per = SAMPLE_PERIOD; + start_channel(0); + + + custom.cop1lc = is_pal ? (ULONG) coplist_pal : (ULONG) coplist_ntsc; + + // strobe the COPJMP1 register to make sure the system is using + // copper list 1 (I found out that leaving this out can lead to + // strange effects on an emulated 4000 system) + custom.copjmp1 = 1; + + waitmouse(); + stop_channel(0); + + reset_display(); + return 0; +} diff --git a/utils/png2image.py b/utils/png2image.py index 4072de5..cbe0e08 100755 --- a/utils/png2image.py +++ b/utils/png2image.py @@ -75,6 +75,8 @@ def write_amiga_image(im, outfile, img_name, use_intuition, interleaved, verbose # colors is a list of 3-integer lists ([[r1, g1, b1], ...]) colors = [i for i in chunks([b for b in im.palette.tobytes()], 3)] depth = round(math.log(len(colors), 2)) + if depth == 0: + raise Exception("images with only 1 color can't be handled") # fill the missing colors with black entries num_missing_colors = 2 ** depth - len(colors) colors += [[0, 0, 0]] * num_missing_colors