-/*\r
- Skelton for retropc emulator\r
-\r
- Author : Takeda.Toshiya\r
- Date : 2007.02.09 -\r
-\r
- [ 1bit PCM ]\r
-*/\r
-\r
-#include "pcm1bit.h"\r
-#include "../fileio.h"\r
-\r
-void PCM1BIT::initialize()\r
-{\r
- signal = false;\r
- on = true;\r
- mute = false;\r
- \r
-#ifdef PCM1BIT_HIGH_QUALITY\r
- sample_count = 0;\r
- prev_clock = 0;\r
- prev_vol = 0;\r
-#endif\r
- update = 0;\r
- \r
- register_frame_event(this);\r
-}\r
-\r
-void PCM1BIT::write_signal(int id, uint32 data, uint32 mask)\r
-{\r
- if(id == SIG_PCM1BIT_SIGNAL) {\r
- bool next = ((data & mask) != 0);\r
- if(signal != next) {\r
-#ifdef PCM1BIT_HIGH_QUALITY\r
- if(sample_count < 1024) {\r
- samples_signal[sample_count] = signal;\r
- samples_out[sample_count] = (on && !mute);\r
- samples_clock[sample_count] = passed_clock(prev_clock);\r
- sample_count++;\r
- }\r
-#endif\r
- // mute if signal is not changed in 2 frames\r
- update = 2;\r
- signal = next;\r
- }\r
- } else if(id == SIG_PCM1BIT_ON) {\r
- on = ((data & mask) != 0);\r
- } else if(id == SIG_PCM1BIT_MUTE) {\r
- mute = ((data & mask) != 0);\r
- }\r
-}\r
-\r
-void PCM1BIT::event_frame()\r
-{\r
- if(update && --update == 0) {\r
-#ifdef PCM1BIT_HIGH_QUALITY\r
- prev_vol = 0;\r
-#endif\r
- }\r
-}\r
-\r
-void PCM1BIT::mix(int32* buffer, int cnt)\r
-{\r
-#ifdef PCM1BIT_HIGH_QUALITY\r
- uint32 cur_clock = current_clock();\r
- if(update) {\r
- if(sample_count < 1024) {\r
- samples_signal[sample_count] = signal;\r
- samples_out[sample_count] = (on && !mute);\r
- samples_clock[sample_count] = passed_clock(prev_clock);\r
- sample_count++;\r
- }\r
- uint32 start_clock = 0;\r
- int start_index = 0;\r
- for(int i = 0; i < cnt; i++) {\r
- uint32 end_clock = ((cur_clock - prev_clock) * (i + 1)) / cnt;\r
- int on_clocks = 0, off_clocks = 0;\r
- for(int s = start_index; s < sample_count; s++) {\r
- uint32 clock = samples_clock[s];\r
- if(clock <= end_clock) {\r
- if(samples_out[s]) {\r
- if(samples_signal[s]) {\r
- on_clocks += clock - start_clock;\r
- } else {\r
- off_clocks += clock - start_clock;\r
- }\r
- }\r
- start_clock = clock;\r
- start_index = s + 1;\r
- } else {\r
- if(samples_out[s]) {\r
- if(samples_signal[s]) {\r
- on_clocks += end_clock - start_clock;\r
- } else {\r
- off_clocks += end_clock - start_clock;\r
- }\r
- }\r
- start_clock = end_clock;\r
- start_index = s;\r
- break;\r
- }\r
- }\r
- int clocks = on_clocks + off_clocks;\r
- if(clocks) {\r
- prev_vol = max_vol * (on_clocks - off_clocks) / clocks;\r
- }\r
- *buffer++ += prev_vol; // L\r
- *buffer++ += prev_vol; // R\r
- }\r
- }\r
- prev_clock = cur_clock;\r
- sample_count = 0;\r
-#else\r
- if(on && !mute && signal) {\r
- for(int i = 0; i < cnt; i++) {\r
- *buffer++ += max_vol; // L\r
- *buffer++ += max_vol; // R\r
- }\r
- }\r
-#endif\r
-}\r
-\r
-void PCM1BIT::init(int rate, int volume)\r
-{\r
- max_vol = volume;\r
-}\r
-\r
-#define STATE_VERSION 1\r
-\r
-void PCM1BIT::save_state(FILEIO* state_fio)\r
-{\r
- state_fio->FputUint32(STATE_VERSION);\r
- state_fio->FputInt32(this_device_id);\r
- \r
- state_fio->FputBool(signal);\r
- state_fio->FputBool(on);\r
- state_fio->FputBool(mute);\r
-#ifdef PCM1BIT_HIGH_QUALITY\r
- state_fio->Fwrite(samples_signal, sizeof(samples_signal), 1);\r
- state_fio->Fwrite(samples_out, sizeof(samples_out), 1);\r
- state_fio->Fwrite(samples_clock, sizeof(samples_clock), 1);\r
- state_fio->FputInt32(sample_count);\r
- state_fio->FputUint32(prev_clock);\r
- state_fio->FputInt32(prev_vol);\r
-#endif\r
- state_fio->FputInt32(update);\r
-}\r
-\r
-bool PCM1BIT::load_state(FILEIO* state_fio)\r
-{\r
- if(state_fio->FgetUint32() != STATE_VERSION) {\r
- return false;\r
- }\r
- if(state_fio->FgetInt32() != this_device_id) {\r
- return false;\r
- }\r
- signal = state_fio->FgetBool();\r
- on = state_fio->FgetBool();\r
- mute = state_fio->FgetBool();\r
-#ifdef PCM1BIT_HIGH_QUALITY\r
- state_fio->Fread(samples_signal, sizeof(samples_signal), 1);\r
- state_fio->Fread(samples_out, sizeof(samples_out), 1);\r
- state_fio->Fread(samples_clock, sizeof(samples_clock), 1);\r
- sample_count = state_fio->FgetInt32();\r
- prev_clock = state_fio->FgetUint32();\r
- prev_vol = state_fio->FgetInt32();\r
-#endif\r
- update = state_fio->FgetInt32();\r
- return true;\r
-}\r
-\r
+/*
+ Skelton for retropc emulator
+
+ Author : Takeda.Toshiya
+ Date : 2007.02.09 -
+
+ [ 1bit PCM ]
+*/
+
+#include "pcm1bit.h"
+
+void PCM1BIT::initialize()
+{
+ signal = false;
+ on = true;
+ mute = false;
+ realtime = false;
+ changed = 0;
+ last_vol_l = last_vol_r = 0;
+
+ register_frame_event(this);
+}
+
+void PCM1BIT::reset()
+{
+ prev_clock = get_current_clock();
+ positive_clocks = negative_clocks = 0;
+}
+
+void PCM1BIT::write_signal(int id, uint32_t data, uint32_t mask)
+{
+ if(id == SIG_PCM1BIT_SIGNAL) {
+ bool next = ((data & mask) != 0);
+ if(signal != next) {
+ touch_sound();
+ if(signal) {
+ positive_clocks += get_passed_clock(prev_clock);
+ } else {
+ negative_clocks += get_passed_clock(prev_clock);
+ }
+ prev_clock = get_current_clock();
+ // mute if signal is not changed in 2 frames
+ changed = 2;
+ signal = next;
+ }
+ } else if(id == SIG_PCM1BIT_ON) {
+ touch_sound();
+ on = ((data & mask) != 0);
+ set_realtime_render(this, on & !mute);
+ } else if(id == SIG_PCM1BIT_MUTE) {
+ touch_sound();
+ mute = ((data & mask) != 0);
+ set_realtime_render(this, on & !mute);
+ }
+}
+
+void PCM1BIT::event_frame()
+{
+ if(changed) {
+ changed--;
+ }
+}
+
+void PCM1BIT::mix(int32_t* buffer, int cnt)
+{
+ if(on && !mute && changed) {
+ if(signal) {
+ positive_clocks += get_passed_clock(prev_clock);
+ } else {
+ negative_clocks += get_passed_clock(prev_clock);
+ }
+ int clocks = positive_clocks + negative_clocks;
+ int sample = clocks ? (max_vol * positive_clocks - max_vol * negative_clocks) / clocks : signal ? max_vol : -max_vol;
+
+ last_vol_l = apply_volume(sample, volume_l);
+ last_vol_r = apply_volume(sample, volume_r);
+
+ for(int i = 0; i < cnt; i++) {
+ *buffer++ += last_vol_l; // L
+ *buffer++ += last_vol_r; // R
+ }
+ } else {
+ // suppress petite noise when go to mute
+ for(int i = 0; i < cnt; i++) {
+ *buffer++ += last_vol_l; // L
+ *buffer++ += last_vol_r; // R
+
+ if(last_vol_l > 0) {
+ last_vol_l--;
+ } else if(last_vol_l < 0) {
+ last_vol_l++;
+ }
+ if(last_vol_r > 0) {
+ last_vol_r--;
+ } else if(last_vol_r < 0) {
+ last_vol_r++;
+ }
+ }
+ }
+ prev_clock = get_current_clock();
+ positive_clocks = negative_clocks = 0;
+}
+
+void PCM1BIT::set_volume(int ch, int decibel_l, int decibel_r)
+{
+ volume_l = decibel_to_volume(decibel_l);
+ volume_r = decibel_to_volume(decibel_r);
+}
+
+void PCM1BIT::initialize_sound(int rate, int volume)
+{
+ max_vol = volume;
+}
+
+#define STATE_VERSION 3
+
+void PCM1BIT::save_state(FILEIO* state_fio)
+{
+ state_fio->FputUint32(STATE_VERSION);
+ state_fio->FputInt32(this_device_id);
+
+ state_fio->FputBool(signal);
+ state_fio->FputBool(on);
+ state_fio->FputBool(mute);
+ state_fio->FputBool(realtime);
+ state_fio->FputInt32(changed);
+ state_fio->FputUint32(prev_clock);
+ state_fio->FputInt32(positive_clocks);
+ state_fio->FputInt32(negative_clocks);
+}
+
+bool PCM1BIT::load_state(FILEIO* state_fio)
+{
+ if(state_fio->FgetUint32() != STATE_VERSION) {
+ return false;
+ }
+ if(state_fio->FgetInt32() != this_device_id) {
+ return false;
+ }
+ signal = state_fio->FgetBool();
+ on = state_fio->FgetBool();
+ mute = state_fio->FgetBool();
+ realtime = state_fio->FgetBool();
+ changed = state_fio->FgetInt32();
+ prev_clock = state_fio->FgetUint32();
+ positive_clocks = state_fio->FgetInt32();
+ negative_clocks = state_fio->FgetInt32();
+
+ // post process
+ last_vol_l = last_vol_r = 0;
+ //touch_sound();
+ set_realtime_render(on & !mute);
+ return true;
+}
+