Mercurial > pmdwin
annotate pmd_play.c @ 10:e3849cd10ad3 default tip
Build an intermediary fmgen.a archive.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Mon, 08 Sep 2014 17:44:53 +0200 |
parents | c4218fbe158f |
children |
rev | line source |
---|---|
4
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
1 #include <stdio.h> |
0 | 2 #include <stdint.h> |
3 #include <string.h> | |
4 #include <fcntl.h> | |
5 #include <unistd.h> | |
6 #include <sys/stat.h> | |
7 #include <math.h> | |
8 #include <getopt.h> | |
9 #if defined(WIN32) || defined(WIN64) | |
10 #include <windows.h> | |
11 #include <mmsystem.h> | |
12 int wave_out_open(HWAVEOUT *hWaveOut, uint32_t rate, uint8_t channels); | |
13 void wave_out_close(HWAVEOUT hWaveOut); | |
14 void writeAudio(HWAVEOUT hWaveOut, void *data, uint32_t size); | |
15 #else | |
16 int oss_audio_open(uint32_t oss_audio_rate, uint8_t nchannels); | |
17 int alsa_audio_open(uint32_t *rate, uint8_t channels, uint32_t *_ver, int mode); | |
18 int alsa_audio_close(int snd_fd); | |
19 long snd_pcm_writei(int snd_fd, const void *buffer, unsigned long size); | |
20 int snd_pcm_prepare(int snd_fd); | |
21 #ifdef USE_ALSA | |
22 #include <errno.h> | |
23 #endif | |
24 #endif | |
25 #include "pmdwin.h" | |
26 | |
27 #ifndef O_BINARY | |
28 #define O_BINARY 0 | |
29 #endif | |
30 | |
31 static uint32_t atoui(const uint8_t *str) { | |
32 uint32_t c, num = 0; | |
33 while(1) { | |
34 c = *str++ - 0x30; | |
35 if(c > 9) break; | |
36 num = (num << 1) + (num << 3) + c; | |
37 } | |
38 return num; | |
39 } | |
40 | |
41 static const unsigned char static_hdr_portion[20] = { | |
42 0x52, 0x49, 0x46, 0x46, 0xFF, 0xFF, 0xFF, 0x7F, | |
43 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, | |
44 0x10, 0x00, 0x00, 0x00 | |
45 }; | |
46 | |
47 typedef struct _WAVHeader { | |
48 uint16_t wav_id; | |
49 uint16_t channels; | |
50 uint32_t samplerate; | |
51 uint32_t bitrate; | |
52 uint32_t block_align; | |
53 uint32_t pad0; | |
54 uint32_t pad1; | |
55 } __attribute__((packed)) WAVHeader; | |
56 | |
57 static void write_wav_header(int fd, uint32_t rate) | |
58 { | |
59 WAVHeader w; | |
60 write(fd, &static_hdr_portion, 20); | |
61 | |
62 w.wav_id = 1; | |
63 w.channels = 1; | |
64 w.samplerate = rate; | |
65 w.bitrate = rate*2; | |
66 w.block_align = 0x00100010; | |
67 w.pad0 = 0x61746164; | |
68 w.pad1 = 0x7fffffff; | |
69 write(fd, &w, sizeof(WAVHeader)); | |
70 } | |
71 | |
72 //============================================================================= | |
73 // Load PMD data from file into memory. Uses mmap() on both Windows and *nix. | |
74 // Originally in pmdwin.cpp, moved here to minimize platform-specific code | |
75 // in pmdwin.cpp | |
76 //============================================================================= | |
77 #if defined(WIN32) || defined(WIN64) | |
78 static int music_load(char *filename) | |
79 { | |
80 HANDLE mapped_file, h; | |
81 int result; | |
82 size_t size; | |
83 uchar *musbuf; | |
84 struct stat st; | |
85 | |
86 mapped_file = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |
87 h = CreateFileMapping(mapped_file, NULL, PAGE_READWRITE, 0, 0, NULL); | |
88 if(!h) { | |
89 return ERR_OPEN_MUSIC_FILE; | |
90 } | |
91 stat(filename, &st); | |
92 musbuf = (uchar*)MapViewOfFile(h, FILE_MAP_READ, 0, 0, 0); | |
93 CloseHandle(h); | |
94 CloseHandle(mapped_file); | |
95 size = st.st_size; | |
96 result = music_load3(musbuf, size); | |
97 return result; | |
98 } | |
99 #else | |
100 static int music_load(char *filename) | |
101 { | |
102 int fd = -1; | |
103 int result; | |
104 size_t size; | |
105 uchar musbuf[mdata_def*1024]; | |
106 | |
107 if((fd = open(filename, O_RDONLY)) < 0) { | |
108 return ERR_OPEN_MUSIC_FILE; | |
109 } | |
5
c4218fbe158f
Fix indent in pmd_play.c.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
4
diff
changeset
|
110 |
0 | 111 size = read(fd, musbuf, sizeof(musbuf)); |
112 close(fd); | |
113 result = music_load3(musbuf, size); | |
114 return result; | |
115 } | |
116 #endif | |
117 | |
4
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
118 static void usage(char *argv0) { |
5
c4218fbe158f
Fix indent in pmd_play.c.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
4
diff
changeset
|
119 const char *usage_str = |
4
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
120 "%s [-f <output file>] [-l <loop count>] [-m <device mask>] [-c <channel mask>] [-t <time>] [-r <samplerate>] <PMD File>\n\ |
0 | 121 -f - Write output to wavfile\n\ |
122 -l - Loop n times (default is once - use 0 for infinite loop)\n\ | |
123 -m - Device mask: 1 - OPNA, 2 - PSG, 4 - Rhythm (or them together)\n\ | |
124 -c - Channel mask: 1,2,4,8,16,32 are OPNA channels 1-6, 64,128,256 are PSG channels 1-3. Once again, or them together\n\ | |
4
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
125 -t - Play song for n seconds\n\ |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
126 -r - Set the output sample rate\n\n"; |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
127 fprintf(stderr, usage_str, argv0); |
0 | 128 } |
129 | |
130 int main(int argc, char **argv) { | |
131 uint32_t i, frameno, samplerate_out = SOUND_44K, infloop = 0; | |
132 uint32_t pmd_length = 0, pmd_loopcount = 2; | |
133 int opt, j, out_fd = -1; | |
134 uint8_t tofile = 0; | |
135 int16_t pcmbuf[8192]; | |
136 char pmd_title[1024]; | |
137 char pmd_compo[1024]; | |
138 #if defined(WIN32) || defined(WIN64) | |
139 HWAVEOUT hWaveOut; | |
140 #endif | |
141 char buf[1024]; | |
5
c4218fbe158f
Fix indent in pmd_play.c.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
4
diff
changeset
|
142 pmdwininit(); |
0 | 143 |
144 while((opt = getopt(argc, argv, "f:l:m:c:t:r:")) != -1) { | |
145 switch(opt) { | |
146 case 'f': | |
147 tofile = 1; | |
148 out_fd = open(optarg, O_WRONLY|O_CREAT|O_APPEND|O_BINARY, 0644); | |
149 break; | |
150 case 'l': | |
151 pmd_loopcount = atoui((uint8_t*)optarg); | |
152 if(!pmd_loopcount) infloop = 1; | |
153 break; | |
154 case 'c': | |
155 setchanmask(atoui((uint8_t*)optarg)); | |
156 break; | |
157 case 'm': | |
158 setdevmask(atoui((uint8_t*)optarg)); | |
159 break; | |
160 case 't': | |
161 pmd_length = atoui((uint8_t*)optarg)*1000; | |
162 break; | |
5
c4218fbe158f
Fix indent in pmd_play.c.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
4
diff
changeset
|
163 case 'r': |
c4218fbe158f
Fix indent in pmd_play.c.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
4
diff
changeset
|
164 samplerate_out = atoui((uint8_t*)optarg); |
c4218fbe158f
Fix indent in pmd_play.c.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
4
diff
changeset
|
165 break; |
0 | 166 default: |
4
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
167 usage(argv[0]); |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
168 return 1; |
0 | 169 } |
170 } | |
171 | |
172 if(!tofile) { | |
173 #if defined(WIN32) || defined(WIN64) | |
174 if(!wave_out_open(&hWaveOut, samplerate_out, 1)) { | |
175 #else | |
176 #ifdef USE_ALSA | |
5
c4218fbe158f
Fix indent in pmd_play.c.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
4
diff
changeset
|
177 if((out_fd = alsa_audio_open(&samplerate_out, 1, NULL, 0)) < 0) { |
0 | 178 #else |
179 if((out_fd = oss_audio_open(samplerate_out, 1)) < 0) { | |
180 #endif | |
181 #endif | |
4
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
182 fprintf(stderr, "Cannot open sound device, exiting.\n"); |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
183 return 3; |
0 | 184 } |
185 } else { | |
186 write_wav_header(out_fd, samplerate_out); | |
187 } | |
4
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
188 if (!argv[optind] || optind + 1 != argc) { |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
189 usage(argv[0]); |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
190 return 1; |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
191 } |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
192 if (music_load(argv[optind]) == ERR_OPEN_MUSIC_FILE) { |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
193 fprintf(stderr, "Cannot open music file ā%sā, exiting.\n", argv[optind]); |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
194 return 2; |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
195 } |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
196 getmemo3(pmd_title, NULL, 0, 1); |
0 | 197 #if defined(WIN32) || defined(WIN64) |
198 SetConsoleOutputCP(65001); // UTF-8 | |
199 #endif | |
4
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
200 printf("Title = %s\n", pmd_title); |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
201 getmemo3(pmd_compo, NULL, 0, 2); |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
202 printf("Composer = %s\n", pmd_compo); |
5
c4218fbe158f
Fix indent in pmd_play.c.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
4
diff
changeset
|
203 setpcmrate(samplerate_out); |
0 | 204 music_start(); |
205 i = 0; | |
206 if(pmd_length != 0) { | |
207 frameno = lrintf(((float)pmd_length*(float)samplerate_out)/(4096.0f*1000.0f)); | |
208 } else { | |
209 frameno = 0; | |
210 } | |
211 while(getloopcount() < pmd_loopcount || infloop || i < frameno) { | |
4
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
212 getstatus(buf, 1023); |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
213 printf("\r%s", buf); |
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
214 fflush(stdout); |
0 | 215 getpcmdata(pcmbuf, 4096); |
216 #if defined(WIN32) || defined(WIN64) | |
217 if(!tofile) { | |
218 writeAudio(hWaveOut, pcmbuf, 8192); | |
219 } else | |
220 #endif | |
221 #ifdef USE_ALSA | |
222 if(!tofile) { | |
223 j = snd_pcm_writei(out_fd, pcmbuf, 2048); | |
224 if(j == -EPIPE) { | |
5
c4218fbe158f
Fix indent in pmd_play.c.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
4
diff
changeset
|
225 snd_pcm_prepare(out_fd); |
0 | 226 } |
227 } else | |
228 #endif | |
229 write(out_fd, pcmbuf, 8192); | |
230 i++; | |
231 } | |
4
c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
0
diff
changeset
|
232 fputc('\n', stdout); |
0 | 233 #if defined(WIN32) || defined(WIN64) |
234 if(!tofile) { | |
235 wave_out_close(hWaveOut); | |
236 } else | |
237 #endif | |
238 #ifdef USE_ALSA | |
5
c4218fbe158f
Fix indent in pmd_play.c.
Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
parents:
4
diff
changeset
|
239 alsa_audio_close(out_fd); |
0 | 240 #else |
241 close(out_fd); | |
242 #endif | |
243 return 0; | |
244 } | |
245 |