egedit: do not save cursor movement in undo -- this is my stupid habit, and it comple...
[iv.d.git] / tinysid / tinysid.d
blob1564e8d61fdef2a8faadb2b64fc91893e2893ced
1 module tinysid /*is aliced*/;
3 import core.time;
5 import iv.alice;
6 import iv.cmdcon;
7 import iv.cmdcontty;
8 import iv.follin.resampler;
9 import iv.follin.utils;
10 import iv.simplealsa;
11 import iv.rawtty;
12 import iv.vfs;
14 import sidengine;
17 // ////////////////////////////////////////////////////////////////////////// //
18 enum BUF_SIZE = 882; // Audio Buffer size, in samples
20 __gshared short[BUF_SIZE*16] soundbuffer; // the soundbuffer
21 __gshared SpeexResampler rsm;
24 // ////////////////////////////////////////////////////////////////////////// //
25 void main (string[] args) {
26 if (args.length < 2) {
27 conwriteln("TinySID v0.94");
28 conwriteln("(c)Copyright 1999-2006 T. Hinrichs and R. Sinsch.");
29 conwriteln("All rights reserved.");
30 return;
33 if (ttyIsRedirected) assert(0, "no redirects, please!");
35 concmd("exec tinysid.rc tan");
36 conProcessQueue(); // load config
37 conProcessArgs!true(args);
39 ttySetRaw();
40 scope(exit) ttySetNormal();
41 ttyconInit();
43 alsaDevice = "plug:default";
45 float[] rsfbufi, rsfbufo;
47 int idx = 1;
48 mainloop: while (idx < args.length) {
49 string fname = args[idx];
51 auto fl = VFile(fname);
52 ubyte speed = c64SidGetSpeed(fl);
54 c64Init(/*speed == 1*/);
56 if (!alsaInit(44100, 1)) assert(0, "error initializing ALSA");
57 scope(exit) alsaShutdown(true);
59 conwriteln("ALSA initialized; real sampling rate is ", alsaRealRate, "Hz");
61 conwriteln("Loading '", args[1], "'...");
62 SidSong song;
63 c64SidLoad(fl, song);
65 conwriteln("TITLE : ", song.name);
66 conwriteln("AUTHOR : ", song.author);
67 conwriteln("COPYRIGHT: ", song.copyright);
68 conwriteln("SPEED : ", speed);
70 cpuJSR(song.init_addr, song.sub_song_start);
71 //conwriteln("Playing... Hit ^C to quit.");
73 ushort play_addr = song.play_addr;
74 ubyte play_speed = song.speed;
76 //conwriteln("SPEED : ", play_speed);
78 auto stt = MonoTime.currTime;
79 auto stu = MonoTime.zero;
80 uint msecs = 0;
82 bool playing = true;
83 bool paused = false;
85 if (speed == 1) {
86 rsm.setup(1, 44100*2, 44100, 8);
89 while (playing) {
90 if (!paused) {
91 if (play_speed == 0) {
92 // Single Speed (50Hz); render 16*50Hz buffer
93 foreach (immutable j; 0..8) {
94 cpuJSR(play_addr, 0);
95 synth_render(&soundbuffer[BUF_SIZE*j], BUF_SIZE);
97 } else if (play_speed == 1) {
98 // Double Speed (100Hz); render 16*50Hz buffer
99 foreach (immutable j; 0..16) {
100 cpuJSR(play_addr, 0);
101 synth_render(&soundbuffer[BUF_SIZE/2*j], BUF_SIZE/2);
103 } else {
104 assert(0, "invalid playing speed");
106 alsaWriteShort(soundbuffer[0..BUF_SIZE*8]); // mono
108 foreach (immutable j; 0..8) {
109 cpuJSR(play_addr, 0);
110 synth_render(&soundbuffer[BUF_SIZE*j], BUF_SIZE);
112 if (speed == 1) {
113 // oops, must resample
114 SpeexResampler.Data srbdata;
115 if (rsfbufi.length < BUF_SIZE*8) rsfbufi.length = BUF_SIZE*8;
116 if (rsfbufo.length < BUF_SIZE*8) rsfbufo.length = BUF_SIZE*8;
117 tflShort2Float(soundbuffer[0..BUF_SIZE*8], rsfbufi[0..BUF_SIZE*8]);
118 uint inpos = 0;
119 for (;;) {
120 srbdata = srbdata.init; // just in case
121 srbdata.dataIn = rsfbufi[inpos..BUF_SIZE*8];
122 srbdata.dataOut = rsfbufo[];
123 if (rsm.process(srbdata) != 0) assert(0, "resampling error");
124 if (srbdata.outputSamplesUsed) {
125 tflFloat2Short(rsfbufo[0..srbdata.outputSamplesUsed], soundbuffer[0..srbdata.outputSamplesUsed]);
126 //outSoundFlushX(b, srbdata.outputSamplesUsed*2);
127 //conwriteln("RSM: ", srbdata.outputSamplesUsed);
128 alsaWriteShort(soundbuffer[0..srbdata.outputSamplesUsed]); // mono
129 } else {
130 // no data consumed, no data produced, so we're done
131 if (inpos >= BUF_SIZE*8) break;
133 inpos += cast(uint)srbdata.inputSamplesUsed;
135 } else {
136 alsaWriteShort(soundbuffer[0..BUF_SIZE*8]); // mono
139 } else {
140 soundbuffer[0..BUF_SIZE*8] = 0;
141 alsaWriteShort(soundbuffer[0..BUF_SIZE*8]); // mono
144 while (ttyIsKeyHit) {
145 auto key = ttyReadKey(0, 20);
146 if (!ttyconEvent(key)) {
147 switch (key.key) {
148 case TtyEvent.Key.Char:
149 if (key.ch == '<') { if (idx > 1) { ttyRawWrite("\n"); --idx; continue mainloop; } }
150 if (key.ch == '>') { ttyRawWrite("\n"); ++idx; continue mainloop; }
151 if (key.ch == 'q') { ttyRawWrite("\n"); break mainloop; }
152 if (key.ch == ' ') {
153 paused = !paused;
154 auto ctt = MonoTime.currTime;
155 if (paused) {
156 auto cms = (ctt-stt).total!"msecs"+msecs;
157 msecs = cast(uint)cms;
159 stt = ctt;
161 break;
162 default: break;
168 auto ctt = MonoTime.currTime;
169 auto cms = (ctt-stt).total!"msecs"+msecs;
170 if (cms >= 2*60*1000) break;
171 if ((ctt-stu).total!"seconds" >= 1) {
172 import core.stdc.stdio : snprintf;
173 stu = ctt;
174 char[512] tmp;
175 auto len = snprintf(tmp.ptr, tmp.length, "\r%02u:%02u", cast(uint)(cms/1000/60), cast(uint)(cms/1000%60));
176 ttyRawWrite(tmp[0..len]);
180 ttyRawWrite("\n");
182 ++idx;