55280f7f526c7cdeed53af40983de9f028db20d2
[sdk] / samples / audio / S3MPlayer / S3MPlayer.ec
1 import "EcereAudio"
2
3 static uint16 periods[] = { 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 907};
4 static const String notes[12] = { "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-"};
5
6 struct S3MHeader
7 {
8    byte name[28];
9    byte byte1A;
10    byte typ;
11    uint16 junk;
12    uint16 ordNum;
13    uint16 insNum;
14    uint16 patNum;
15    uint16 flags;
16    uint16 created;
17    uint16 format;
18    byte sign[4];
19    byte globalVolume;
20    byte initSpeed;
21    byte initTempo;
22    byte masterVolume;
23    byte ultraClicks;
24    byte defaultPan;
25    byte junk2[8];
26    uint16 special;
27    byte channelSettings[32];
28 } __attribute__((packed));
29
30 struct InstrumentHeader
31 {
32    byte type;
33    byte filename[13];
34    uint16 memSeg;
35    uint32 length;
36    uint32 loopBegin;
37    uint32 loopEnd;
38    byte volume;
39    byte junk;
40    byte pack;
41    byte flags;
42    uint32 c2spd;
43    byte junk2[4];
44    uint16 intGP;
45    uint16 int512;
46    uint32 lastUsed;
47    byte name[28];
48    byte sign[4];
49 } __attribute__((packed));
50
51 struct Instrument
52 {
53    InstrumentHeader header;
54    Sound sound;
55 };
56
57 struct Note
58 {
59    byte note;
60    byte ins;
61    byte vol;
62    byte com;
63    byte info;
64 } __attribute__((packed));
65
66 struct Pattern
67 {
68    Note channels[32][64];
69 };
70
71 class S3M
72 {
73    S3MHeader header;
74    Array<byte> orders { };
75    Array<uint16> insPTR { };
76    Array<uint16> patPTR { };
77    byte panPos[4];
78    Array<Instrument> instruments { };
79    Array<Pattern> patterns { };
80    Voice voices[32];
81
82    bool Load(const char *fileName)
83    {
84       int i,p,c;
85       byte what, row, channel;
86       uint16 length;
87
88       //Let's begin by opening the file...
89       File f = FileOpen(fileName, read);
90       if(!f)
91          return false;
92       printf("*** Loading S3M ******************\n");
93       printf("Loading header...\n");
94       //Load header
95       f.Read(&header, sizeof(header), 1);
96
97       printf("Loading Order List...\n");
98       //Load Order List and pointers to instruments and patterns
99       orders.size = header.ordNum;
100       insPTR.size = header.insNum;
101       patPTR.size = header.patNum;
102
103       f.Read(orders.array, sizeof(byte), header.ordNum);
104       f.Read(insPTR.array, sizeof(uint16), header.insNum);
105       f.Read(patPTR.array, sizeof(uint16), header.patNum);
106
107       //Load channels panning positions
108       f.Read(panPos, sizeof(panPos), 1);
109
110       printf("Loading instruments...\n");
111       //Load instruments
112       instruments.size = header.insNum;
113       for(i = 0; i<header.insNum; i++)
114       {
115          Sound sound { };
116          Instrument * ins = &instruments[i];
117          //Read Instrument header
118          f.Seek(insPTR[i] << 4, start);
119          f.Read(&ins->header, sizeof(InstrumentHeader), 1);
120
121          ins->sound = sound;
122          //Allocate and Read Instrument Sound
123          sound.data = new byte[ins->header.length];
124          f.Seek(ins->header.memSeg << 4, start);
125          f.Read(sound.data, ins->header.length, 1);
126          sound.channels = (ins->header.flags & 2) ? 2 : 1;
127          sound.bits = (ins->header.flags & 4) ? 16 : 8;
128          sound.length = ins->header.length;
129          sound.frequency = 22150*2;
130
131          printf("%i: %s\n", i, ins->header.name);
132       }
133
134       printf("Loading patterns...\n");
135       //Load patterns
136       patterns.size = header.patNum;
137       if(!patterns)
138          printf("Not enough memory to load patterns!!\n");
139       for(p=0; p<header.patNum; p++)
140       {
141          Pattern * pat = &patterns[p];
142          for(row=0; row<64; row++)
143             for(c=0; c<32; c++)
144             {
145                pat->channels[c][row].note=0;
146                pat->channels[c][row].ins=0;
147                pat->channels[c][row].vol=255;
148                pat->channels[c][row].com=255;
149                pat->channels[c][row].info=0;
150             }
151          //Read Pattern
152          f.Seek(patPTR[p] << 4, start);
153          row=0;
154          f.Read(&length, sizeof(uint16), 1);
155          for(;;)
156          {
157             f.Read(&what, sizeof(byte), 1);
158             if(!what)
159             {
160                row++;
161                if(row==64)
162                   break;
163                else
164                   continue;
165             }
166             channel = what&31;
167             if(what&32)
168             {
169                f.Read(&pat->channels[channel][row].note, sizeof(byte), 1);
170                f.Read(&pat->channels[channel][row].ins, sizeof(byte), 1);
171             }
172             if(what&64)
173             {
174                f.Read(&pat->channels[channel][row].vol, sizeof(byte), 1);
175             }
176             if(what&128)
177             {
178                f.Read(&pat->channels[channel][row].com, sizeof(byte), 1);
179                f.Read(&pat->channels[channel][row].info, sizeof(byte), 1);
180             }
181          }
182          printf("Loading Pattern %i\n", p);
183       }
184       delete f;
185       return true;
186    }
187
188    void PrintOut(Surface surface, uint16 pattern)
189    {
190       uint16 row, channel, note, octave, ins;
191       Pattern * pat = &patterns[pattern];
192       surface.WriteTextf(5,5, "Pattern %d", pattern);
193       for(row=0; row<64; row++)
194       {
195          for(channel=0; channel<32; channel++)
196          {
197             int x = (channel*8+2) * 8;
198             int y = (row+2) * 16;
199             note   =pat->channels[channel][row].note&0x0F;
200             octave=(pat->channels[channel][row].note&0xF0)>>4;
201             ins   =pat->channels[channel][row].ins;
202             if(ins && note < 12)
203                surface.WriteTextf(x, y, "%s%i %i", notes[note], octave, octave);
204             else
205                surface.WriteTextf(x, y, "... ..");
206          }
207       }
208    }
209
210    ~S3M()
211    {
212       for(i : instruments)
213       {
214          delete i.sound;
215       }
216    }
217
218    void Stop(Mixer mixer, int channel)
219    {
220       Voice voice = voices[channel];
221       if(voice)
222       {
223          mixer.Wait();
224          voice.looped = false;
225          voice.pos = voice.loopEnd;
226          mixer.Release();
227       }
228    }
229
230    void PlayNote(Mixer mixer, Instrument ins, uint16 note, uint16 octave, byte volume, uint16 channel)
231    {
232       uint32 note_st3period;
233       double vol = volume/255.0;
234       double bal = 0;
235       //while(note > 12) { note -= 12; octave++; }
236       mixer.Wait();
237       if(note < 12 && ins)
238       {
239          note_st3period=(8363*16*((uint32)(periods[note])>>octave))/(uint32)ins.header.c2spd;
240          //if(note_st3period)
241          {
242             /*SND_SetLoop(ins->header.Flags&1, ins->header.LoopBegin, ins->header.LoopEnd, channel);
243             SND_SetFreq((uint32)(14317056L / note_st3period), channel);
244             if(volume!=255)
245                SND_SetVolume(volume, channel);
246             SND_Play(ins.sound, channel);
247             */
248             //int c2spd = ins.header.c2spd;
249             //double freq = pow(Do_, note) * pow(2, octave-4) / (c2spd/8363.0);
250             double freq = 14317456.0 / note_st3period / 22150/2;
251             //double freq = 500.0/note_st3period;
252             Voice v = voices[channel-1];
253
254             if(!v)
255             {
256                v = mixer.Play(ins.sound, vol, bal, freq);
257                voices[channel-1] = v;
258                incref v;
259             }
260             else
261                mixer.PlayInVoice(v, ins.sound, vol, bal, freq);
262             v.pos = offsets[channel-1];
263             v.loopStart = ins.header.loopBegin;
264             v.loopEnd = ins.header.loopEnd;
265             v.looped = ins.header.flags & 1;
266          }
267       }
268       else
269       {
270          Voice v = voices[channel-1];
271          if(v)
272          {
273             v.volume = vol;
274          }
275       }
276       mixer.Release();
277    }
278
279    Time lastTick;
280    uint16 order, pattern, row, channel;
281    int volumes[32];
282    Instrument *lastInst[32];
283    order = 0;
284    int speed;
285    speed = 6;
286    int slides[32];
287    int tempo;
288    int offsets[32];
289
290    bool Play(Mixer mixer)
291    {
292       bool result = false;
293       // int initSpeed = header.initSpeed;
294       Time time = /*pattern == 10*/ 1? speed/(1.1*45.5) : 0;
295       Time current = GetTime();
296
297       if(!lastTick)
298       {
299          int i;
300          for(i = 0; i < 32; i ++)
301             volumes[i] = 63;
302          lastTick = current;
303          pattern = orders[order];
304          tempo = header.initTempo;
305          speed = header.initSpeed;
306          /*
307          SND_SetPB(&Instruments[0].Sound);
308          SND_StartPB();
309          for(channel=0; channel<32; channel++)
310             SND_SetVolume(64, channel+1);
311          */
312       }
313
314       if((current-lastTick) >= time)
315       {
316          Pattern * pat = &patterns[pattern];
317          for(channel=0; channel<32; channel++)
318          {
319             Note * n = &pat->channels[channel][row];
320             bool gotVolume = false;
321             int com = n->com;
322             int info = n->info;
323             uint16 note = n->note&0x0F;
324             uint16 octave = (n->note&0xF0)>>4;
325             int ins = n->ins;
326             int volume = volumes[channel];
327             int vol = n->vol;
328
329             if(octave == 0xF && note == 0xE)
330             {
331                Stop(mixer, channel);
332             }
333             else if(vol!=255)
334             {
335                if(ins)
336                {
337                   volumes[channel] = volume = vol;
338                   gotVolume = true;
339                }
340                else
341                   Stop(mixer, channel);
342             }
343             if(com != 255 && com)
344             {
345                char fx = 'A'+(char)com-1;
346                printf("%c%02X\n", fx, info);
347                switch(fx)
348                {
349                   case 'A':
350                   {
351                      speed = info;
352                      break;
353                   }
354                   case 'D':
355                   {
356                      if((info & 0x0F) == 0x0)
357                         slides[channel] = (info >> 4);
358                      else if((info & 0x0F) == 0xF)
359                      {
360                         volume += (info >> 4);
361                         if(volume > 63) volume = 63;
362                         volumes[channel] = volume;
363                         gotVolume = true;
364                      }
365                      else if((info & 0xF0) == 0x0)
366                         slides[channel] = -(info & 0x0F);
367                      else if((info & 0xF0) == 0xF)
368                      {
369                         volume -= (info & 0x0F);
370                         if(volume < 0) volume = 0;
371                         volumes[channel] = volume;
372                         gotVolume = true;
373                      }
374                      break;
375                   }
376                   case 'O':
377                   {
378                      offsets[channel] = info*256;
379                      break;
380                   }
381                }
382             }
383             if(slides[channel])
384             {
385                volume += slides[channel] * (speed-1);
386                volumes[channel] = volume;
387                if(volume > 63) volume = 63;
388                if(volume < 0) volume = 0;
389                gotVolume = true;
390             }
391
392             if(ins || gotVolume)
393             {
394                lastInst[channel] = &instruments[ins-1];
395                PlayNote(mixer, ins ? &instruments[ins-1] : null, note, octave, lastInst[channel] ? (byte)(volume*lastInst[channel]->header.volume/63) : (byte)volume, channel+1);
396             }
397             offsets[channel] = 0;
398          }
399          row++;
400          if(row>63)
401          {
402             for(channel=0; channel<32; channel++)
403             {
404                volumes[channel] = 63;
405                slides[channel] = 0;
406                speed = header.initSpeed;
407             }
408             row=0;
409             order++;
410             while(orders[order]==255)
411             {
412                order++;
413                if(order >= header.ordNum)
414                     break;
415             }
416             if(order >= header.ordNum)
417             {
418                order=0;
419
420                for(channel=0; channel<32; channel++)
421                   volumes[channel] = 63;
422             }
423             pattern = orders[order];
424          }
425          lastTick = current;
426          result = true;
427       }
428       return result;
429    }
430 }
431
432 // There are 12 half-tones in an octave, and the frequency doubles in an octave.
433 define Do = 1.0;
434 define Do_ = 1.0594630943592952645618252949463; // The root 12 of 2.
435 define Re = Do_*Do_;
436 define Re_ = Re*Do_;
437 define Mi = Re_*Do_;
438 define Fa = Mi*Do_;
439 define Fa_ = Fa*Do_;
440 define Sol = Fa_*Do_;
441 define Sol_ = Sol*Do_;
442 define La = Sol_*Do_;
443 define La_ = La*Do_;
444 define Si = La_*Do_;
445
446
447 class MainWindow : Window
448 {
449    text = "S3M Player";
450    background = black;
451    borderStyle = sizable;
452    hasMaximize = true;
453    hasMinimize = true;
454    hasClose = true;
455    size = { 1024, 1120 };
456
457    Mixer mixer { };
458    Sound instrument;
459    S3M s3m { };
460
461    bool OnCreate()
462    {
463       mixer.systemHandle = systemHandle;
464       //s3m.Load("2ND_KEV.S3M");
465       //s3m.Load("2nd_pm.s3m");
466       //s3m.Load("theweird.s3m");
467       //s3m.Load("forgivme.s3m");
468       //s3m.Load("quiadroi.s3m");
469       //s3m.Load("everyido.s3m");
470       //s3m.Load("saywords.s3m");
471       s3m.Load("keven1.s3m");
472       //s3m.Load("trans.s3m");
473
474       instrument = s3m.instruments[0].sound;
475       return true;
476    }
477
478    void OnDestroy()
479    {
480       delete mixer;
481    }
482
483    Timer pbTimer
484    {
485       this, 0.01, true;
486
487       bool DelayExpired()
488       {
489          if(s3m.Play(mixer))
490          {
491             Update(null);
492          }
493          return true;
494       }
495    };
496
497    bool OnKeyDown(Key key, unichar ch)
498    {
499       switch(key)
500       {
501          case f1: instrument = s3m.instruments[0].sound; break;
502          case f2: instrument = s3m.instruments[1].sound; break;
503
504          // The regular octave on the zxcvbn row, sharps above (asdf)
505          case z:     mixer.Play(instrument, 1.0, -1, Do); break;
506          case x:     mixer.Play(instrument, 1.0, -.8, Re); break;
507          case c:     mixer.Play(instrument, 1.0, -.6, Mi); break;
508          case v:     mixer.Play(instrument, 1.0, -.4, Fa); break;
509          case b:     mixer.Play(instrument, 1.0, -.2, Sol); break;
510          case n:     mixer.Play(instrument, 1.0, 0, La); break;
511          case m:     mixer.Play(instrument, 1.0, .2, Si); break;
512          case comma: mixer.Play(instrument, 1.0, .4, Do*2); break;
513          case period:mixer.Play(instrument, 1.0, .6, Re*2); break;
514          case slash: mixer.Play(instrument, 1.0, .8, Mi*2); break;
515          case s:     mixer.Play(instrument, 1.0, -.9, Do_); break;
516          case d:     mixer.Play(instrument, 1.0, -.7, Re_); break;
517          case g:     mixer.Play(instrument, 1.0, -.3, Fa_); break;
518          case h:     mixer.Play(instrument, 1.0, -.1, Sol_); break;
519          case j:     mixer.Play(instrument, 1.0, .1, La_); break;
520          case l:     mixer.Play(instrument, 1.0, .5, Do_*2); break;
521          case colon: mixer.Play(instrument, 1.0, .7, Re_*2); break;
522
523          // The lower octave on the qwerty row, sharps above (digits)
524          case q:     mixer.Play(instrument, 1.0, 0, Do/2); break;
525          case w:     mixer.Play(instrument, 1.0, 0, Re/2); break;
526          case e:     mixer.Play(instrument, 1.0, 0, Mi/2); break;
527          case r:     mixer.Play(instrument, 1.0, 0, Fa/2); break;
528          case t:     mixer.Play(instrument, 1.0, 0, Sol/2); break;
529          case y:     mixer.Play(instrument, 1.0, 0, La/2); break;
530          case u:     mixer.Play(instrument, 1.0, 0, Si/2); break;
531          case i:     mixer.Play(instrument, 1.0, 0, Do); break;
532          case o:     mixer.Play(instrument, 1.0, 0, Re); break;
533          case p:     mixer.Play(instrument, 1.0, 0, Mi); break;
534          case k2:     mixer.Play(instrument, 1.0, 0, Do_/2); break;
535          case k3:     mixer.Play(instrument, 1.0, 0, Re_/2); break;
536          case k5:     mixer.Play(instrument, 1.0, 0, Fa_/2); break;
537          case k6:     mixer.Play(instrument, 1.0, 0, Sol_/2); break;
538          case k7:     mixer.Play(instrument, 1.0, 0, La_/2); break;
539          case k9:     mixer.Play(instrument, 1.0, 0, Do_); break;
540          case k0:     mixer.Play(instrument, 1.0, 0, Re_); break;
541       }
542       return true;
543    }
544
545    void OnRedraw(Surface surface)
546    {
547       surface.foreground = mintCream;
548       s3m.PrintOut(surface, s3m.pattern);
549       surface.background = lime;
550       surface.Area(0, (s3m.row+2) * 16, 10, (s3m.row+2) * 16 + 15);
551    }
552 }
553
554 MainWindow mainWindow { };
555
556 class App : GuiApplication
557 {
558    timerResolution = 60;
559 }