- Timidity++ is now fully integrated into the softsynth interface.

The only thing left to do is tweaking the volume of the output to match the one from the EXE.
This commit is contained in:
Christoph Oelckers 2018-02-21 17:35:44 +01:00
parent 445e9451cb
commit e6bae25423
11 changed files with 20 additions and 1381 deletions

View File

@ -835,9 +835,7 @@ set( FASTMATH_SOURCES
gl/models/gl_models.cpp
r_data/models/models.cpp
r_data/matrix.cpp
# These will be removed later.
sound/timiditypp/w32_a.cpp
)
set (PCH_SOURCES
@ -1172,7 +1170,6 @@ set (PCH_SOURCES
sound/timidity/playmidi.cpp
sound/timidity/resample.cpp
sound/timidity/timidity.cpp
sound/timiditypp/aq.cpp
sound/timiditypp/common.cpp
sound/timiditypp/configfile.cpp
sound/timiditypp/effect.cpp
@ -1181,7 +1178,6 @@ set (PCH_SOURCES
sound/timiditypp/instrum.cpp
sound/timiditypp/mblock.cpp
sound/timiditypp/mix.cpp
sound/timiditypp/output.cpp
sound/timiditypp/playmidi.cpp
sound/timiditypp/quantity.cpp
sound/timiditypp/readmidic.cpp

View File

@ -214,7 +214,7 @@ void TimidityPPMIDIDevice::HandleLongEvent(const uint8_t *data, int len)
void TimidityPPMIDIDevice::ComputeOutput(float *buffer, int len)
{
if (Renderer != nullptr)
Renderer->get_output(buffer, len);
Renderer->compute_data(buffer, len);
sampletime += len;
}

View File

@ -1,329 +0,0 @@
/*
TiMidity++ -- MIDI to WAVE converter and player
Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
aq.c - Audio queue.
Written by Masanao Izumo <mo@goice.co.jp>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "timidity.h"
#include "common.h"
#include "aq.h"
#include "instrum.h"
#include "playmidi.h"
#include "effect.h"
namespace TimidityPlus
{
#define TEST_SPARE_RATE 0.9
#define MAX_BUCKET_TIME 0.2
#define MAX_FILLED_TIME 2.0
AudioQueue::AudioQueue(PlayMode *pm, int bufsize, Reverb *reverb)
{
playMode = pm;
buffer_size = bufsize;
effect = new Effect(reverb);
}
AudioQueue::~AudioQueue()
{
freeSoftQueue();
delete effect;
}
void AudioQueue::setup(void)
{
int ch = 2; // stereo only
/* Initialize Bps, bucket_size, device_qsize, and bucket_time */
if (playMode->encoding & PE_24BIT)
Bps = 3 * ch;
else
Bps = 2 * ch;
bucket_size = buffer_size * Bps;
bucket_time = (double)bucket_size / Bps / playMode->rate;
device_qsize = 0;
freeSoftQueue();
nbuckets = 0;
effect->init_effect();
aq_add_count = 0;
}
void AudioQueue::setSoftQueue(double soft_buff_time, double fill_start_time)
{
int nb;
/* for re-initialize */
if (soft_buff_time < 0)
soft_buff_time = last_soft_buff_time;
if (fill_start_time < 0)
fill_start_time = last_fill_start_time;
nb = (int)(soft_buff_time / bucket_time);
if (nb == 0)
aq_start_count = 0;
else
aq_start_count = (int32_t)(fill_start_time * playMode->rate);
aq_fill_buffer_flag = (aq_start_count > 0);
if (nbuckets != nb)
{
nbuckets = nb;
allocSoftQueue();
}
last_soft_buff_time = soft_buff_time;
last_fill_start_time = fill_start_time;
}
/* Send audio data to playMode->output_data() */
int AudioQueue::outputData(char *buff, int nbytes)
{
int i;
play_counter += nbytes / Bps;
while (nbytes > 0)
{
i = nbytes;
if (i > bucket_size)
i = bucket_size;
if (playMode->output_data(buff, i) == -1)
return -1;
nbytes -= i;
buff += i;
}
return 0;
}
int AudioQueue::add(int32_t *samples, int32_t count)
{
int32_t nbytes, i;
char *buff;
if (!count)
{
return 0;
}
aq_add_count += count;
effect->do_effect(samples, count);
nbytes = general_output_convert(samples, count);
buff = (char *)samples;
if (device_qsize == 0)
return playMode->output_data(buff, nbytes);
aq_fill_buffer_flag = (aq_add_count <= aq_start_count);
while ((i = addPlayBucket(buff, nbytes)) < nbytes)
{
buff += i;
nbytes -= i;
if (head && head->len == bucket_size)
{
if (fillOne() == -1)
return -1;
}
aq_fill_buffer_flag = 0;
}
return 0;
}
/* alloc_soft_queue() (re-)initializes audio buckets. */
void AudioQueue::allocSoftQueue(void)
{
int i;
char *base;
freeSoftQueue();
base_buckets = new AudioBucket[nbuckets];
base = new char[nbuckets * bucket_size];
for (i = 0; i < nbuckets; i++)
base_buckets[i].data = base + i * bucket_size;
flushBuckets();
}
void AudioQueue::freeSoftQueue(void)
{
if (base_buckets)
{
delete[] base_buckets[0].data;
delete[] base_buckets;
base_buckets = NULL;
}
}
/* aq_fill_one() transfers one audio bucket to device. */
int AudioQueue::fillOne(void)
{
AudioBucket *tmp;
if (head == NULL)
return 0;
if (outputData(head->data, bucket_size) == -1)
return -1;
tmp = head;
head = head->next;
reuseAudioBucket(tmp);
return 0;
}
int32_t AudioQueue::softFilled(void)
{
int32_t bytes;
AudioBucket *cur;
bytes = 0;
for (cur = head; cur != NULL; cur = cur->next)
bytes += cur->len;
return bytes / Bps;
}
int AudioQueue::softFlush(void)
{
while (head)
{
if (head->len < bucket_size)
{
/* Add silence code */
memset(head->data + head->len, 0, bucket_size - head->len);
head->len = bucket_size;
}
if (fillOne() == -1)
return RC_ERROR;
}
return RC_OK;
}
int AudioQueue::flush(int discard)
{
aq_add_count = 0;
effect->init_effect();
if (discard)
{
if (playMode->acntl(PM_REQ_DISCARD, NULL) != -1)
{
flushBuckets();
return RC_OK;
}
}
playMode->acntl(PM_REQ_FLUSH, NULL);
flushBuckets();
return RC_OK;
}
/* add_play_bucket() attempts to add buf to audio bucket.
* It returns actually added bytes.
*/
int AudioQueue::addPlayBucket(const char *buf, int n)
{
int total;
if (n == 0)
return 0;
if (!nbuckets) {
playMode->output_data((char *)buf, n);
return n;
}
if (head == NULL)
head = tail = nextAllocatedBucket();
total = 0;
while (n > 0)
{
int i;
if (tail->len == bucket_size)
{
AudioBucket *b;
if ((b = nextAllocatedBucket()) == NULL)
break;
if (head == NULL)
head = tail = b;
else
tail = tail->next = b;
}
i = bucket_size - tail->len;
if (i > n)
i = n;
memcpy(tail->data + tail->len, buf + total, i);
total += i;
n -= i;
tail->len += i;
}
return total;
}
/* Flush and clear audio bucket */
void AudioQueue::flushBuckets(void)
{
int i;
allocated_bucket_list = NULL;
for (i = 0; i < nbuckets; i++)
reuseAudioBucket(&base_buckets[i]);
head = tail = NULL;
aq_fill_buffer_flag = (aq_start_count > 0);
play_counter = play_offset_counter = 0;
}
/* next_allocated_bucket() gets free bucket. If all buckets is used, it
* returns NULL.
*/
AudioQueue::AudioBucket *AudioQueue::nextAllocatedBucket(void)
{
AudioBucket *b;
if (allocated_bucket_list == NULL)
return NULL;
b = allocated_bucket_list;
allocated_bucket_list = allocated_bucket_list->next;
b->len = 0;
b->next = NULL;
return b;
}
/* Reuse specified bucket */
void AudioQueue::reuseAudioBucket(AudioBucket *bucket)
{
bucket->next = allocated_bucket_list;
allocated_bucket_list = bucket;
}
}

View File

@ -1,97 +0,0 @@
/*
TiMidity++ -- MIDI to WAVE converter and player
Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
aq.h - Audio queue.
Written by Masanao Izumo <mo@goice.co.jp>
*/
#ifndef ___AQ_H_
#define ___AQ_H_
#include <stdint.h>
namespace TimidityPlus
{
/* interfaces */
class Effect;
class Reverb;
struct PlayMode;
class AudioQueue
{
struct AudioBucket
{
char *data;
int len;
AudioBucket *next;
};
Effect *effect;
PlayMode *playMode;
int buffer_size = 0;
int32_t device_qsize = 0;
int Bps = 0; /* Bytes per sample frame */
int bucket_size = 0;
int nbuckets = 0;
double bucket_time = 0;
int aq_fill_buffer_flag = 0;
int32_t aq_start_count = 0;
int32_t aq_add_count = 0;
int32_t play_counter = 0, play_offset_counter = 0;
double play_start_time = 0;
AudioBucket *base_buckets = NULL;
AudioBucket *allocated_bucket_list = NULL;
AudioBucket *head = NULL;
AudioBucket *tail = NULL;
void allocSoftQueue(void);
int addPlayBucket(const char *buf, int n);
void reuseAudioBucket(AudioBucket *bucket);
AudioBucket *nextAllocatedBucket(void);
void flushBuckets(void);
int fillOne(void);
int outputData(char *buff, int nbytes);
double last_soft_buff_time, last_fill_start_time;
public:
AudioQueue(PlayMode *pm, int buffersize, Reverb *reverb); // play_mode, audio_buffer_size
~AudioQueue();
void setup(); // allocates the buffer for software queue, and estimate maxmum queue size of audio device.
void setSoftQueue(double soft_buff_time, double fill_start_time); // makes software audio queue. If fill_start_time is positive, TiMidity doesn't start playing immidiately until the autio buffer is filled.
int add(int32_t *samples, int32_t count); // adds new samples to software queue. If samples is NULL, only updates internal software queue buffer.
int32_t softFilled(void); // returns filled queue length of software buffer.
int flush(int discard); // If discard is true, discards all audio queue and returns immediately, otherwise waits until play all out.
int softFlush(void); // transfers all buffer to device
void freeSoftQueue(void); // free soft_que memory
int fillBufferFlag() // non-zero if aq->add() is in filling mode
{
return aq_fill_buffer_flag;
}
};
}
#endif /* ___AQ_H_ */

View File

@ -1,92 +0,0 @@
/*
TiMidity++ -- MIDI to WAVE converter and player
Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
output.c
Audio output (to file / device) functions.
*/
#include "timidity.h"
#include "common.h"
#include "sysdep.h"
#include "tables.h"
namespace TimidityPlus
{
int audio_buffer_bits = DEFAULT_AUDIO_BUFFER_BITS;
extern PlayMode w32_play_mode;
#define DEV_PLAY_MODE &w32_play_mode
extern PlayMode raw_play_mode;
PlayMode *play_mode = DEV_PLAY_MODE;
/*****************************************************************/
/* Some functions to convert signed 32-bit data to other formats */
void s32tos16(int32_t *lp, int32_t c)
{
int16_t *sp=(int16_t *)(lp);
int32_t l, i;
for(i = 0; i < c; i++)
{
l=(lp[i])>>(32-16-GUARD_BITS);
if (l > 32767) l=32767;
else if (l<-32768) l=-32768;
sp[i] = (int16_t)(l);
}
}
// This only gets used as intermediate so we do not care about byte order
#define STORE_S24(cp, l) *cp++ = l & 0xFF, *cp++ = l >> 8 & 0xFF, *cp++ = l >> 16
#define MAX_24BIT_SIGNED (8388607)
#define MIN_24BIT_SIGNED (-8388608)
void s32tos24(int32_t *lp, int32_t c)
{
uint8_t *cp = (uint8_t *)(lp);
int32_t l, i;
for(i = 0; i < c; i++)
{
l = (lp[i]) >> (32 - 24 - GUARD_BITS);
l = (l > MAX_24BIT_SIGNED) ? MAX_24BIT_SIGNED
: (l < MIN_24BIT_SIGNED) ? MIN_24BIT_SIGNED : l;
STORE_S24(cp, l);
}
}
/* return: number of bytes */
int32_t general_output_convert(int32_t *buf, int32_t count)
{
int32_t bytes;
count *= 2; /* Stereo samples */
bytes = count;
bytes *= 2;
s32tos16(buf, count);
return bytes;
}
}

View File

@ -1,92 +0,0 @@
/*
TiMidity++ -- MIDI to WAVE converter and player
Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
output.h
*/
#ifndef ___OUTPUT_H_
#define ___OUTPUT_H_
namespace TimidityPlus
{
/* Data format encoding bits */
#define PE_16BIT (1u<<2) /* versus 8-bit */
#define PE_24BIT (1u<<6) /* versus 8-bit, 16-bit */
/* for play_mode->acntl() */
enum {
PM_REQ_DISCARD, /* ARG: not-used
* Discard the audio device buffer and returns
* immediatly.
*/
PM_REQ_FLUSH, /* ARG: not-used
* Wait until all audio data is out.
*/
};
/* Flag bits */
#define PF_PCM_STREAM (1u<<0) /* Enable output PCM data */
#define PF_BUFF_FRAGM_OPT (1u<<3) /* Enable set extra_param[0] to specify
the number of audio buffer fragments */
#define PF_AUTO_SPLIT_FILE (1u<<4) /* Split PCM files automatically */
#define PF_FILE_OUTPUT (1u<<5) /* Output is to file rather than device */
struct PlayMode {
int32_t rate, encoding, flag;
int fd; /* file descriptor for the audio device
-1 means closed otherwise opened. It must be -1 by default. */
int32_t extra_param[5]; /* System depended parameters
e.g. buffer fragments, ... */
const char *id_name;
char id_character;
const char *name; /* default device or file name */
int (* open_output)(void); /* 0=success, 1=warning, -1=fatal error */
void (* close_output)(void);
int (* output_data)(char *buf, int32_t bytes);
/* return: -1=error, otherwise success */
int (* acntl)(int request, void *arg); /* see PM_REQ_* above
* return: 0=success, -1=fail
*/
int (* detect)(void); /* 0=not available, 1=available */
};
extern PlayMode *play_mode_list[], *play_mode;
/* 16-bit */
extern void s32tos16(int32_t *lp, int32_t c);
/* 24-bit */
extern void s32tos24(int32_t *lp, int32_t c);
extern int32_t general_output_convert(int32_t *buf, int32_t count);
}
#endif /* ___OUTPUT_H_ */

View File

@ -32,7 +32,6 @@
#include "mix.h"
#include "recache.h"
#include "reverb.h"
#include "aq.h"
#include "freq.h"
#include "quantity.h"
#include "c_cvars.h"
@ -135,8 +134,6 @@ Player::Player(int freq, Instruments *instr)
mixer = new Mixer(this);
recache = new Recache(this);
aq = new AudioQueue(play_mode, AUDIO_BUFFER_SIZE, reverb);
aq->setup();
for (int i = 0; i < MAX_CHANNELS; i++)
init_channel_layer(i);
@ -190,8 +187,6 @@ Player::~Player()
reuse_mblock(&playmidi_pool);
if (reverb_buffer != nullptr) free(reverb_buffer);
for (int i = 0; i < MAX_CHANNELS; i++) free_drum_effect(i);
aq->flush(1);
delete aq;
delete mixer;
delete recache;
delete effect;
@ -2670,7 +2665,8 @@ void Player::midi_program_change(int ch, int prog)
channel[ch].program = (instruments->defaultProgram(ch) == SPECIAL_PROGRAM)
? SPECIAL_PROGRAM : prog;
channel[ch].altassign = NULL;
if (opt_realtime_playing && (play_mode->flag & PF_PCM_STREAM)) {
if (opt_realtime_playing)
{
b = channel[ch].bank, p = prog;
instruments->instrument_map(channel[ch].mapID, &b, &p);
play_midi_load_instrument(0, b, p);
@ -5021,46 +5017,24 @@ void Player::do_compute_data(int32_t count)
current_sample += count;
}
int Player::send_output(int32_t *samples, int32_t count)
{
aq->add(samples, count);
/*
effect->do_effect(samples, count);
// pass to caller
for (int i = 0; i < count && output_len > 0; i++)
{
*output_buffer++ = (*samples++)*(1.f / 0x80000000u);
*output_buffer++ = (*samples++)*(1.f / 0x80000000u);
output_len--;
}
*/
memset(output_buffer, 0, output_len * 8);
return RC_OK;
}
int Player::compute_data(int32_t count)
int Player::compute_data(float *buffer, int32_t count)
{
if (count == 0) return RC_OK;
if (!buffered_count) buffer_pointer = common_buffer;
buffer_pointer = common_buffer;
while ((count + buffered_count) >= AUDIO_BUFFER_SIZE)
while (count > 0)
{
do_compute_data(AUDIO_BUFFER_SIZE - buffered_count);
count -= AUDIO_BUFFER_SIZE - buffered_count;
int process = std::min(count, AUDIO_BUFFER_SIZE);
do_compute_data(process);
count -= process;
if (aq->add(common_buffer, AUDIO_BUFFER_SIZE) == -1)
return RC_ERROR;
buffer_pointer = common_buffer;
buffered_count = 0;
}
if (count > 0)
{
do_compute_data(count);
buffered_count += count;
buffer_pointer += count * 2;
effect->do_effect(common_buffer, process);
// pass to caller
for (int i = 0; i < process*2; i++)
{
*buffer++ = (common_buffer[i])*(5.f / 0x80000000u);
}
}
return RC_OK;
}
@ -5157,32 +5131,11 @@ void Player::update_legato_controls(int ch)
int Player::play_event(MidiEvent *ev)
{
int32_t i, j, cet;
int32_t i, j;
int k, l, ch, orig_ch, port_ch, offset, layered;
//current_event = ev;
cet = MIDI_EVENT_TIME(ev);
if (cet > current_sample)
{
int rc = RC_OK;
if (midi_streaming != 0 && (cet - current_sample) * 1000 / playback_rate > stream_max_compute)
{
kill_all_voices();
current_sample = cet;
}
//Printf("Computing %d samples\n", cet - current_sample);
rc = compute_data(cet - current_sample);
if (rc != RC_OK)
return rc;
}
current_event = ev;
//Printf("Playing event %d, time %d\n", ev->type, ev->time);
#ifndef SUPPRESS_CHANNEL_LAYER
orig_ch = ev->channel;
layered = !IS_SYSEX_EVENT_TYPE(ev);
@ -5896,9 +5849,7 @@ void Player::playmidi_tmr_reset(void)
{
int i;
aq->flush(0);
current_sample = 0;
buffered_count = 0;
buffer_pointer = common_buffer;
for(i = 0; i < MAX_CHANNELS; i++)
channel[i].lasttime = 0;
@ -6061,14 +6012,6 @@ void Player::init_channel_layer(int ch)
channel[ch].port_select = ch >> 4;
}
void Player::get_output(float *buffer, int len)
{
output_buffer = buffer;
output_len = len;
//Printf("Not Computing %d samples\n", len);
//compute_data(len);
}
static const struct ctl_chg_types {
unsigned char mtype;

View File

@ -512,7 +512,6 @@ class Recache;
class Mixer;
class Reverb;
class Effect;
class AudioQueue;
class Player
{
@ -534,7 +533,6 @@ private:
Mixer *mixer;
Reverb *reverb;
Effect *effect;
AudioQueue *aq;
MidiEvent *current_event;
@ -561,8 +559,6 @@ private:
int current_freq_table;
int current_temper_freq_table;
int master_tuning;
float *output_buffer;
int output_len;
int make_rvid_flag; /* For reverb optimization */
@ -577,7 +573,6 @@ private:
int32_t lost_notes, cut_notes;
int32_t common_buffer[AUDIO_BUFFER_SIZE * 2], *buffer_pointer; /* stereo samples */
int16_t wav_buffer[AUDIO_BUFFER_SIZE * 2];
int32_t buffered_count;
int32_t insertion_effect_buffer[AUDIO_BUFFER_SIZE * 2];
@ -663,15 +658,12 @@ private:
void do_compute_data(int32_t count);
int check_midi_play_end(MidiEvent *e, int len);
int midi_play_end(void);
int compute_data(int32_t count);
void update_modulation_wheel(int ch);
void drop_portamento(int ch);
void update_portamento_time(int ch);
void update_legato_controls(int ch);
void set_master_tuning(int tune);
struct midi_file_info *new_midi_file_info();
int send_output(int32_t *samples, int32_t count);
void adjust_amplification(void);
void init_freq_table_user(void);
@ -740,7 +732,7 @@ public:
void recompute_freq(int v);
int get_default_mapID(int ch);
void init_channel_layer(int ch);
void get_output(float *buffer, int len);
int compute_data(float *buffer, int32_t count);
int send_event(int time, int status, int parm1, int parm2);
void send_long_event(int sampletime, const uint8_t *sysexbuffer, int exlen);
};

View File

@ -253,20 +253,9 @@ void initialize_resampler_coeffs(void)
initialize_newton_coeffs();
initialize_gauss_table(gauss_n);
/* we don't have to initialize newton table any more */
/* bounds checking values for the appropriate sample types */
/* this is as good a place as any to initialize them */
if (play_mode->encoding & PE_24BIT)
{
sample_bounds_min = -8388608;
sample_bounds_max = 8388607;
}
else /* 16-bit */
{
sample_bounds_min = -32768;
sample_bounds_max = 32767;
}
sample_bounds_min = -32768;
sample_bounds_max = 32767;
}

View File

@ -29,7 +29,6 @@
#define TIMIDITY_H_INCLUDED 1
#include "c_cvars.h"
#include "output.h"
#include "controls.h"
#include "mblock.h"

View File

@ -1,670 +0,0 @@
/*
TiMidity++ -- MIDI to WAVE converter and player
Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
w32_a.c
Functions to play sound on the Windows audio driver (Windows 95/98/NT).
Modified by Masanao Izumo <mo@goice.co.jp>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "timidity.h"
/*****************************************************************************************************************************/
/*****************************************************************************************************************************/
#include "instrum.h"
#include "playmidi.h"
#include "mblock.h"
namespace TimidityPlus
{
extern void *safe_malloc(size_t count);
extern CRITICAL_SECTION critSect;
static int opt_wmme_device_id = -2;
UINT uDeviceID;
#define NOT !
static int open_output (void); /* 0=success, 1=warning, -1=fatal error */
static void close_output (void);
static int output_data (char * Data, int32_t Size);
static int acntl (int request, void * arg);
static void print_device_list(void);
#define DATA_BLOCK_SIZE (4 * AUDIO_BUFFER_SIZE)
#define DATA_BLOCK_NUM 8
static int data_block_trunc_size;
struct MMBuffer
{
int Number;
int Prepared; // Non-zero if this buffer has been prepared.
HGLOBAL hData;
void * Data;
HGLOBAL hHead;
WAVEHDR * Head;
struct MMBuffer * Next;
};
static struct MMBuffer * Buffers;
static volatile struct MMBuffer * FreeBuffers;
static volatile int NumBuffersInUse;
static HWAVEOUT hDevice;
static int BufferDelay; // in milliseconds
static const int AllowSynchronousWaveforms = 1;
/*****************************************************************************************************************************/
static void CALLBACK OnPlaybackEvent (HWAVE hWave, UINT Msg, DWORD_PTR UserData, DWORD_PTR Param1, DWORD_PTR Param2);
static void BufferPoolReset (void);
static struct MMBuffer * GetBuffer ();
static void PutBuffer (struct MMBuffer *);
static const char * MMErrorMessage (MMRESULT Result);
static void WaitForBuffer (int WaitForAllBuffers);
/*****************************************************************************************************************************/
static int detect(void);
#define dpm w32_play_mode
PlayMode dpm =
{
44100,
PE_16BIT,
PF_PCM_STREAM|PF_BUFF_FRAGM_OPT,
-1,
{32},
"Windows audio driver", 'd',
NULL,
open_output,
close_output,
output_data,
acntl,
detect
};
/*****************************************************************************************************************************/
CRITICAL_SECTION critSect;
static int open_output(void)
{
int i;
int j;
int IsMono;
WAVEFORMATEX wf;
WAVEOUTCAPS woc;
MMRESULT Result;
UINT DeviceID;
int ret;
InitializeCriticalSection(&critSect);
if( dpm.name != NULL)
ret = sscanf(dpm.name, "%d", &opt_wmme_device_id);
if ( dpm.name == NULL || ret == 0 || ret == EOF)
opt_wmme_device_id = -2;
if (opt_wmme_device_id == -1){
print_device_list();
return -1;
}
/** Check if there is at least one audio device. **/
if (waveOutGetNumDevs() == 0)
{
//ctl_cmsg (CMSG_ERROR, VERB_NORMAL, "No audio devices present!");
return -1;
}
/** They can't mean these. **/
dpm.encoding = PE_16BIT;
IsMono = false;
memset(&wf, 0, sizeof(wf));
wf.wFormatTag = WAVE_FORMAT_PCM;
wf.nChannels = 2;
wf.nSamplesPerSec = dpm.rate;
i = dpm.rate;
j = 4;
i *= j;
data_block_trunc_size = DATA_BLOCK_SIZE - (DATA_BLOCK_SIZE % j);
wf.nAvgBytesPerSec = i;
wf.nBlockAlign = j;
wf.wBitsPerSample = 16;
wf.cbSize = sizeof(WAVEFORMAT);
/** Open the device. **/
{ CHAR b[256]; wsprintf(b, "Opening device...\n"); OutputDebugString(b); }
hDevice = 0;
if (opt_wmme_device_id == -2){
uDeviceID = WAVE_MAPPER;
}else{
uDeviceID= (UINT)opt_wmme_device_id;
}
if (AllowSynchronousWaveforms)
Result = waveOutOpen(&hDevice, uDeviceID, (LPWAVEFORMATEX) &wf, (DWORD_PTR) OnPlaybackEvent, 0, CALLBACK_FUNCTION | WAVE_ALLOWSYNC);
else
Result = waveOutOpen(&hDevice, uDeviceID, (LPWAVEFORMATEX) &wf, (DWORD_PTR) OnPlaybackEvent, 0, CALLBACK_FUNCTION);
if (Result)
{
return -1;
}
else
{ CHAR b[256]; wsprintf(b, "Device opened.\n"); OutputDebugString(b); }
/** Get the device ID. **/
DeviceID = 0;
waveOutGetID(hDevice, &DeviceID);
/** Get the device capabilities. **/
memset(&woc, 0, sizeof(WAVEOUTCAPS));
Result = waveOutGetDevCaps(DeviceID, &woc, sizeof(WAVEOUTCAPS));
//ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Device ID: %d", DeviceID);
//ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Manufacture ID: %d", woc.wMid);
//ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Product ID: %d", woc.wPid);
//ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Driver version: %d", woc.vDriverVersion);
//ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Product name: %s", woc.szPname);
//ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Formats supported: 0x%08X", woc.dwFormats);
//ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Max. channels: %d", woc.wChannels);
//ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Supported features: 0x%08X", woc.dwSupport);
/** Calculate the buffer delay. **/
BufferDelay = AUDIO_BUFFER_SIZE * 4;
BufferDelay = (BufferDelay * 1000) / dpm.rate;
/** Create the buffer pool. **/
Buffers = (struct MMBuffer *) safe_malloc(DATA_BLOCK_NUM * sizeof(struct MMBuffer));
for (i = 0; i < DATA_BLOCK_NUM; i++)
{
struct MMBuffer * b;
b = &Buffers[i];
b->hData = GlobalAlloc(GMEM_ZEROINIT, DATA_BLOCK_SIZE);
b->Data = (WAVEHDR*)GlobalLock (b->hData);
b->hHead = GlobalAlloc(GMEM_ZEROINIT, sizeof(WAVEHDR));
b->Head = (WAVEHDR*)GlobalLock (b->hHead);
}
BufferPoolReset();
/** Set the file descriptor. **/
dpm.fd = 0;
return 0;
}
/*****************************************************************************************************************************/
static void close_output(void)
{
int i;
if (dpm.fd != -1)
{
WaitForBuffer(1);
{ CHAR b[256]; wsprintf(b, "Closing device...\n"); OutputDebugString(b); }
waveOutReset(hDevice);
waveOutClose(hDevice);
{ CHAR b[256]; wsprintf(b, "Device closed.\n"); OutputDebugString(b); }
/** Free all buffers. **/
for (i = 0; i < DATA_BLOCK_NUM; i++)
{
struct MMBuffer * block;
block = &Buffers[i];
GlobalUnlock(block->hHead);
GlobalFree (block->hHead);
GlobalUnlock(block->hData);
GlobalFree (block->hData);
}
free(Buffers);
/** Reset the file descriptor. **/
dpm.fd = -1;
}
}
static int detect(void)
{
if (waveOutGetNumDevs() == 0) {return 0;} /* not found */
return 1; /* found */
}
/*****************************************************************************************************************************/
#ifdef OutputDebugString
#undef OutputDebugString
#endif
void OutputDebugString(LPSTR sre)
{
}
static int output_data(char * Data, int32_t Size)
{
char * d;
int32_t s;
d = Data;
s = Size;
while (s > 0)
{
int32_t n;
struct MMBuffer * b;
MMRESULT Result;
LPWAVEHDR wh;
if ((b = GetBuffer()) == NULL)
{
WaitForBuffer(0);
continue;
}
if (s <= data_block_trunc_size)
n = s;
else
n = data_block_trunc_size;
CopyMemory(b->Data, d, n);
wh = b->Head;
wh->dwBufferLength = n;
wh->lpData = (LPSTR)b->Data;
wh->dwUser = b->Number;
/** Prepare the buffer. **/
{ CHAR b[256]; wsprintf(b, "%2d: Preparing buffer %d...\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); }
Result = waveOutPrepareHeader(hDevice, wh, sizeof(WAVEHDR));
if (Result)
{
{ CHAR b[256]; wsprintf(b, "%2d: Buffer preparation failed.\n", NumBuffersInUse); OutputDebugString(b); }
//ctl_cmsg (CMSG_ERROR, VERB_NORMAL, "waveOutPrepareHeader(): %s", MMErrorMessage(Result));
return -1;
}
else
{ CHAR b[256]; wsprintf(b, "%2d: Buffer %d prepared.\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); }
b->Prepared = 1;
/** Queue the buffer. **/
{ CHAR b[256]; wsprintf(b, "%2d: Queueing buffer %d...\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); }
Result = waveOutWrite(hDevice, wh, sizeof(WAVEHDR));
if (Result)
{
{ CHAR b[256]; wsprintf(b, "%2d: Buffer queueing failed.\n", NumBuffersInUse); OutputDebugString(b); }
//ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "waveOutWrite(): %s", MMErrorMessage(Result));
return -1;
}
else
{ CHAR b[256]; wsprintf(b, "%2d: Buffer %d queued.\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); }
d += n;
s -= n;
}
return 0;
}
/*****************************************************************************************************************************/
static int acntl(int request, void *arg)
{
static char dummy_sounds[4*AUDIO_BUFFER_SIZE];
switch(request)
{
case PM_REQ_DISCARD:
{
{ CHAR b[256]; wsprintf(b, "Resetting audio device.\n"); OutputDebugString(b); }
waveOutReset(hDevice);
close_output();
open_output();
{ CHAR b[256]; wsprintf(b, "Audio device reset.\n"); OutputDebugString(b); }
return 0;
}
case PM_REQ_FLUSH:
{
close_output();
open_output();
return 0;
}
}
return -1;
}
/*****************************************************************************************************************************/
static void CALLBACK OnPlaybackEvent(HWAVE hWave, UINT Msg, DWORD_PTR UserData, DWORD_PTR Param1, DWORD_PTR Param2)
{
//ctl_cmsg(CMSG_INFO, VERB_DEBUG, "Msg: 0x%08X, Num. buffers in use: %d", Msg, NumBuffersInUse);
switch (Msg)
{
case WOM_OPEN:
{ CHAR b[256]; wsprintf(b, "%2d: Device opened.\n", NumBuffersInUse); OutputDebugString(b); }
break;
case WOM_CLOSE:
{ CHAR b[256]; wsprintf(b, "%2d: Device closed.\n", NumBuffersInUse); OutputDebugString(b); }
break;
case WOM_DONE:
{
WAVEHDR * wh;
EnterCriticalSection(&critSect);
wh = (WAVEHDR *) Param1;
/* It's not safe to do this here. Read the remarks of waveOutProc() in the SDK on which functions are safe to call.
if (NOT Queueing)
{
{ CHAR b[256]; wsprintf(b, "%2d: Dequeueing buffer %d...\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); }
waveOutUnprepareHeader(hDevice, wh, sizeof(WAVEHDR));
{ CHAR b[256]; wsprintf(b, "%2d: Buffer %d dequeued.\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); }
}
else
{ CHAR b[256]; wsprintf(b, "%2d: *** Buffer %d not dequeued! ***\n", NumBuffersInUse, wh->dwUser); OutputDebugString(b); }
*/
PutBuffer(&Buffers[wh->dwUser]);
LeaveCriticalSection(&critSect);
break;
}
default:
{
CHAR b[256];
wsprintf(b, "%2d: Unknown play back event 0x%08X.\n", NumBuffersInUse, Msg);
OutputDebugString(b);
}
}
}
/*****************************************************************************************************************************/
#define DIM(a) sizeof(a) / sizeof(a[0])
static const char * mmsyserr_code_string[] =
{
"no error",
"unspecified error",
"device ID out of range",
"driver failed enable",
"device already allocated",
"device handle is invalid",
"no device driver present",
"memory allocation error",
"function isn't supported",
"error value out of range",
"invalid flag passed",
"invalid parameter passed",
"handle being used",
};
static const char * waverr_code_sring[] =
{
"unsupported wave format",
"still something playing",
"header not prepared",
"device is synchronous",
};
static const char * MMErrorMessage(MMRESULT ErrorCode)
{
static char s[32];
if (ErrorCode >= WAVERR_BASE)
{
ErrorCode -= WAVERR_BASE;
if (ErrorCode > DIM(waverr_code_sring))
{
wsprintf(s, "Unknown wave error %d", ErrorCode);
return s;
}
else
return waverr_code_sring[ErrorCode];
}
else
if (ErrorCode > DIM(mmsyserr_code_string))
{
wsprintf(s, "Unknown multimedia error %d", ErrorCode);
return s;
}
else
return mmsyserr_code_string[ErrorCode];
}
#undef DIM
/*****************************************************************************************************************************/
static void BufferPoolReset(void)
{
int i;
{ CHAR b[256]; wsprintf(b, "Resetting buffer pool...\n"); OutputDebugString(b); }
Buffers[0].Number = 0;
Buffers[0].Prepared = 0;
Buffers[0].Next = &Buffers[1];
for (i = 1; i < DATA_BLOCK_NUM - 1; i++)
{
Buffers[i].Number = i;
Buffers[i].Prepared = 0;
Buffers[i].Next = &Buffers[i + 1];
}
Buffers[i].Number = i;
Buffers[i].Prepared = 0;
Buffers[i].Next = NULL;
FreeBuffers = &Buffers[0];
NumBuffersInUse = 0;
{ CHAR b[256]; wsprintf(b, "Buffer pool reset.\n", NumBuffersInUse); OutputDebugString(b); }
}
/*****************************************************************************************************************************/
static struct MMBuffer * GetBuffer()
{
struct MMBuffer * b;
{ CHAR b[256]; wsprintf(b, "%2d: Getting buffer...\n", NumBuffersInUse); OutputDebugString(b); }
EnterCriticalSection(&critSect);
if (FreeBuffers)
{
b = (struct MMBuffer *)FreeBuffers;
FreeBuffers = FreeBuffers->Next;
NumBuffersInUse++;
/** If this buffer is still prepared we can safely unprepare it because we got it from the free buffer list. **/
if (b->Prepared)
{
waveOutUnprepareHeader(hDevice, (LPWAVEHDR) b->Head, sizeof(WAVEHDR));
b->Prepared = 0;
}
b->Next = NULL;
}
else
b = NULL;
LeaveCriticalSection(&critSect);
{ CHAR b[256]; wsprintf(b, "%2d: Got buffer.\n", NumBuffersInUse); OutputDebugString(b); }
return b;
}
/*****************************************************************************************************************************/
static void PutBuffer(struct MMBuffer * b)
{
{ CHAR b[256]; wsprintf(b, "%2d: Putting buffer...\n", NumBuffersInUse); OutputDebugString(b); }
b->Next = (struct MMBuffer *)FreeBuffers;
FreeBuffers = b;
NumBuffersInUse--;
{ CHAR b[256]; wsprintf(b, "%2d: Buffer put.\n", NumBuffersInUse); OutputDebugString(b); }
}
/*****************************************************************************************************************************/
static void WaitForBuffer(int WaitForAllBuffers)
{
int numbuf;
if (WaitForAllBuffers)
{
{ CHAR b[256]; wsprintf(b, "%2d: Waiting for all buffers to be dequeued...\n", NumBuffersInUse); OutputDebugString(b); }
while (1) {
EnterCriticalSection(&critSect);
numbuf = NumBuffersInUse;
if (numbuf) {
LeaveCriticalSection(&critSect);
Sleep(BufferDelay);
continue;
}
break;
}
LeaveCriticalSection(&critSect);
// while (NumBuffersInUse)
// Sleep(BufferDelay);
{ CHAR b[256]; wsprintf(b, "%2d: All buffers dequeued.\n", NumBuffersInUse); OutputDebugString(b); }
BufferPoolReset();
}
else
{
{ CHAR b[256]; wsprintf(b, "%2d: Waiting %dms...\n", NumBuffersInUse, BufferDelay); OutputDebugString(b); }
Sleep(BufferDelay);
{ CHAR b[256]; wsprintf(b, "%2d: Wait finished.\n", NumBuffersInUse); OutputDebugString(b); }
}
}
/*****************************************************************************************************************************/
#define DEVLIST_MAX 20
static void print_device_list(void){
UINT num;
int i, list_num;
WAVEOUTCAPS woc;
typedef struct tag_DEVICELIST{
int deviceID;
char name[256];
} DEVICELIST;
DEVICELIST device[DEVLIST_MAX];
num = waveOutGetNumDevs();
list_num=0;
for(i = 0 ; i < (int)num && i < DEVLIST_MAX ; i++){
if (MMSYSERR_NOERROR == waveOutGetDevCaps((UINT)i, &woc, sizeof(woc)) ){
device[list_num].deviceID=i;
strcpy(device[list_num].name, woc.szPname);
list_num++;
}
}
for(i=0;i<list_num;i++){
//ctl_cmsg(CMSG_ERROR, VERB_NORMAL, "%2d %s", device[i].deviceID, device[i].name);
}
}
}