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