307 lines
7.7 KiB
C++
307 lines
7.7 KiB
C++
/*
|
|
* QT AGI Studio :: Copyright (C) 2000 Helen Zommer
|
|
*
|
|
* Midi support, written by Jarno Elonen <elonen@iki.fi>, 2003
|
|
* Based on SND2MIDI 1.2 by Jens Restemeier <jrestemeier@currantbun.com>
|
|
*
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include "global.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
|
|
#include <qdatastream.h>
|
|
#include <qiodevice.h>
|
|
#include <q3filedialog.h>
|
|
#include <qlabel.h>
|
|
#include <q3vbox.h>
|
|
#include <qcombobox.h>
|
|
#include <q3combobox.h>
|
|
#include <qpushbutton.h>
|
|
|
|
static const char* g_gm_instrument_names[] =
|
|
{
|
|
"1. Acoustic Grand Piano",
|
|
"2. Bright Acoustic Piano",
|
|
"3. Electric Grand Piano",
|
|
"4. Honky-tonk Piano",
|
|
"5. Electric Piano 1",
|
|
"6. Electric Piano 2",
|
|
"7. Harpsichord",
|
|
"8. Clavi",
|
|
"9. Celesta",
|
|
"10. Glockenspiel",
|
|
"11. Music Box",
|
|
"12. Vibraphone",
|
|
"13. Marimba",
|
|
"14. Xylophone",
|
|
"15. Tubular Bells",
|
|
"16. Dulcimer",
|
|
"17. Drawbar Organ",
|
|
"18. Percussive Organ",
|
|
"19. Rock Organ",
|
|
"20. Church Organ",
|
|
"21. Reed Organ",
|
|
"22. Accordion",
|
|
"23. Harmonica",
|
|
"24. Tango Accordion",
|
|
"25. Acoustic Guitar (nylon)",
|
|
"26. Acoustic Guitar (steel)",
|
|
"27. Electric Guitar (jazz)",
|
|
"28. Electric Guitar (clean)",
|
|
"29. Electric Guitar (muted)",
|
|
"30. Overdriven Guitar",
|
|
"31. Distortion Guitar",
|
|
"32. Guitar harmonics",
|
|
"33. Acoustic Bass",
|
|
"34. Electric Bass (finger)",
|
|
"35. Electric Bass (pick)",
|
|
"36. Fretless Bass",
|
|
"37. Slap Bass 1",
|
|
"38. Slap Bass 2",
|
|
"39. Synth Bass 1",
|
|
"40. Synth Bass 2",
|
|
"41. Violin",
|
|
"42. Viola",
|
|
"43. Cello",
|
|
"44. Contrabass",
|
|
"45. Tremolo Strings",
|
|
"46. Pizzicato Strings",
|
|
"47. Orchestral Harp",
|
|
"48. Timpani",
|
|
"49. String Ensemble 1",
|
|
"50. String Ensemble 2",
|
|
"51. SynthStrings 1",
|
|
"52. SynthStrings 2",
|
|
"53. Choir Aahs",
|
|
"54. Voice Oohs",
|
|
"55. Synth Voice",
|
|
"56. Orchestra Hit",
|
|
"57. Trumpet",
|
|
"58. Trombone",
|
|
"59. Tuba",
|
|
"60. Muted Trumpet",
|
|
"61. French Horn",
|
|
"62. Brass Section",
|
|
"63. SynthBrass 1",
|
|
"64. SynthBrass 2",
|
|
"65. Soprano Sax",
|
|
"66. Alto Sax",
|
|
"67. Tenor Sax",
|
|
"68. Baritone Sax",
|
|
"69. Oboe",
|
|
"70. English Horn",
|
|
"71. Bassoon",
|
|
"72. Clarinet",
|
|
"73. Piccolo",
|
|
"74. Flute",
|
|
"75. Recorder",
|
|
"76. Pan Flute",
|
|
"77. Blown Bottle",
|
|
"78. Shakuhachi",
|
|
"79. Whistle",
|
|
"80. Ocarina",
|
|
"81. Lead 1 (square)",
|
|
"82. Lead 2 (sawtooth)",
|
|
"83. Lead 3 (calliope)",
|
|
"84. Lead 4 (chiff)",
|
|
"85. Lead 5 (charang)",
|
|
"86. Lead 6 (voice)",
|
|
"87. Lead 7 (fifths)",
|
|
"88. Lead 8 (bass + lead)",
|
|
"89. Pad 1 (new age)",
|
|
"90. Pad 2 (warm)",
|
|
"91. Pad 3 (polysynth)",
|
|
"92. Pad 4 (choir)",
|
|
"93. Pad 5 (bowed)",
|
|
"94. Pad 6 (metallic)",
|
|
"95. Pad 7 (halo)",
|
|
"96. Pad 8 (sweep)",
|
|
"97. FX 1 (rain)",
|
|
"98. FX 2 (soundtrack)",
|
|
"99. FX 3 (crystal)",
|
|
"100. FX 4 (atmosphere)",
|
|
"101. FX 5 (brightness)",
|
|
"102. FX 6 (goblins)",
|
|
"103. FX 7 (echoes)",
|
|
"104. FX 8 (sci-fi)",
|
|
"105. Sitar",
|
|
"106. Banjo",
|
|
"107. Shamisen",
|
|
"108. Koto",
|
|
"109. Kalimba",
|
|
"110. Bag pipe",
|
|
"111. Fiddle",
|
|
"112. Shanai",
|
|
"113. Tinkle Bell",
|
|
"114. Agogo",
|
|
"115. Steel Drums",
|
|
"116. Woodblock",
|
|
"117. Taiko Drum",
|
|
"118. Melodic Tom",
|
|
"119. Synth Drum",
|
|
"120. Reverse Cymbal",
|
|
"121. Guitar Fret Noise",
|
|
"122. Breath Noise",
|
|
"123. Seashore",
|
|
"124. Bird Tweet",
|
|
"125. Telephone Ring",
|
|
"126. Helicopter",
|
|
"127. Applause",
|
|
"128. Gunshot",
|
|
NULL
|
|
};
|
|
|
|
// Write given AGI Sound resource to given QIODevice
|
|
void writeMidi(const byte* snd, QIODevice& write_to, const unsigned char instr[])
|
|
{
|
|
long delta_tmp;
|
|
#define WRITE_DELTA(X) \
|
|
delta_tmp=X>>21; if (delta_tmp>0) out << Q_UINT8((delta_tmp&127)|128); \
|
|
delta_tmp=X>>14; if (delta_tmp>0) out << Q_UINT8((delta_tmp&127)|128); \
|
|
delta_tmp=X>>7; if (delta_tmp>0) out << Q_UINT8((delta_tmp&127)|128); \
|
|
out << Q_UINT8(X&127)
|
|
|
|
double ll=log10(pow(2.0,1.0/12.0));
|
|
|
|
if ( write_to.isSequentialAccess() || !write_to.isWritable())
|
|
qFatal("writeMidi() requires a writable random access IODevice");
|
|
|
|
QDataStream out( &write_to );
|
|
|
|
// Header
|
|
out.writeRawBytes("MThd",4);
|
|
out << Q_UINT32(6)
|
|
<< Q_UINT16(1) // mode
|
|
<< Q_UINT16(3) // # of tracks
|
|
<< Q_UINT16(96); // ticks / quarter
|
|
|
|
for (int n=0;n<3;n++)
|
|
{
|
|
out.writeRawBytes("MTrk",4);
|
|
qlonglong lp = write_to.at();
|
|
out << Q_UINT32(0); // chunklength
|
|
WRITE_DELTA(0); // set instrument
|
|
out << Q_UINT8(0xc0+n)
|
|
<< Q_UINT8(instr[n]);
|
|
|
|
unsigned short start=snd[n*2+0] | (snd[n*2+1]<<8);
|
|
unsigned short end=((snd[n*2+2] | (snd[n*2+3]<<8)))-5;
|
|
|
|
for (unsigned short pos=start; pos<end; pos+=5)
|
|
{
|
|
unsigned short freq, dur;
|
|
dur=(snd[pos+0] | (snd[pos+1]<<8))*4;
|
|
freq=((snd[pos+2] & 0x3F) << 4) + (snd[pos+3] & 0x0F);
|
|
if (snd[pos+2]>0)
|
|
{
|
|
double fr;
|
|
int note;
|
|
// I don't know, what frequency equals midi note 0 ...
|
|
// This moves the song 3 octaves down:
|
|
fr=(log10(111860.0 / (double)freq) / ll)-36;
|
|
note=(int)floor(fr);
|
|
if (note<0) note=0;
|
|
if (note>127) note=127;
|
|
// note on
|
|
WRITE_DELTA(0);
|
|
out << Q_UINT8(0x90+n)
|
|
<< Q_UINT8(note)
|
|
<< Q_UINT8(100);
|
|
// note off
|
|
WRITE_DELTA(dur);
|
|
out << Q_UINT8(0x80+n)
|
|
<< Q_UINT8(note)
|
|
<< Q_UINT8(0);
|
|
}
|
|
else
|
|
{
|
|
// note on
|
|
WRITE_DELTA(0);
|
|
out << Q_UINT8(0x90+n)
|
|
<< Q_UINT8(0)
|
|
<< Q_UINT8(0);
|
|
// note off
|
|
WRITE_DELTA(dur);
|
|
out << Q_UINT8(0x80+n)
|
|
<< Q_UINT8(0)
|
|
<< Q_UINT8(0);
|
|
}
|
|
}
|
|
|
|
WRITE_DELTA(0);
|
|
out << Q_UINT8(0xff)
|
|
<< Q_UINT8(0x2f)
|
|
<< Q_UINT8(0x0);
|
|
|
|
qlonglong ep=write_to.at();
|
|
write_to.at(lp);
|
|
out << Q_UINT32((ep-lp)-4);
|
|
write_to.at(ep);
|
|
}
|
|
|
|
#undef WRITE_DELTA
|
|
}
|
|
|
|
static unsigned char s_selected_instr[3] = {0,0,0};
|
|
|
|
// Show a "Save as" file dialog and call the Midi export function
|
|
void showSaveAsMidi( QWidget* parent, const byte* snd )
|
|
{
|
|
class MyFileDialog : public Q3FileDialog
|
|
{
|
|
public:
|
|
MyFileDialog( QWidget* parent, const char* name ) :
|
|
Q3FileDialog( parent, name )
|
|
{
|
|
QLabel* label = new QLabel( "Channel instruments", this );
|
|
label->setAlignment( AlignAuto | AlignTop | ExpandTabs );
|
|
Q3VBox* butts = new Q3VBox(this);
|
|
butts->setSpacing ( 2 );
|
|
for (int i=0; i<3; ++i )
|
|
{
|
|
instr[i] = new Q3ComboBox( butts );
|
|
instr[i]->insertStrList( g_gm_instrument_names );
|
|
instr[i]->setCurrentItem( s_selected_instr[i] );
|
|
}
|
|
addWidgets( label, butts, 0 );
|
|
}
|
|
Q3ComboBox* instr[3];
|
|
} fd( parent, NULL );
|
|
|
|
fd.setMode( Q3FileDialog::AnyFile );
|
|
fd.setFilter( "MIDI files (*.mid *.midi)" );
|
|
|
|
if ( fd.exec() == QDialog::Accepted )
|
|
{
|
|
QString fname = fd.selectedFile();
|
|
if ( fname.find( '.' ) < 0 )
|
|
fname += ".mid";
|
|
|
|
QFile f( fname );
|
|
f.open( QIODevice::WriteOnly );
|
|
|
|
for (int i=0; i<3; ++i )
|
|
s_selected_instr[i] = (unsigned char)fd.instr[i]->currentItem();
|
|
|
|
writeMidi(snd, f, s_selected_instr);
|
|
f.close();
|
|
}
|
|
}
|