#include #include #include #include #include #include /* USE INSTRUCTIONS: Change input_filename below Compile Run: audio_transform_tandy > transformed_audio.raw Audio is expected to be 16-bit stereo. The sampling rate is defined below. Why such odd sampling rates like 46305? Frankly, I can't remember. Feel free to change it and see if something breaks. COPYRIGHT (C) 2012 Joel Yliluoma - http://iki.fi/bisqwit/ License: Creative Commons Attribution 3.0 Unported If you retain my copyright message in this file, it'd be nice. */ static const char input_filename[] = "orig_ptal2.raw"; #if 0 static const unsigned rate = 46305; static const unsigned slice_ratio = 1029; // Number of times in second the rate is adjusted static const unsigned fft_length = 512;//4096; #elif 0 static const unsigned rate = 46305; static const unsigned slice_ratio = 245; // Number of times in second the rate is adjusted static const unsigned fft_length = 512;//4096; #elif 0 static const unsigned rate = 46305; static const unsigned slice_ratio = 105; // Number of times in second the rate is adjusted static const unsigned fft_length = 512;//4096; #elif 0 static const unsigned rate = 46305; static const unsigned slice_ratio = 63; // Number of times in second the rate is adjusted static const unsigned fft_length = 768;//4096; #elif 0 static const unsigned rate = 46305; static const unsigned slice_ratio = 45; // Number of times in second the rate is adjusted static const unsigned fft_length = 2048;//4096; #else static const unsigned rate = 46300; static const unsigned slice_ratio = 30; // Number of times in second the rate is adjusted static const unsigned fft_length = 2048;//4096; #endif static const unsigned slice_length = rate / slice_ratio; static const unsigned half_fft = fft_length / 2; static void DoFFT(const double *, double* Out, fftw_plan& plan, double Power[half_fft+1]) { memset(&Out[0], 0, sizeof(double) * fft_length); fftw_execute(plan); Power[0] = fabs(Out[0]); for(unsigned a=1; a 0; ) out *= 1.023292992; /* build volume table (2dB per step) */ for (int i = 0;i < 15; ++i) { /* limit volume to avoid clipping */ if (out > MAX_OUTPUT / 3) VolTable[i] = MAX_OUTPUT / 3; else VolTable[i] = (int)out; out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */ } VolTable[15] = 0; SampleRate = rate; unsigned clock = 3579545; UpdateStep = (unsigned int)(((double)STEP * SampleRate * 16) / clock); for(unsigned i=0; i<4; ++i) { Volume[i] = Output[i] = 0; Period[i] = Count[i] = UpdateStep; //Register[i*2+0] = 0; Register[i*2+1] = 0x0F; V[i] = 0; P[i] = 1; } RNG = NG_PRESET; NoiseFB = FB_WNOISE; Output[3] = RNG&1; } }; struct Synth { struct State { SN76496 chip; } state; void GenerateTo(short* buffer, unsigned length) { /* Uses: Count, Period, RNG, NoiseFB, Output */ SN76496* R = &state.chip; for(int i=0; i<4; ++i) { unsigned p = R->P[i]; if(i == 3) { if(p == 3) p = R->P[2] * 2; else p = 1 << (5 + p); } R->Period[i] = R->UpdateStep * p; R->Volume[i] = R->VolTable[ R->V[i] ]; if(R->Volume[i] == 0) if (R->Count[i] <= (int)length*STEP) R->Count[i] += length*STEP; } unsigned count=length; while (count) { int vol[4], left; unsigned int out; /* vol[] keeps track of how long each square wave stays */ /* in the 1 position during the sample period. */ vol[0] = vol[1] = vol[2] = vol[3] = 0; for (unsigned i = 0;i < 3;i++) { if (R->Output[i]) vol[i] += R->Count[i]; R->Count[i] -= STEP; /* Period[i] is the half period of the square wave. Here, in each */ /* loop I add Period[i] twice, so that at the end of the loop the */ /* square wave is in the same status (0 or 1) it was at the start. */ /* vol[i] is also incremented by Period[i], since the wave has been 1 */ /* exactly half of the time, regardless of the initial position. */ /* If we exit the loop in the middle, Output[i] has to be inverted */ /* and vol[i] incremented only if the exit status of the square */ /* wave is 1. */ while (R->Count[i] <= 0) { R->Count[i] += R->Period[i]; if (R->Count[i] > 0) { R->Output[i] ^= 1; if (R->Output[i]) vol[i] += R->Period[i]; break; } R->Count[i] += R->Period[i]; vol[i] += R->Period[i]; } if (R->Output[i]) vol[i] -= R->Count[i]; } left = STEP; do { int nextevent; if (R->Count[3] < left) nextevent = R->Count[3]; else nextevent = left; if (R->Output[3]) vol[3] += R->Count[3]; R->Count[3] -= nextevent; if (R->Count[3] <= 0) { if (R->RNG & 1) R->RNG ^= R->NoiseFB; R->RNG >>= 1; R->Output[3] = R->RNG & 1; R->Count[3] += R->Period[3]; if (R->Output[3]) vol[3] += R->Period[3]; } if (R->Output[3]) vol[3] -= R->Count[3]; left -= nextevent; } while (left > 0); out = vol[0] * R->Volume[0] + vol[1] * R->Volume[1] + vol[2] * R->Volume[2] + vol[3] * R->Volume[3]; if (out > MAX_OUTPUT * STEP) out = MAX_OUTPUT * STEP; *(buffer++) = (short)(out / STEP); count--; } } }; struct FFTcontext { double In[fft_length]; double Out[fft_length]; fftw_plan plan; FFTcontext() { memset(&Out, 0, sizeof(Out)); plan = fftw_plan_r2r_1d (fft_length, &In[0], &Out[0], FFTW_R2HC, FFTW_MEASURE | FFTW_PATIENT | FFTW_EXHAUSTIVE); memset(&In, 0, sizeof(In)); memset(&Out, 0, sizeof(Out)); } }; static void TryMatchSpectrum(const double RefPower[half_fft+1]) { static Synth synth; static std::vector ctx ( omp_get_num_procs() ); for(auto& c : ctx) for(unsigned a=0; a= 3) break; Synth s = { cur_state }; SN76496* R = &s.state.chip; /* Tweak synthesizers */ if(index == 2 && mode3 != 0) { R->V[3] = vol3; R->P[3] = 3; } R->P[index] = index==3 ? period0-1 : period0; R->V[index] = vol0; /* Synthesize audio */ short SynthBuf[slice_length]; s.GenerateTo(SynthBuf, slice_length); /* Feed audio to FFTW */ for(unsigned a=0; a= 512 && a <= 1024) significance *= 2.0; if(a >= 2048) significance *= 0.5; if(a >= 4096) significance *= 0.5;*/ error += fabs( (TestPower[a]) - (RefPower[a]) ); } #pragma omp critical(test_error) { #pragma omp flush(best_error) if(error < best_error) { /* Save this synthesization candidate */ best_error = error; best_state = s.state; improved = true; fprintf(stderr, "Error %10g with %4u:%2u %4u:%2u %4u:%2u %4u:%2u\n", error, R->P[0], R->V[0], R->P[1], R->V[1], R->P[2], R->V[2], R->P[3], R->V[3]); #pragma omp flush(improved,best_error,best_state) } } if(vol0 == 15 && period0 == 1) break; //if(index == 3 && period0 == 4+1) break; } } for(unsigned i=0; i<4; ++i) { cur_state.chip.P[i] = best_state.chip.P[i]; cur_state.chip.V[i] = best_state.chip.V[i]; } } } /* Render the audio and update the synthesizer */ short SynthBuf[slice_length]; synth.state = base_state; for(unsigned i=0; i<4; ++i) { synth.state.chip.P[i] = best_state.chip.P[i]; synth.state.chip.V[i] = best_state.chip.V[i]; } synth.GenerateTo(SynthBuf, slice_length); fwrite(&SynthBuf, 2, slice_length, stdout); fflush(stdout); for(auto& c: ctx) for(unsigned a=0; a