add minor new features

master
Len Shustek 4 years ago
parent 446ebadf83
commit 3a0d14ce12
  1. 2
      LICENSE.txt
  2. 17
      README.txt
  3. 42
      miditones.c
  4. BIN
      miditones.exe
  5. 25
      miditones_scroll.c
  6. BIN
      miditones_scroll.exe

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2016, Len Shustek
Copyright (c) 2016,2021 Len Shustek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

@ -45,6 +45,15 @@
instrument strike. Each generator's volume is independently adjusted according to
the MIDI velocity of the note being played before all channels are mixed.
www.github.com/LenShustek/Playtune_Teensy
The fifth version is for the Teensy 3.1/3.2, and uses the four Periodic Interval
Timers in the Cortex M4 processor to support up to 4 simultaneous notes.
It uses less CPU time than the polling version, but is limited to 4 notes at a time.
(This was written to experiment with multi-channel multi-Tesla Coil music playing,
where I use Flexible Timer Module FTM0 for generating precise one-shot pulses.
But I ultimately switched to the polling version to play more simultaneous notes.)
www.github.com/LenShustek/Playtune_Teensy
www.github.com/LenShustek/ATtiny-playtune
This is a much simplified version that fits, with a small song, into an ATtiny
processor with only 4K of flash memory. It also using polling with only one timer,
@ -93,6 +102,9 @@
The <basefilename> is the base name, without an extension, for the input and
output files. It can contain directory path information, or not.
If the user specifies the full .mid filename, the .mid or .MID extension
will be dropped and the remaining name will be used as <basefilename>.
The input file is <basefilename>.mid, and the output filename(s)
are the base file name with .c, .h, .bin, and/or .log extensions.
@ -152,6 +164,9 @@
-h Give command-line help.
-showskipped Display information to the console each note that had to be skipped
because there weren't enough tone generators.
-delaymin=x Don't generate delays less than x milliseconds long, to reduce the number
of "delay" commands and thus make the bytestream smaller, at the expense of
moving notes slightly. The deficits are accumulated and eventually used,
@ -238,4 +253,4 @@
Any subsequent header bytes covered by the count, if present, are currently undefined
and should be ignored by players.
Len Shustek, 2011 to 2019; see the change log.
Len Shustek, 2011 to 2021; see the change log.

@ -47,6 +47,15 @@
instrument strike. Each generator's volume is independently adjusted according to
the MIDI velocity of the note being played before all channels are mixed.
www.github.com/LenShustek/Playtune_Teensy
The fifth version is for the Teensy 3.1/3.2, and uses the four Periodic Interval
Timers in the Cortex M4 processor to support up to 4 simultaneous notes.
It uses less CPU time than the polling version, but is limited to 4 notes at a time.
(This was written to experiment with multi-channel multi-Tesla Coil music playing,
where I use Flexible Timer Module FTM0 for generating precise one-shot pulses.
But I ultimately switched to the polling version to play more simultaneous notes.)
www.github.com/LenShustek/Playtune_Teensy
www.github.com/LenShustek/ATtiny-playtune
This is a much simplified version that fits, with a small song, into an ATtiny
processor with only 4K of flash memory. It also using polling with only one timer,
@ -157,6 +166,9 @@
-h Give command-line help.
-showskipped Display information to the console each note that had to be skipped
because there weren't enough tone generators.
-delaymin=x Don't generate delays less than x milliseconds long, to reduce the number
of "delay" commands and thus make the bytestream smaller, at the expense of
moving notes slightly. The deficits are accumulated and eventually used,
@ -243,11 +255,11 @@
Any subsequent header bytes covered by the count, if present, are currently undefined
and should be ignored by players.
Len Shustek, 2011 to 2019; see the change log.
Len Shustek, 2011 to 2021; see the change log.
*----------------------------------------------------------------------------------------
* The MIT License (MIT)
* Copyright (c) 2011,2013,2015,2016,2019 Len Shustek
* Copyright (c) 2011,2013,2015,2016,2019,2021 Len Shustek
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -367,6 +379,9 @@
-Let user supply full filename to MIDI file on command line to be friendlier
to users using shell autocompletion. If a .mid or .MID file is provided,
the extension will be dropped to generate the base filename.
22 April 2021, Len Shustek, V2.2
-Add -showskipped to log the places where notes had to be discarded because
there aren't enough tone generators.
future version ideas
@ -382,7 +397,7 @@ future version ideas
channel 8 // organ
options -attacktime=1000 -sustainlevel=80% -releasetime=100 -notemin=200
*/
#define VERSION "2.1"
#define VERSION "2.2"
/*--------------------------------------------------------------------------------------------
@ -571,7 +586,7 @@ struct track_header {
bool loggen, logparse, parseonly, strategy1, strategy2, binaryoutput, define_progmem,
volume_output, instrumentoutput, percussion_ignore, percussion_translate, do_header,
gen_restart, scorename;
gen_restart, scorename, showskipped;
FILE *infile, *outfile, *logfile;
uint8_t *buffer, *hdrptr;
unsigned long buflen;
@ -703,6 +718,7 @@ void SayUsage(char *programName) {
" -r terminate output file with \"restart\" instead of \"stop\" command",
" -s1 strategy 1: favor track 1",
" -s2 strategy 2: try to assign tracks to specific tone generators",
" -showskipped display information about each note that had to be skipped",
" -delaymin=x minimum delay is x msec, to save bytestream space",
" -attacktime=x the high volume attack phase lasts x msec",
" -attacknotemax=x notes over x msec don't use the attack/sustain profile",
@ -752,6 +768,7 @@ int HandleOptions (int argc, char *argv[]) {
else if (opt_key(arg, "s1")) strategy1 = true;
else if (opt_key(arg, "s2")) strategy2 = true;
else if (opt_key(arg, "scorename")) scorename = true;
else if (opt_key(arg, "showskipped")) showskipped = true;
else if (opt_int(arg, "t", &num_tonegens, 1, MAX_TONEGENS))
printf("Using %d tone generators\n", num_tonegens);
else if (opt_key(arg, "v")) volume_output = true;
@ -898,8 +915,11 @@ struct channel_status { // current status of a channel
char *describe(struct noteinfo *np) { // create a description of a note
// WARNING: returns a pointer to a static string, so only call once per line, in a printf!
static char notedescription[100];
sprintf(notedescription, "at %lu.%03lu msec, note %d (0x%02X) track %d channel %d volume %d instrument %d",
np->time_usec / 1000, np->time_usec % 1000, np->note, np->note,
int secs = np->time_usec / 1000000;
sprintf(notedescription, "at %3lu.%06lu sec (%d:%02d), note %d (0x%02X) track %d channel %d volume %d instrument %d",
secs, np->time_usec % 1000000,
secs/60, secs % 60,
np->note, np->note,
np->track, np->channel, np->volume, np->instrument);
return notedescription; }
@ -1056,7 +1076,8 @@ void remove_queue_entry(int ndx) { // remove the oldest queue entry
else {
if (loggen) fprintf(logfile, " *** at %lu.%03lu msec no free generator; skipping %s\n",
output_usec / 1000, output_usec % 1000, describe(&q->note));
++notes_skipped; } } }
if (showskipped) printf(" *** no free generator %s\n",
describe(&q->note)); ++notes_skipped; } } }
void generate_delay(unsigned long delta_msec) { // output a delay command
if (delta_msec > 0) {
@ -1557,10 +1578,9 @@ int main (int argc, char *argv[]) {
// strip off trailing .mid or .MID extension if provided by user
basenamelen = strlength(filebasename);
if (basenamelen > 4 &&
(charcmp (filebasename + basenamelen - 4, ".mid") ||
charcmp (filebasename + basenamelen - 4, ".MID"))) {
filebasename[basenamelen - 4] = 0;
}
(charcmp (filebasename + basenamelen - 4, ".mid") ||
charcmp (filebasename + basenamelen - 4, ".MID"))) {
filebasename[basenamelen - 4] = 0; }
if (logparse || loggen) { // open the log file
miditones_strlcpy (filename, filebasename, MAXPATH);

Binary file not shown.

@ -53,7 +53,7 @@
*
*----------------------------------------------------------------------------------------
* The MIT License (MIT)
* Copyright (c) 2011,2013,2015,2016,2019, Len Shustek
* Copyright (c) 2011,2013,2015,2016,2019,2021 Len Shustek
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -109,9 +109,12 @@
* - Write the output to <basefilename>.txt, if not -c
* - Decode the header flags
* - Reformat to condense the code
* 23 April 2021, L. Shustek, V1.9
* - show a summary at the end of what instruments were used and how many times
* - show the lowest and highest volume used
*/
#define VERSION "1.8"
#define VERSION "1.9"
#include <stdio.h>
#include <stdlib.h>
@ -150,6 +153,7 @@ bool codeoutput = false;
bool expect_volume = false;
bool ignore_volume = false;
bool showhex = false;
unsigned max_vol = 0, min_vol = 255;
struct file_hdr_t { /* what the optional file header looks like */
char id1; // 'P'
@ -216,6 +220,7 @@ static char *instrumentname[128] = { /* maximum 6 characters */
"Sitar ", "Banjo ", "Shamis", "Koto ", "Kalimb", "Bagpip", "Fiddle", "Shanai",
"TnkBel", "Agogo ", "StDrum", "WdBlok", "TaiDrm", "MelTom", "SynDrm", "RevCym",
"GuitFr", "Breath", "Seashr", "BirdTw", "Phone ", "Copter", "Claps ", "Guns " };
int instrument_count[128] = { 0 };
/************** command-line processing *******************/
@ -450,8 +455,7 @@ int main (int argc, char *argv[]) {
for (int i = 0; i < argc; i++) fprintf(outfile, "%s ", argv[i]);
fprintf(outfile, "\n");
fprintf(outfile, "reading %s.bin with %ld bytes\n", filebasename, buflen);
if (num_tonegens < MAX_TONEGENS) fprintf(outfile, "displaying only %d tone generators.\n", num_tonegens);
}
if (num_tonegens < MAX_TONEGENS) fprintf(outfile, "displaying only %d tone generators.\n", num_tonegens); }
else {
fprintf (outfile, "// Playtune bytestream for file \"%s.bin\"", filebasename);
fprintf (outfile, " created by MIDITONES_SCROLL V%s on %s\n", VERSION,
@ -513,11 +517,14 @@ int main (int argc, char *argv[]) {
if (cmd == 0x90) { /* note on */
gen_note[gen] = *++bufptr; // note number
tonegens_used |= 1 << gen; // record that we used this generator at least once
++instrument_count[gen_instrument[gen]]; // count a use of this instrument
if (gen_did_stopnote[gen]) { // unnecesary stop note
++stopnotes_before_startnote;
warning = true; }
if (expect_volume)
gen_volume[gen] = *++bufptr; // volume
if (expect_volume) {
unsigned volume = gen_volume[gen] = *++bufptr; // volume
if (volume > max_vol) max_vol = volume;
if (volume < min_vol) min_vol = volume; }
if (gen >= num_tonegens) ++notes_skipped; // won't be displaying this note
}
else if (cmd == 0x80) { /* note off */
@ -555,4 +562,10 @@ int main (int argc, char *argv[]) {
fprintf (infofile, "%u stopnote commands were unnecessary.\n", stopnotes_before_startnote);
fprintf (infofile, "%u consecutive delays could have been merged.\n", consecutive_delays);
if (stopnotes_before_startnote + consecutive_delays > 0) fprintf (infofile, "(Those locations are marked with \"!\")\n");
fprintf(infofile, "instruments used:\n");
for (int i = 0; i < 128; ++i)
if (instrument_count[i]) {
fprintf(infofile, " %s (%3d, 0x%02X) %7d\n", instrumentname[i], i, i, instrument_count[i]); }
if (expect_volume)
fprintf(infofile, "volume ranged from %d to %d\n", min_vol, max_vol);
printf ("Done.\n"); }

Binary file not shown.
Loading…
Cancel
Save