view pmd_play.c @ 4:c8875256b767

Use stdio for console output, update usage, and use sensible exit values.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 21 May 2013 12:10:27 +0200
parents c55ea9478c80
children c4218fbe158f
line wrap: on
line source

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <math.h>
#include <getopt.h>
#if defined(WIN32) || defined(WIN64)
#include <windows.h>
#include <mmsystem.h>
int wave_out_open(HWAVEOUT *hWaveOut, uint32_t rate, uint8_t channels);
void wave_out_close(HWAVEOUT hWaveOut);
void writeAudio(HWAVEOUT hWaveOut, void *data, uint32_t size);
#else
int oss_audio_open(uint32_t oss_audio_rate, uint8_t nchannels);
int alsa_audio_open(uint32_t *rate, uint8_t channels, uint32_t *_ver, int mode);
int alsa_audio_close(int snd_fd);
long snd_pcm_writei(int snd_fd, const void *buffer, unsigned long size);
int snd_pcm_prepare(int snd_fd);
#ifdef USE_ALSA
#include <errno.h>
#endif
#endif
#include "pmdwin.h"

#ifndef O_BINARY
#define O_BINARY 0
#endif

static uint32_t atoui(const uint8_t *str) {
    uint32_t c, num = 0;
    while(1) {
        c = *str++ - 0x30;
        if(c > 9) break;
        num = (num << 1) + (num << 3) + c;
    }
    return num;
}

static const unsigned char static_hdr_portion[20] = {
   0x52, 0x49, 0x46, 0x46, 0xFF, 0xFF, 0xFF, 0x7F,
   0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20,
   0x10, 0x00, 0x00, 0x00
};

typedef struct _WAVHeader {
    uint16_t wav_id;
    uint16_t channels;
    uint32_t samplerate;
    uint32_t bitrate;
    uint32_t block_align;
    uint32_t pad0;
    uint32_t pad1;
} __attribute__((packed)) WAVHeader;

static void write_wav_header(int fd, uint32_t rate)
{
   WAVHeader w;
   write(fd, &static_hdr_portion, 20);

   w.wav_id = 1;
   w.channels = 1;
   w.samplerate = rate;
   w.bitrate = rate*2;
   w.block_align = 0x00100010;
   w.pad0 = 0x61746164;
   w.pad1 = 0x7fffffff;
   write(fd, &w, sizeof(WAVHeader));
}

//=============================================================================
// Load PMD data from file into memory. Uses mmap() on both Windows and *nix.
// Originally in pmdwin.cpp, moved here to minimize platform-specific code
// in pmdwin.cpp
//=============================================================================
#if defined(WIN32) || defined(WIN64)
static int music_load(char *filename)
{
    HANDLE mapped_file, h;
    int     result;
    size_t size;
    uchar *musbuf;
    struct stat st;

    mapped_file = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    h = CreateFileMapping(mapped_file, NULL, PAGE_READWRITE, 0, 0, NULL);
    if(!h) {
        return ERR_OPEN_MUSIC_FILE;
    }
    stat(filename, &st);
    musbuf = (uchar*)MapViewOfFile(h, FILE_MAP_READ, 0, 0, 0);
    CloseHandle(h);
    CloseHandle(mapped_file);
    size = st.st_size;
    result = music_load3(musbuf, size);
    return result;
}
#else
static int music_load(char *filename)
{
    int fd = -1;
    int     result;
    size_t size;
    uchar musbuf[mdata_def*1024];

    if((fd = open(filename, O_RDONLY)) < 0) {
        return ERR_OPEN_MUSIC_FILE;
    }
    
    size = read(fd, musbuf, sizeof(musbuf));
    close(fd);
    result = music_load3(musbuf, size);
    return result;
}
#endif

static void usage(char *argv0) {
    const char *usage_str = 
"%s [-f <output file>] [-l <loop count>] [-m <device mask>] [-c <channel mask>] [-t <time>] [-r <samplerate>] <PMD File>\n\
       -f - Write output to wavfile\n\
       -l - Loop n times (default is once - use 0 for infinite loop)\n\
       -m - Device mask: 1 - OPNA, 2 - PSG, 4 - Rhythm (or them together)\n\
       -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\
       -t - Play song for n seconds\n\
       -r - Set the output sample rate\n\n";
    fprintf(stderr, usage_str, argv0);
}

int main(int argc, char **argv) {
    uint32_t i, frameno, samplerate_out = SOUND_44K, infloop = 0;
    uint32_t pmd_length = 0, pmd_loopcount = 2;
    int opt, j, out_fd = -1;
    uint8_t tofile = 0;
    int16_t pcmbuf[8192];
    char pmd_title[1024];
    char pmd_compo[1024];
#if defined(WIN32) || defined(WIN64)
    HWAVEOUT hWaveOut;
#endif
    char buf[1024];
	pmdwininit();

    while((opt = getopt(argc, argv, "f:l:m:c:t:r:")) != -1) {
        switch(opt) {
            case 'f':
              tofile = 1;
              out_fd = open(optarg, O_WRONLY|O_CREAT|O_APPEND|O_BINARY, 0644);
              break;
            case 'l':
              pmd_loopcount = atoui((uint8_t*)optarg);
              if(!pmd_loopcount) infloop = 1;
              break;
            case 'c':
              setchanmask(atoui((uint8_t*)optarg));
              break;
            case 'm':
              setdevmask(atoui((uint8_t*)optarg));
              break;
            case 't':
              pmd_length = atoui((uint8_t*)optarg)*1000;
              break;
			case 'r':
			  samplerate_out = atoui((uint8_t*)optarg);
			  break;
            default:
              usage(argv[0]);
              return 1;
        }
    }

    if(!tofile) {
#if defined(WIN32) || defined(WIN64)
        if(!wave_out_open(&hWaveOut, samplerate_out, 1)) {
#else
#ifdef USE_ALSA
		if((out_fd = alsa_audio_open(&samplerate_out, 1, NULL, 0)) < 0) {
#else
        if((out_fd = oss_audio_open(samplerate_out, 1)) < 0) {
#endif
#endif
            fprintf(stderr, "Cannot open sound device, exiting.\n");
            return 3;
        }
    } else {
        write_wav_header(out_fd, samplerate_out);
    }
    if (!argv[optind] || optind + 1 != argc) {
        usage(argv[0]);
        return 1;
    }
    if (music_load(argv[optind]) == ERR_OPEN_MUSIC_FILE) {
        fprintf(stderr, "Cannot open music file ā€œ%sā€, exiting.\n", argv[optind]);
        return 2;
    }
    getmemo3(pmd_title, NULL, 0, 1);
#if defined(WIN32) || defined(WIN64)
    SetConsoleOutputCP(65001); // UTF-8
#endif
    printf("Title = %s\n", pmd_title);
    getmemo3(pmd_compo, NULL, 0, 2);
    printf("Composer = %s\n", pmd_compo);
	setpcmrate(samplerate_out);
    music_start();
    i = 0;
    if(pmd_length != 0) {
        frameno = lrintf(((float)pmd_length*(float)samplerate_out)/(4096.0f*1000.0f));
    } else {
        frameno = 0;
    }
    while(getloopcount() < pmd_loopcount || infloop || i < frameno) {
        getstatus(buf, 1023);
        printf("\r%s", buf);
        fflush(stdout);
        getpcmdata(pcmbuf, 4096);
#if defined(WIN32) || defined(WIN64)
        if(!tofile) {
            writeAudio(hWaveOut, pcmbuf, 8192);
        } else
#endif
#ifdef USE_ALSA
        if(!tofile) {
            j = snd_pcm_writei(out_fd, pcmbuf, 2048);
            if(j == -EPIPE) {
            	snd_pcm_prepare(out_fd);
            }
        } else
#endif
            write(out_fd, pcmbuf, 8192);
        i++;
    }
    fputc('\n', stdout);
#if defined(WIN32) || defined(WIN64)
    if(!tofile) {
        wave_out_close(hWaveOut);
    } else
#endif
#ifdef USE_ALSA
		alsa_audio_close(out_fd);
#else
        close(out_fd);
#endif
    return 0;
}