diff --git a/CHANGELOG.txt b/CHANGELOG.txt
new file mode 100644
index 0000000..dfda3b5
--- /dev/null
+++ b/CHANGELOG.txt
@@ -0,0 +1,22 @@
+ToneMatrix changelog:
+
+0.9:
+ - Added save song and load song functions
+ - Changed from 12 grids to 16 grids
+ - Removed "Quit" and "Stop playing" menu options
+
+0.8:
+ - Added 12 grid sequencer
+ - Added loop all function
+ - Added cut, copy, and paste functions
+
+0.7:
+ - Added mute row function
+ - Added solo row function
+
+0.6:
+ - Fixed "trail" bug
+
+0.5:
+ - Initial release
+ - Clear grid, tempo, and stop functions
diff --git a/LICENSE b/LICENSE.txt
similarity index 98%
rename from LICENSE
rename to LICENSE.txt
index 22fbe5d..d159169 100644
--- a/LICENSE
+++ b/LICENSE.txt
@@ -1,7 +1,7 @@
-GNU GENERAL PUBLIC LICENSE
+ GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -290,8 +290,8 @@ to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
- {description}
- Copyright (C) {year} {fullname}
+
+ Copyright (C)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -329,11 +329,11 @@ necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
- {signature of Ty Coon}, 1 April 1989
+ , 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
\ No newline at end of file
+Public License instead of this License.
diff --git a/README.md b/README.md
deleted file mode 100644
index cb3d1b8..0000000
--- a/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-ToneMatrix
-==========
-
-Drum machine for the PSP.
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..3593cd4
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,53 @@
+ToneMatrix is a 16-step drum machine. The Y (vertical) axis
+represents eight different sounds, and more than one can be
+played at a time. The X (horizontal) axis represents one
+4/4 measure in sixteenth-notes.
+
+Select a node on the grid with the D-pad and the X button,
+and that sound will be played in time. Press O to stop/
+resume playing. Press [] (square) to mute the row the cursor
+is on. Press /\ (triangle) to mute every row except the
+row the cursor is on. You can mute more than one row at
+once.
+
+Press START to use the menu, and use the D-pad to select a
+menu item or change a value (like the tempo), and X (cross)
+to confirm your choice. Press SELECT for help.
+
+You can play up to twelve measures in a row by using the R
+and L triggers, or by switching the "grid" value in the
+START mneu. If "loop all" is on, then the measures will
+play one after another. If it is off, it will only repeat
+one measure. It is recommended that you stop playback
+before editing another grid.
+
+To install:
+If you have custom firmware or a homebrew-enabled PSP,
+place the "unsigned" folder into the /PSP/GAME folder on
+your Memory Stick. If you have official firmware on your PSP
+(or if you have a PSPgo), place the "signed" folder into the
+/PSP/GAME folder on your Memory Stick.
+
+-
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public
+License along with this program; if not, visit
+http://www.gnu.org/licenses/gpl-2.0.txt
+
+If you want to redistribute any modifications you make to
+the program, please credit yourself in the modified source
+file(s) under my name as,
+"Copyright (c) "
+
+Feel free to contact me at babkock@gmail.com if you have any
+questions or concerns. Thanks for playing :)
diff --git a/screen1.png b/screen1.png
new file mode 100644
index 0000000..873bd8a
Binary files /dev/null and b/screen1.png differ
diff --git a/screen2.png b/screen2.png
new file mode 100644
index 0000000..482d3f9
Binary files /dev/null and b/screen2.png differ
diff --git a/screen3.png b/screen3.png
new file mode 100644
index 0000000..2307974
Binary files /dev/null and b/screen3.png differ
diff --git a/screen4.png b/screen4.png
new file mode 100644
index 0000000..35d474f
Binary files /dev/null and b/screen4.png differ
diff --git a/screen5.png b/screen5.png
new file mode 100644
index 0000000..5d62202
Binary files /dev/null and b/screen5.png differ
diff --git a/screen6.png b/screen6.png
new file mode 100644
index 0000000..3c1629f
Binary files /dev/null and b/screen6.png differ
diff --git a/signed/EBOOT.PBP b/signed/EBOOT.PBP
new file mode 100644
index 0000000..fccb0d6
Binary files /dev/null and b/signed/EBOOT.PBP differ
diff --git a/signed/select.png b/signed/select.png
new file mode 100644
index 0000000..10b8cc2
Binary files /dev/null and b/signed/select.png differ
diff --git a/signed/songs/SAVED-SONGS-GO-HERE.txt b/signed/songs/SAVED-SONGS-GO-HERE.txt
new file mode 100644
index 0000000..721a952
--- /dev/null
+++ b/signed/songs/SAVED-SONGS-GO-HERE.txt
@@ -0,0 +1,13 @@
+ToneMatrix will only load songs if the file name is one of the following:
+
+song0.tms
+song1.tms
+song2.tms
+song3.tms
+song4.tms
+song5.tms
+song6.tms
+song7.tms
+song8.tms
+song9.tms
+
diff --git a/signed/sounds/tone0.wav b/signed/sounds/tone0.wav
new file mode 100644
index 0000000..fa7c568
Binary files /dev/null and b/signed/sounds/tone0.wav differ
diff --git a/signed/sounds/tone1.wav b/signed/sounds/tone1.wav
new file mode 100644
index 0000000..1637e0e
Binary files /dev/null and b/signed/sounds/tone1.wav differ
diff --git a/signed/sounds/tone2.wav b/signed/sounds/tone2.wav
new file mode 100644
index 0000000..86cacb4
Binary files /dev/null and b/signed/sounds/tone2.wav differ
diff --git a/signed/sounds/tone3.wav b/signed/sounds/tone3.wav
new file mode 100644
index 0000000..c023c76
Binary files /dev/null and b/signed/sounds/tone3.wav differ
diff --git a/signed/sounds/tone4.wav b/signed/sounds/tone4.wav
new file mode 100644
index 0000000..119868d
Binary files /dev/null and b/signed/sounds/tone4.wav differ
diff --git a/signed/sounds/tone5.wav b/signed/sounds/tone5.wav
new file mode 100644
index 0000000..b3b5ed6
Binary files /dev/null and b/signed/sounds/tone5.wav differ
diff --git a/signed/sounds/tone6.wav b/signed/sounds/tone6.wav
new file mode 100644
index 0000000..2a572b6
Binary files /dev/null and b/signed/sounds/tone6.wav differ
diff --git a/signed/sounds/tone7.wav b/signed/sounds/tone7.wav
new file mode 100644
index 0000000..f8f8fca
Binary files /dev/null and b/signed/sounds/tone7.wav differ
diff --git a/src/ICON0.png b/src/ICON0.png
new file mode 100644
index 0000000..03c6b6a
Binary files /dev/null and b/src/ICON0.png differ
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..f1a710c
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,17 @@
+TARGET = tm
+OBJS = main.o draw.o file.o menu.o misc.o sound.o
+
+CFLAGS = -G4 -Wall -O2
+CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
+ASFLAGS = $(CFLAGS)
+
+BUILD_PRX = 1
+
+LIBS = -losl -lc -ljpeg -lpng -lz -lpspsdk -lpspctrl -lpspumd -lpsprtc -lpsppower -lpspgu -lpspgum -lpsphprm -lpspaudiolib -lpspaudio -lm
+
+EXTRA_TARGETS = EBOOT.PBP
+PSP_EBOOT_TITLE = ToneMatrix 0.9
+PSP_EBOOT_ICON = ICON0.PNG
+
+PSPSDK=$(shell psp-config --pspsdk-path)
+include $(PSPSDK)/lib/build.mak
diff --git a/src/draw.c b/src/draw.c
new file mode 100644
index 0000000..817d29f
--- /dev/null
+++ b/src/draw.c
@@ -0,0 +1,58 @@
+/* ToneMatrix - draw.c
+ * Copyright (c) 2012 Tanner Babcock */
+#include
+#include "main.h"
+
+// GOLD
+void tmDrawGrid(void) {
+ int x, y;
+ oslDrawFillRect(0, 15, 480, 255, RGBA(0, 0, 0, 255));
+ for (x = 1; x < (MAX_X+1); x++) {
+ for (y = 1; y < (MAX_Y+1); y++) {
+ if (tmMuteEmpty(mute) || solo == 0) {
+ if (data[current].grid[x-1][y-1] == OFF)
+ oslDrawFillRect(x1form, y1form, x2form, y2form, (x == col) ? HOFF : OFFC);
+ else if (data[current].grid[x-1][y-1] == ON)
+ oslDrawFillRect(x1form, y1form, x2form, y2form, (x == col) ? HON : ONC);
+ else if (data[current].grid[x-1][y-1] == CURSOFF) {
+ oslDrawFillRect(x1form, y1form, x2form, y2form, (x == col) ? HOFF : OFFC);
+ oslDrawImageXY(select, x1form, y1form);
+ }
+ else if (data[current].grid[x-1][y-1] == CURSON) {
+ oslDrawFillRect(x1form, y1form, x2form, y2form, (x == col) ? HON : ONC);
+ oslDrawImageXY(select, x1form, y1form);
+ }
+ }
+ if (mute[y-1] && solo == 0) {
+ if (data[current].grid[x-1][y-1] == OFF)
+ oslDrawFillRect(x1form, y1form, x2form, y2form, (x == col) ? MHOFF : MOFF);
+ else if (data[current].grid[x-1][y-1] == ON)
+ oslDrawFillRect(x1form, y1form, x2form, y2form, (x == col) ? MHON : MON);
+ else if (data[current].grid[x-1][y-1] == CURSOFF) {
+ oslDrawFillRect(x1form, y1form, x2form, y2form, (x == col) ? MHOFF : MOFF);
+ oslDrawImageXY(select, x1form, y1form);
+ }
+ else if (data[current].grid[x-1][y-1] == CURSON) {
+ oslDrawFillRect(x1form, y1form, x2form, y2form, (x == col) ? MHON : MON);
+ oslDrawImageXY(select, x1form, y1form);
+ }
+ }
+
+ if (solo == y && tmMuteEmpty(mute)) {
+ if (data[current].grid[x-1][y-1] == OFF)
+ oslDrawFillRect(x1form, y1form, x2form, y2form, (x == col) ? SHOFF : SOFF);
+ else if (data[current].grid[x-1][y-1] == ON)
+ oslDrawFillRect(x1form, y1form, x2form, y2form, (x == col) ? SHON : SON);
+ else if (data[current].grid[x-1][y-1] == CURSOFF) {
+ oslDrawFillRect(x1form, y1form, x2form, y2form, (x == col) ? SHOFF : SOFF);
+ oslDrawImageXY(select, x1form, y1form);
+ }
+ else if (data[current].grid[x-1][y-1] == CURSON) {
+ oslDrawFillRect(x1form, y1form, x2form, y2form, (x == col) ? SHON : SON);
+ oslDrawImageXY(select, x1form, y1form);
+ }
+ }
+ }
+ }
+ return;
+}
diff --git a/src/file.c b/src/file.c
new file mode 100644
index 0000000..8846012
--- /dev/null
+++ b/src/file.c
@@ -0,0 +1,158 @@
+/* ToneMatrix - file.c
+ * Copyright (c) 2012 Tanner Babcock */
+#include
+#include
+#include
+#include
+#include
+#include "main.h"
+
+void tmFileDialog(bool save) {
+ int x;
+ unsigned char option = 0;
+ bool empty[MAX_SAVE_SLOTS];
+ char strings[MAX_SAVE_SLOTS][50];
+ char mdate[MAX_SAVE_SLOTS][50];
+ char tmp[30];
+
+ SceIoStat a;
+ typedef struct {
+ int year, month, day;
+ int minute, hour;
+ } metadata;
+ metadata songMeta[MAX_SAVE_SLOTS];
+
+ current = 0;
+ for (x = 0; x < MAX_SAVE_SLOTS; x++) {
+ bzero(tmp, sizeof(tmp));
+ sprintf(tmp, "songs/song%d.tms", x);
+ sceIoGetstat(tmp, &a);
+ songMeta[x].year = a.st_mtime.year;
+ songMeta[x].month = a.st_mtime.month;
+ songMeta[x].day = a.st_mtime.day;
+ songMeta[x].minute = a.st_mtime.minute;
+ songMeta[x].hour = a.st_mtime.hour;
+ }
+ // I have to make a separate struct because accessing sm->tm_mday
+ // from the while (!osl_quit) fries the memory stick
+ // same reason why there's a bool empty[]
+
+ for (x = 0; x < MAX_SAVE_SLOTS; x++) {
+ bzero(tmp, sizeof(tmp));
+ sprintf(tmp, "songs/song%d.tms", x);
+ empty[x] = tmDoesFileExist(tmp);
+ }
+ while (!osl_quit) {
+ oslStartDrawing();
+ oslReadKeys();
+ tmDrawGrid();
+ oslDrawFillRect(0, 0, 480, 272, RGBA(0, 0, 0, 125));
+ if (save)
+ oslDrawString(10, 40, "Press X (cross) to save your song in the selected slot.");
+ else
+ oslDrawString(10, 40, "Press X (cross) to load a song from the selected slot.");
+ oslDrawString(10, 50, "Press O (circle) to cancel.");
+ for (x = 0; x < MAX_SAVE_SLOTS; x++) {
+ bzero(strings[x], sizeof(strings[x]));
+ bzero(tmp, sizeof(tmp));
+ sprintf(tmp, "song%d.tms", x);
+ sprintf(strings[x], "%s %s", (option == x) ? "->" : " ", (empty[x]) ? "EMPTY" : tmp);
+ sprintf(mdate[x], "%d/%d/%d %d:%d UTC", songMeta[x].month, songMeta[x].day, songMeta[x].year, songMeta[x].hour, songMeta[x].minute);
+ oslDrawString(10, (70+(x*10)), strings[x]);
+ if (!empty[x])
+ oslDrawString(200, (70+(x*10)), mdate[x]);
+ }
+ oslDrawString(10, 190, "Press /\\ (triangle) to delete the selected song slot.");
+ if ((osl_keys->pressed.up) && (option > 0))
+ option--;
+ if ((osl_keys->pressed.down) && (option < (MAX_SAVE_SLOTS-1)))
+ option++;
+ if (osl_keys->pressed.cross) {
+ if (empty[option]) {
+ if (save) {
+ tmWrite(option);
+ break;
+ }
+ }
+ else {
+ if (save) {
+ if (tmOverwriteWarning(option)) {
+ tmWrite(option);
+ break;
+ }
+ }
+ else {
+ tmRead(option);
+ break;
+ }
+ }
+ }
+ if (osl_keys->pressed.triangle) {
+ if (!empty[option]) {
+ if (tmDeleteWarning(option)) {
+ bzero(tmp, sizeof(tmp));
+ sprintf(tmp, "songs/song%d.tms", option);
+ remove(tmp);
+ empty[option] = TRUE;
+ }
+ }
+ }
+ if (osl_keys->pressed.circle)
+ break;
+ if (osl_keys->pressed.start)
+ break;
+ oslEndDrawing();
+ oslSyncFrame();
+ }
+ return;
+}
+
+bool tmDoesFileExist(const char *path) {
+ FILE *in = fopen(path, "r");
+ if (!in) {
+ fclose(in);
+ return TRUE;
+ }
+ else {
+ fclose(in);
+ return FALSE;
+ }
+}
+
+void tmRead(int slot) {
+ int x, y, z;
+ char f[25];
+ sprintf(f, "songs/song%d.tms", slot);
+ FILE *in = fopen(f, "r");
+ for (z = 0; z < MAX_GRIDS; z++) {
+ for (x = 0; x < MAX_X; x++) {
+ for (y = 0; y < MAX_Y; y++)
+ fscanf(in, "%c", &data[z].grid[x][y]);
+ fseek(in, 4, SEEK_CUR);
+ }
+ fseek(in, 5, SEEK_CUR);
+ }
+ fclose(in);
+ return;
+}
+
+void tmWrite(int slot) {
+ int x, y, z;
+ char f[25];
+ sprintf(f, "songs/song%d.tms", slot);
+ FILE *out = fopen(f, "r");
+ if (out)
+ remove(f);
+ fclose(out);
+ out = fopen(f, "w");
+ for (z = 0; z < MAX_GRIDS; z++) {
+ for (x = 0; x < MAX_X; x++) {
+ for (y = 0; y < MAX_Y; y++)
+ fprintf(out, "%c", data[z].grid[x][y]);
+ fprintf(out, "%c%c%c%c", 82, 42, 82, 22);
+ }
+ fprintf(out, "%c%c%c%c\a", 82, 42, 82, 22);
+ }
+ fclose(out);
+ return;
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..812f2a4
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,171 @@
+/* ToneMatrix - main.c
+ * Copyright (c) 2012 Tanner Babcock */
+#include
+#include
+#include
+#include "main.h"
+#define c data[current]
+PSP_MODULE_INFO("ToneMatrix", 0, 1, 1);
+PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
+PSP_HEAP_SIZE_KB(-2048);
+
+void tmMainLoop(void) {
+ int x = 0, y = 0, q, m;
+ int tmpcurrent = current;
+ clock_t start, cu, d;
+ start = sceKernelLibcClock();
+ oslClearScreen(RGBA(0, 0, 0, 255));
+ tmStart();
+ while (!osl_quit && !quit) {
+ oslStartDrawing();
+ oslReadKeys();
+ oslClearScreen(RGBA(0, 0, 0, 255));
+ cu = sceKernelLibcClock();
+ tmDrawGrid();
+ tmSwitchGrid(&x, &y);
+ if (current != tmpcurrent) {
+ if (c.grid[x][y] == ON)
+ c.grid[x][y] = CURSON;
+ if (c.grid[x][y] == OFF)
+ c.grid[x][y] = CURSOFF;
+ tmpcurrent = current;
+ }
+ /* if ((d = start - cu) <= 1250000)
+ oslDrawString(0, 300, msg); */
+ // grid[x][y] is where the cursor is
+ if (c.grid[x][y] == ON)
+ c.grid[x][y] = CURSON;
+ else if (c.grid[x][y] == OFF)
+ c.grid[x][y] = CURSOFF;
+ if (x > MAX_X || x < 0)
+ x = 0;
+ if (y > MAX_Y || y < 0)
+ y = 0;
+ if (osl_keys->pressed.up) {
+ if (y > 0) {
+ if (c.grid[x][y] == CURSON)
+ c.grid[x][y] = ON;
+ else if (c.grid[x][y] == CURSOFF)
+ c.grid[x][y] = OFF;
+ if (c.grid[x][y-1] == OFF) // fix for the "trail" bug
+ c.grid[x][y-1] = CURSOFF;
+ else if (c.grid[x][y-1] == ON)
+ c.grid[x][y-1] = CURSON;
+ y--;
+ }
+ }
+ if (osl_keys->pressed.down) {
+ if (y < (MAX_Y-1)) {
+ if (c.grid[x][y] == CURSON)
+ c.grid[x][y] = ON;
+ else if (c.grid[x][y] == CURSOFF)
+ c.grid[x][y] = OFF;
+ if (c.grid[x][y+1] == ON)
+ c.grid[x][y+1] = CURSON;
+ else if (c.grid[x][y+1] == OFF)
+ c.grid[x][y+1] = CURSOFF;
+ y++;
+ }
+ }
+ if (osl_keys->pressed.left) {
+ if (x > 0) {
+ if (c.grid[x][y] == CURSON)
+ c.grid[x][y] = ON;
+ else if (c.grid[x][y] == CURSOFF)
+ c.grid[x][y] = OFF;
+ if (c.grid[x-1][y] == ON)
+ c.grid[x-1][y] = CURSON;
+ else if (c.grid[x-1][y] == OFF)
+ c.grid[x-1][y] = CURSOFF;
+ x--;
+ }
+ }
+ if (osl_keys->pressed.right) {
+ if (x < (MAX_X-1)) {
+ if (c.grid[x][y] == CURSON)
+ c.grid[x][y] = ON;
+ else if (c.grid[x][y] == CURSOFF)
+ c.grid[x][y] = OFF;
+ if (c.grid[x+1][y] == ON)
+ c.grid[x+1][y] = CURSON;
+ else if (c.grid[x+1][y] == OFF)
+ c.grid[x+1][y] = CURSOFF;
+ x++;
+ }
+ }
+ if (osl_keys->pressed.cross) {
+ if (c.grid[x][y] == CURSOFF)
+ c.grid[x][y] = CURSON;
+ else if (c.grid[x][y] == CURSON)
+ c.grid[x][y] = CURSOFF;
+ }
+ if (osl_keys->pressed.circle) {
+ if (isplaying)
+ tmStop();
+ else if (!isplaying)
+ tmStart();
+ }
+ if (osl_keys->pressed.triangle) {
+ if (solo != 0)
+ solo = 0;
+ else if (tmMuteEmpty(mute))
+ solo = (y +1);
+ else {
+ for (m = 0; m < 8; m++)
+ mute[m] = FALSE;
+ solo = (y +1);
+ }
+ }
+ if (osl_keys->pressed.square) {
+ if (solo != 0)
+ solo = 0;
+ mute[y] = (mute[y]) ? FALSE : TRUE;
+ }
+
+ if (osl_keys->pressed.R) {
+ if (current < 11)
+ current++;
+ }
+ if (osl_keys->pressed.L) {
+ if (current > 0)
+ current--;
+ }
+ if (osl_keys->pressed.start) {
+ switch (tmMenu()) {
+ case SAVEM:
+ tmFileDialog(TRUE);
+ break;
+ case LOADM:
+ tmFileDialog(FALSE);
+ break;
+ case HELPM:
+ tmHelp();
+ break;
+ case CLEARM:
+ data[current] = tmClear();
+ break;
+ case CLEARALLM:
+ if (tmClearAllWarning()) {
+ for (q = 0; q < MAX_GRIDS; q++)
+ data[q] = tmClear();
+ current = 0;
+ x = 0, y = 0;
+ }
+ break;
+ }
+ }
+ if (osl_keys->pressed.select)
+ tmHelp();
+ oslEndDrawing();
+ oslSyncFrame();
+ }
+ quit = TRUE;
+ return;
+}
+
+int main(void) {
+ tmInit();
+ tmMainLoop();
+ tmQuit();
+ return 0;
+}
diff --git a/src/main.h b/src/main.h
new file mode 100644
index 0000000..3b371dc
--- /dev/null
+++ b/src/main.h
@@ -0,0 +1,111 @@
+/* ToneMatrix - main.h
+ * Copyright (c) 2012 Tanner Babcock */
+#ifndef MAIN_H
+#define MAIN_H
+
+#include
+#include
+
+#define VERSION "0.9"
+#define bool char
+#define tmQuit() oslEndGfx(); oslQuit()
+#define tmAbort(err) \
+ int s; \
+ oslClearScreen(RGBA(0, 0, 0, 255)); \
+ for (s = 0; s < 4; s++) { \
+ oslStartDrawing(); \
+ oslReadKeys(); \
+ oslDrawString(10, 10, err); \
+ oslEndDrawing(); \
+ oslSyncFrame(); \
+ } \
+ sceKernelDelayThread(2000000); \
+ tmQuit()
+
+#define OFFC RGBA(30,30,30,255) // off
+#define ONC RGBA(199,199,199,255) // on
+#define HOFF RGBA(50,50,50,255) // lit up and off
+#define HON RGBA(220,220,220,255) // lit up and on
+#define MOFF RGBA(40,0,0,255) // off, row muted
+#define MON RGBA(199,0,0,255) // on, row muted
+#define MHOFF RGBA(60,0,0,255) // lit up and off, row muted
+#define MHON RGBA(220,0,0,255) // lit up and on, row muted
+#define SOFF RGBA(0,7,40,255) // off, row soloed
+#define SON RGBA(0,8,199,255) // on, row soloed
+#define SHOFF RGBA(0,7,60,255) // lit up and off, row soloed
+#define SHON RGBA(0,8,220,255) // lit up and on, row soloed
+
+#define x1form ((x*30)-29)
+#define y1form ((15+(y*30))-29)
+#define x2form ((x*30)-1)
+#define y2form ((15+(y*30))-1)
+
+enum { FALSE, TRUE };
+enum { OFF, ON, CURSOFF, CURSON };
+enum {
+ SAVEM, // Save song
+ LOADM, // Load song
+ HELPM, // Help me
+ LOOPM, // Loop all: on
+ TEMPOM, // Tempo: 120 BPM
+ GRIDM, // Grid: 0
+ BREAK, //
+ CUTM, // Cut grid
+ COPYM, // Copy grid
+ PASTEM, // Paste grid
+ CLEARM, // Clear current grid
+ CLEARALLM // Clear all grids
+};
+enum {
+ MAX_MENU_ITEMS = 12,
+ MAX_GRIDS = 16,
+ MAX_X = 16,
+ MAX_Y = 8,
+ MAX_BPM = 250,
+ MIN_BPM = 60,
+ MAX_SAVE_SLOTS = 10
+};
+
+typedef struct {
+ char grid[16][8];
+} tmGrid;
+
+int bpm;
+unsigned char col, solo;
+unsigned char menuoption, current;
+char msg[30];
+double tempo;
+bool mute[MAX_Y];
+bool quit, isplaying, loopall, rflag;
+tmGrid data[MAX_GRIDS], clipboard;
+SceUID soundloop;
+OSL_SOUND *sound[MAX_Y];
+OSL_IMAGE *select;
+
+void tmInit(void);
+void tmDebug(int x, int y, const char *str);
+void tmSoundLoop(void);
+void tmMainLoop(void);
+void tmDrawGrid(void);
+void tmHelp(void);
+void setUpVirtualFileMenu(void);
+unsigned char tmMenu(void);
+tmGrid tmClear(void);
+double tmTempo(int b);
+int tmBPM(double t);
+void tmMuteCheck(void);
+bool tmMuteEmpty(bool m[8]);
+bool tmIsGridEmpty(tmGrid g);
+void tmSwitchGrid(int a, int b);
+bool tmClearAllWarning(void);
+void tmFileDialog(bool save);
+bool tmOverwriteWarning(int slot);
+bool tmDeleteWarning(int slot);
+bool tmDoesFileExist(const char *path);
+const char * tmOSK(const char *name);
+void tmRead(int slot);
+void tmWrite(int slot);
+void tmStop(void);
+void tmStart(void);
+
+#endif
diff --git a/src/menu.c b/src/menu.c
new file mode 100644
index 0000000..13a67a4
--- /dev/null
+++ b/src/menu.c
@@ -0,0 +1,239 @@
+/* ToneMatrix - menu.c
+ * Copyright (c) 2012 Tanner Babcock */
+#include
+#include "main.h"
+
+unsigned char tmMenu(void) {
+ int x;
+ char mstrings[MAX_MENU_ITEMS][40];
+ char ver[25];
+ while (!osl_quit) {
+ oslStartDrawing();
+ oslReadKeys();
+ tmDrawGrid();
+ oslDrawFillRect(0, 0, 480, 272, RGBA(0, 0, 0, 125));
+ tempo = tmTempo(bpm);
+ bpm = tmBPM(tempo);
+ for (x = 0; x < MAX_MENU_ITEMS; x++) {
+ bzero(mstrings[x], sizeof(mstrings[x]));
+ switch (x) {
+ case SAVEM:
+ sprintf(mstrings[x], "%s Save song", (menuoption == x) ? "->" : " ");
+ break;
+ case LOADM:
+ sprintf(mstrings[x], "%s Load song", (menuoption == x) ? "->" : " ");
+ break;
+ case HELPM:
+ sprintf(mstrings[x], "%s Help me", (menuoption == x) ? "->" : " ");
+ break;
+ case LOOPM:
+ sprintf(mstrings[x], "%s Loop all: %s", (menuoption == x) ? "->" : " ", (loopall) ? "on" : "off");
+ break;
+ case TEMPOM:
+ sprintf(mstrings[x], "%s Tempo: %d BPM", (menuoption == x) ? "->" : " ", tmBPM(tempo));
+ break;
+ case GRIDM:
+ sprintf(mstrings[x], "%s Grid: %d", (menuoption == x) ? "->" : " ", (current+1));
+ break;
+ case CUTM:
+ sprintf(mstrings[x], "%s Cut grid", (menuoption == x) ? "->" : " ");
+ break;
+ case COPYM:
+ sprintf(mstrings[x], "%s Copy grid", (menuoption == x) ? "->" : " ");
+ break;
+ case PASTEM:
+ if (tmIsGridEmpty(clipboard))
+ oslSetTextColor(RGBA(255,0,0,255));
+ sprintf(mstrings[x], "%s Paste grid", (menuoption == x) ? "->" : " ");
+ break;
+ case CLEARM:
+ sprintf(mstrings[x], "%s Clear grid", (menuoption == x) ? "->" : " ");
+ break;
+ case CLEARALLM:
+ sprintf(mstrings[x], "%s Clear all grids", (menuoption == x) ? "->" : " ");
+ break;
+ }
+ oslDrawString(10, (60+(x*10)), mstrings[x]);
+ oslSetTextColor(RGBA(255,255,255,255));
+ }
+ bzero(ver, sizeof(ver));
+ // sprintf(ver, "ToneMatrix %s by Babkock", VERSION);
+ oslDrawString(10, 240, ver);
+ if ((osl_keys->pressed.up) && (menuoption > 0)) {
+ menuoption--;
+ if (menuoption == BREAK)
+ menuoption--;
+ }
+ if ((osl_keys->pressed.down) && (menuoption < (MAX_MENU_ITEMS-1))) {
+ menuoption++;
+ if (menuoption == BREAK)
+ menuoption++;
+ }
+ if (osl_keys->pressed.left) {
+ switch (menuoption) {
+ case GRIDM:
+ if (current > 0)
+ current--;
+ break;
+ case LOOPM:
+ loopall = (loopall) ? FALSE : TRUE;
+ break;
+ case TEMPOM:
+ if (bpm > MIN_BPM)
+ bpm -= 5;
+ tempo = tmTempo(bpm);
+ break;
+ }
+ }
+ if (osl_keys->pressed.right) {
+ switch (menuoption) {
+ case GRIDM:
+ if (current < (MAX_GRIDS-1))
+ current++;
+ break;
+ case LOOPM:
+ loopall = (loopall) ? FALSE : TRUE;
+ break;
+ case TEMPOM:
+ if (bpm < MAX_BPM)
+ bpm += 5;
+ tempo = tmTempo(bpm);
+ break;
+ }
+ }
+ if (osl_keys->pressed.cross) {
+ if ((menuoption != LOOPM) && (menuoption != TEMPOM) && (menuoption != GRIDM) &&
+ (menuoption != CUTM) && (menuoption != COPYM) && (menuoption != PASTEM))
+ return menuoption;
+ else {
+ switch (menuoption) {
+ case CUTM:
+ clipboard = tmClear();
+ clipboard = data[current];
+ data[current] = tmClear();
+ break;
+ case COPYM:
+ clipboard = tmClear();
+ clipboard = data[current];
+ break;
+ case PASTEM:
+ if (!tmIsGridEmpty(clipboard)) {
+ data[current] = tmClear();
+ data[current] = clipboard;
+ }
+ break;
+ }
+ }
+ }
+ if (osl_keys->pressed.start)
+ break;
+ oslEndDrawing();
+ oslSyncFrame();
+ }
+ return 69;
+}
+
+void tmHelp(void) {
+ oslClearScreen(RGBA(0, 0, 0, 255));
+ while (!osl_quit) {
+ oslStartDrawing();
+ oslReadKeys();
+ tmDrawGrid();
+ oslDrawFillRect(0, 0, 480, 272, RGBA(0, 0, 0, 125));
+ oslDrawString(1, 10, "ToneMatrix is a 16-step drum machine. The X axis represents one");
+ oslDrawString(1, 20, "4/4 measure in sixteenth notes, and the Y axis represents eight");
+ oslDrawString(1, 30, "different sounds. Select a node on the grid with the D-pad and the");
+ oslDrawString(1, 40, "sound will be played in time.");
+ oslDrawString(1, 60, "Press O (circle) to stop or resume playing. Press [] (square) to");
+ oslDrawString(1, 70, "mute the row the cursor is on. Press /\\ (triangle) to mute every");
+ oslDrawString(1, 80, "row except the row the cursor is on. You can mute more than one row");
+ oslDrawString(1, 90, "at once.");
+ oslDrawString(1, 110, "Press START to use the menu, and use the D-pad to select a menu");
+ oslDrawString(1, 120, "item or change a value (like the tempo), and X (cross) to confirm");
+ oslDrawString(1, 130, "to confirm your choice.");
+ oslDrawString(1, 150, "You can play up to sixteen measures in a row by using the R and L");
+ oslDrawString(1, 160, "triggers, or by switching the \"grid\" value in the START menu.");
+ oslDrawString(1, 170, "If \"loop all\" is on, then the measures will play one after");
+ oslDrawString(1, 180, "another. If it is off, it will only repeat one measure. It is");
+ oslDrawString(1, 190, "recommended that you stop playback before editing another grid.");
+ oslDrawString(1, 210, "Press SELECT to hide this screen.");
+ if (osl_keys->pressed.select)
+ break;
+ oslEndDrawing();
+ oslSyncFrame();
+ }
+ return;
+}
+
+bool tmClearAllWarning(void) {
+ oslClearScreen(RGBA(0, 0, 0, 255));
+ bool x = FALSE;
+ while (!osl_quit) {
+ oslStartDrawing();
+ oslReadKeys();
+ tmDrawGrid();
+ oslDrawFillRect(0, 0, 480, 272, RGBA(0, 0, 0, 125));
+ oslSetTextColor(RGBA(255,0,0,255));
+ oslDrawString(200, 50, "WARNING:");
+ oslSetTextColor(RGBA(255,255,255,255));
+ oslDrawString(3, 70, "Are you sure you want to clear all grids? This cannot be undone.");
+ oslDrawString(13, 80, "Press X to clear all grids, or press O to cancel.");
+ if (osl_keys->pressed.cross) {
+ x = TRUE;
+ break;
+ }
+ if (osl_keys->pressed.circle)
+ break;
+ oslEndDrawing();
+ oslSyncFrame();
+ }
+ return x;
+}
+
+bool tmOverwriteWarning(int slot) {
+ bool x = FALSE;
+ char msg[50];
+ sprintf(msg, "Are you sure you want to overwrite song%d.tms ?", slot);
+ while (!osl_quit) {
+ oslStartDrawing();
+ oslReadKeys();
+ tmDrawGrid();
+ oslDrawFillRect(0, 0, 480, 272, RGBA(0, 0, 0, 125));
+ oslDrawString(13, 70, msg);
+ oslDrawString(100, 90, "X - Yes");
+ oslDrawString(100, 100, "O - No");
+ if (osl_keys->pressed.cross) {
+ x = TRUE;
+ break;
+ }
+ if (osl_keys->pressed.circle)
+ break;
+ oslEndDrawing();
+ oslSyncFrame();
+ }
+ return x;
+}
+
+bool tmDeleteWarning(int slot) {
+ bool x = FALSE;
+ char msg[50];
+ sprintf(msg, "Are you sure you want to delete song%d.tms ?", slot);
+ while (!osl_quit) {
+ oslStartDrawing();
+ oslReadKeys();
+ tmDrawGrid();
+ oslDrawFillRect(0, 0, 480, 272, RGBA(0, 0, 0, 125));
+ oslDrawString(13, 70, msg);
+ oslDrawString(100, 90, "X - Yes");
+ oslDrawString(100, 100, "O - No");
+ if (osl_keys->pressed.cross) {
+ x = TRUE;
+ break;
+ }
+ if (osl_keys->pressed.circle)
+ break;
+ oslEndDrawing();
+ oslSyncFrame();
+ }
+ return x;
+}
diff --git a/src/misc.c b/src/misc.c
new file mode 100644
index 0000000..7fefb6f
--- /dev/null
+++ b/src/misc.c
@@ -0,0 +1,126 @@
+/* ToneMatrix - misc.c
+ * Copyright (c) 2012 Tanner Babcock */
+#include
+#include
+#include
+#include
+#include
+#include "main.h"
+
+void tmInit(void) {
+ oslInit(0);
+ oslInitGfx(OSL_PF_8888, 1);
+ oslInitConsole();
+ oslInitAudio();
+ int x, y;
+ char tmp[25];
+ rflag = FALSE;
+ for (x = 0; x < 3; x++) {
+ oslStartDrawing();
+ oslReadKeys();
+ if (osl_keys->pressed.R)
+ rflag = TRUE;
+ oslEndDrawing();
+ oslSyncFrame();
+ }
+ if (rflag)
+ tmDebug(10, 10, "Debug mode activated");
+ for (x = 0; x < MAX_Y; x++) {
+ bzero(tmp, sizeof(tmp));
+ sprintf(tmp, "sounds/tone%d.wav", x);
+ sound[x] = oslLoadSoundFile(tmp, OSL_FMT_NONE);
+ if (!sound[x]) {
+ bzero(tmp, sizeof(tmp));
+ sprintf(tmp, "ERROR: sound file %d missing", x);
+ tmDebug(10, 10, tmp);
+ sceKernelDelayThread(2000000);
+ tmQuit();
+ }
+ }
+ // setUpVirtualFileMenu();
+ select = oslLoadImageFilePNG("select.png", OSL_IN_RAM, OSL_PF_5551);
+ oslSetBkColor(RGBA(0, 0, 0, 0));
+ current = 0;
+ clipboard = tmClear();
+ for (y = 0; y < MAX_GRIDS; y++)
+ data[y] = tmClear();
+ quit = FALSE;
+ col = 0;
+ menuoption = 0;
+ tempo = 125000;
+ bpm = tmBPM(tempo);
+ isplaying = TRUE;
+ loopall = TRUE;
+ soundloop = sceKernelCreateThread("Sound Loop", tmSoundLoop, 0x11, (256 * 1024), PSP_THREAD_ATTR_USER, NULL);
+ oslClearScreen(RGBA(0, 0, 0, 255));
+ return;
+}
+
+void tmDebug(int x, int y, const char *str) {
+ int z;
+ for (z = 0; z != 3; z++) {
+ oslStartDrawing();
+ oslDrawString(x, y, str);
+ oslEndDrawing();
+ oslSyncFrame();
+ }
+ return;
+}
+
+/* void setUpVirtualFileMenu(void) {
+ OSL_VIRTUALFILENAME ram_files[] = {{"ram:/select.png", (void*)select_data, sizeof(select_data), &VF_MEMORY}};
+ oslAddVirtualFileList(ram_files, oslNumberof(ram_files));
+} */
+
+tmGrid tmClear(void) {
+ int y, z;
+ tmGrid g;
+ for (y = 0; y < MAX_X; y++) {
+ for (z = 0; z < MAX_Y; z++)
+ g.grid[y][z] = OFF;
+ }
+ g.grid[0][0] = CURSOFF;
+ return g;
+}
+
+double tmTempo(int b) {
+ return (15000000 / b);
+}
+
+int tmBPM(double t) {
+ return (15000000 / t);
+}
+
+bool tmMuteEmpty(bool m[8]) {
+ int x;
+ for (x = 0; x < MAX_Y; x++) {
+ if (m[x])
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool tmIsGridEmpty(tmGrid g) {
+ int x, y;
+ for (x = 0; x < MAX_Y; x++) {
+ for (y = 0; y < MAX_X; y++) {
+ if (g.grid[x][y] != OFF && g.grid[x][y] != CURSOFF)
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void tmSwitchGrid(int a, int b) {
+ // call from tmMainloop with tmSwitchGrid(&x, &y)
+ int x, y;
+ for (x = 0; x < MAX_X; x++) {
+ for (y = 0; y < MAX_Y; y++) {
+ if (data[current].grid[x][y] == CURSON)
+ data[current].grid[x][y] = ON;
+ if (data[current].grid[x][y] == CURSOFF)
+ data[current].grid[x][y] = OFF;
+ }
+ }
+ return;
+}
diff --git a/src/select.png b/src/select.png
new file mode 100644
index 0000000..10b8cc2
Binary files /dev/null and b/src/select.png differ
diff --git a/src/sound.c b/src/sound.c
new file mode 100644
index 0000000..648b515
--- /dev/null
+++ b/src/sound.c
@@ -0,0 +1,47 @@
+/* ToneMatrix - sound.c
+ * Copyright (c) 2012 Tanner Babcock */
+#include
+#include
+#include
+#include "main.h"
+
+void tmSoundLoop(void) {
+ int x, y;
+ bpm = tmBPM(tempo);
+ tempo = tmTempo(bpm);
+ while (!quit) {
+ for (x = 0; x < MAX_X; x++) {
+ col = (x+1);
+ for (y = 0; y < 8; y++) {
+ if (data[current].grid[x][y] == ON || data[current].grid[x][y] == CURSON) {
+ if ((solo != 0 && solo == (y +1)) || ((solo == 0) && (!mute[y])))
+ oslPlaySound(sound[y], y);
+ }
+ }
+ sceKernelDelayThread(tmTempo(bpm));
+ }
+ if (rflag) {
+ if (loopall)
+ current = (!tmIsGridEmpty(data[current+1])) ? (current+1) : 0;
+ }
+ else {
+ if (loopall)
+ current = ((!tmIsGridEmpty(data[current+1])) && (current < MAX_GRIDS)) ? (current+1) : 0;
+ }
+ }
+ sceKernelExitDeleteThread(NULL);
+ return;
+}
+
+void tmStop(void) {
+ isplaying = FALSE;
+ col = 0;
+ sceKernelTerminateThread(soundloop);
+ return;
+}
+
+void tmStart(void) {
+ isplaying = TRUE;
+ sceKernelStartThread(soundloop, 0, NULL);
+ return;
+}
diff --git a/unsigned/EBOOT.PBP b/unsigned/EBOOT.PBP
new file mode 100644
index 0000000..b53be5b
Binary files /dev/null and b/unsigned/EBOOT.PBP differ
diff --git a/unsigned/select.png b/unsigned/select.png
new file mode 100644
index 0000000..10b8cc2
Binary files /dev/null and b/unsigned/select.png differ
diff --git a/unsigned/songs/SAVED-SONGS-GO-HERE.txt b/unsigned/songs/SAVED-SONGS-GO-HERE.txt
new file mode 100644
index 0000000..721a952
--- /dev/null
+++ b/unsigned/songs/SAVED-SONGS-GO-HERE.txt
@@ -0,0 +1,13 @@
+ToneMatrix will only load songs if the file name is one of the following:
+
+song0.tms
+song1.tms
+song2.tms
+song3.tms
+song4.tms
+song5.tms
+song6.tms
+song7.tms
+song8.tms
+song9.tms
+
diff --git a/unsigned/sounds/tone0.wav b/unsigned/sounds/tone0.wav
new file mode 100644
index 0000000..fa7c568
Binary files /dev/null and b/unsigned/sounds/tone0.wav differ
diff --git a/unsigned/sounds/tone1.wav b/unsigned/sounds/tone1.wav
new file mode 100644
index 0000000..1637e0e
Binary files /dev/null and b/unsigned/sounds/tone1.wav differ
diff --git a/unsigned/sounds/tone2.wav b/unsigned/sounds/tone2.wav
new file mode 100644
index 0000000..86cacb4
Binary files /dev/null and b/unsigned/sounds/tone2.wav differ
diff --git a/unsigned/sounds/tone3.wav b/unsigned/sounds/tone3.wav
new file mode 100644
index 0000000..c023c76
Binary files /dev/null and b/unsigned/sounds/tone3.wav differ
diff --git a/unsigned/sounds/tone4.wav b/unsigned/sounds/tone4.wav
new file mode 100644
index 0000000..119868d
Binary files /dev/null and b/unsigned/sounds/tone4.wav differ
diff --git a/unsigned/sounds/tone5.wav b/unsigned/sounds/tone5.wav
new file mode 100644
index 0000000..b3b5ed6
Binary files /dev/null and b/unsigned/sounds/tone5.wav differ
diff --git a/unsigned/sounds/tone6.wav b/unsigned/sounds/tone6.wav
new file mode 100644
index 0000000..2a572b6
Binary files /dev/null and b/unsigned/sounds/tone6.wav differ
diff --git a/unsigned/sounds/tone7.wav b/unsigned/sounds/tone7.wav
new file mode 100644
index 0000000..f8f8fca
Binary files /dev/null and b/unsigned/sounds/tone7.wav differ