/* This is a program that can read in a text file and convert it to a sound file. Since this is going to depend on ffmpeg's libraries, otherwise I am only able to produce raw audio files, which are too large and not very convenient to use, a makefile is included to make the compiling process easier. */ /* include stuff */ #include #include #include /* include FFMPEG stuff */ #include #include #include #include #include /* Self-Dependencies */ #include "util.h" #include "instrument.h" #include "parser.h" /* #include */ /* helper functions */ /* FFMPEG helpers */ /* check that a given sample format is supported by the encoder */ UNUSED static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt) { const enum AVSampleFormat *p = codec->sample_fmts; /* the array is terminated by -1 */ for (int i = 0; *(p+i) != -1; i++) fprintf(stderr, "supports %s\n", av_get_sample_fmt_name(*(p+i))); while (*p != AV_SAMPLE_FMT_NONE) { if (*p == sample_fmt) return 1; p++; } return 0; } __attribute__((__hot__, __unused__)) static void /* encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, * FILE *output) */ encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, FILE *output) { int ret = 0; /* send the frame for encoding */ ret = avcodec_send_frame(ctx, frame); if (ret < 0) { fprintf(stderr, "Error sending the frame to the encoder\n"); exit(1); } /* read all the available output packets (in general there may be any * number of them */ while (ret >= 0) { ret = avcodec_receive_packet(ctx, pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return; else if (ret < 0) { fprintf(stderr, "Error encoding audio frame\n"); exit(1); } fwrite(pkt->data, 1, pkt->size, output); /* av_write_frame(afc, pkt); */ av_packet_unref(pkt); } } /* select layout with the highest channel count */ UNUSED static int select_channel_layout(const AVCodec *codec) { const uint64_t *p; uint64_t best_ch_layout = 0; int best_nb_channels = 0; if (!codec->channel_layouts) return AV_CH_LAYOUT_STEREO; p = codec->channel_layouts; while (*p) { int nb_channels = av_get_channel_layout_nb_channels(*p); if (nb_channels > best_nb_channels) { best_ch_layout = *p; best_nb_channels = nb_channels; } p++; } return best_ch_layout; } UHA_ATTR static inline Pulse square_wave(float counter) { return (fmod(counter, 2*M_PI) < M_PI) ? 1 : -1; } UHA_ATTR static inline Pulse synthesize_wave(float theta) { /* return -1.0 + fmod(theta, 2*M_PI)/M_PI; */ /* return sin(theta); */ /* return asin(sin(theta)) * 2/M_PI; */ /* return pow(theta/2*M_PI, 2); */ /* return sin(theta)+0.442*sin(2.0*theta)+0.315*sin(3.0*theta) * +0.083*sin(4.0*theta)+cos(theta)+0.442*cos(2.0*theta); */ /* piano like */ /* return sin(theta)+1.869*sin(2.0*theta)+0.042*sin(3.0*theta) * +0.022*sin(4.0*theta)+cos(theta); */ /* excellent piano like */ return ((0.65*sin(theta)+0.5*sin(2.0*theta))*exp(-0.004*theta)); } /* This is replaced by make_sound function. */ /* UH_ATTR * static inline WaveFrag * oscillator(Volume v, Hertz h, Seconds s, Instrument *ins) * { * * return 0.0; * } */ UHA_ATTR static inline float frequency_modulation(float theta, Hertz h, float fm) { return theta+FM_AMP*theta*sin(fm); } UH_ATTR static WaveFrag hstow(Volume v, Hertz h, Seconds s) { LENT sample_num = (LENT) floor(SAMPLE_RATE * s); float attack_step = 1.0 / (float) (sample_num * ATTACK_P); float decay_step = 1.0 / (float) (sample_num * DECAY_P); float sustain_step = 1.0 / (float) (sample_num * SUSTAIN_P); float release_step = 1.0 / (float) (sample_num * RELEASE_P); float step = (float) (h * 2 * M_PI) / SAMPLE_RATE; float fm_step = (float) ((FM_FREQUENCY)*2*M_PI) / SAMPLE_RATE; unsigned char phase = 0, sustain_flag = 0; Wave w = MYALLOC(Pulse, sizeof(*w) * sample_num); float adsrcounter = 0.0, counter = 0.0, fm_counter = 0.0; float theta = 0.0, temp = 0.0; for (LENT i = 0; i < sample_num; i++, counter += step, fm_counter += fm_step) { /* theta = frequency_modulation(counter, (float) h*i/((float) SAMPLE_RATE), fm_counter); */ theta=counter; temp = synthesize_wave(theta); temp += temp*temp*temp; temp = synthesize_wave(temp); temp += theta*exp(-2.0*theta); *(w+i) = (float) v * temp; /* (synthesize_wave(temp)/\* + * * 0.01*(2.0*(float)rand()/(float) RAND_MAX - 1.0) *\/) *//* * * ((sustain_flag) ? SUSTAIN_LEVEL : adsrcounter) ;*/ switch (phase) { case 0: /* attack phase */ adsrcounter += attack_step; if (adsrcounter >= 1.0) { adsrcounter = 1.0; phase++; } break; case 1: /* decay phase */ adsrcounter -= decay_step; if (adsrcounter <= SUSTAIN_LEVEL) { adsrcounter = 0.0; sustain_flag = 1; phase++; } break; case 2: /* sustain phase */ adsrcounter += sustain_step; if (adsrcounter >= 1.0) { adsrcounter = SUSTAIN_LEVEL; sustain_flag = 0; phase++; } break; default: /* release phase */ adsrcounter -= release_step; if (adsrcounter <= 0.0) adsrcounter = 0.0; break; } } return (WaveFrag) { w, sample_num }; } H_ATTR static WaveFrag sbtow(Volume v, Semitones st, Beats b) { return hstow(v, stoh(st), (b * BEAT_DUR)); } /* compose */ D_ATTR("play_sheet") static WaveFrag compose(Volume *vs, Semitones *sts, Beats *bs, LENT len) { WaveFrag wf, *temp = MYALLOC(WaveFrag, len); LENT total_len = 0; for (LENT i = 0; i < len; i++) { if (*(sts+i) >= -50) *(temp+i) = sbtow(*(vs+i), *(sts+i), *(bs+i)); else *(temp+i) = sbtow(*(vs+i), *(sts+i), *(bs+i)); total_len += (temp+i)->n; } wf.n = total_len; wf.w = MYALLOC(Pulse, total_len); LENT counter = 0; for (int i = 0; i < len; i++) { LENT local = (temp+i)->n; for (LENT j = 0; j < local; j++) *(wf.w+counter++) = *((temp+i)->w+j); free((temp+i)->w); } free(temp); return wf; } /* Reading */ /* The file should contain an even number of numbers. For any natural number N, the 2*N th number specifies the Semitone of the Nth note, and the 2*N+1 number specifies the beats of the Nth note. */ U_ATTR static void read_sb(const char *str, int str_len, int *len, Semitones **sts, Beats **bs) { int str_counter = 0; float temp = 0; int semi_len = 0, b_len = 0; Node_t semis = EMPTY_NODE, beats = EMPTY_NODE; Node_t sn = EMPTY_NODE, bn = EMPTY_NODE; Node_t *sp = NULL, *bp = NULL; LENT nth = 0; for (;str_counter < str_len; nth++) { int number_chars_read = 0; int result = sscanf(str+str_counter, "%f %n", &temp, &number_chars_read); if (result < 1) break; str_counter += number_chars_read; if (nth % 2) { /* odd */ PUSH_NODE(b_len, bp, beats, temp, n); } else { /* even */ PUSH_NODE(semi_len, sp, semis, temp, n); } } if (nth % 2) { fprintf(stderr, "There should be an even number of floats in " "the file, but found %lu.\n", nth); exit(1); } *len = floor(nth/2.0); *sts = realloc(*sts, sizeof(float) * (*len)); *bs = realloc(*bs, sizeof(float) * (*len)); for (int i = 0; i < *len; i++) { POP_NODE(semi_len, temp, semis, sn, n); *(*sts+*len-1-i) = temp; POP_NODE(b_len, temp, beats, bn, n); *(*bs+*len-1-i) = temp; } } /* main */ int main(int argc, char **argv) { const AVCodec *codec; AVCodecContext *c = NULL; /* AVOutputFormat *of; * AVFormatContext *oc; */ AVFrame *frame; AVPacket *pkt; int ret = 0; float *samples; /* output format setup */ /* if (!(of = av_guess_format(NULL, DEFAULT_OUTPUT_NAME, "audio/aac"))) { * fprintf(stderr, "cannot guess output format\n"); * exit(1); * } */ /* avformat_alloc_output_context2(&oc, of, NULL, NULL); */ /* oc = avformat_alloc_context(); * * if (!oc) { * fprintf(stderr, "cannot allocate output context\n"); * exit(1); * } */ /* oc->oformat = of; */ /* oc->url = MYALLOC(char, sizeof(DEFAULT_OUTPUT_NAME)); * for (int i = 0; i < sizeof(DEFAULT_OUTPUT_NAME); i++) * oc->url[i] = DEFAULT_OUTPUT_NAME[i]; */ /* ret = avio_open(&oc->pb, DEFAULT_OUTPUT_NAME, AVIO_FLAG_READ_WRITE); * * if (ret<0) { * fprintf(stderr, "cannot open %s\n", DEFAULT_OUTPUT_NAME); * exit(1); * } */ /* find the encoder */ codec = avcodec_find_encoder(AV_CODEC_ID_MP3); if (!codec) { fprintf(stderr, "Codec not found\n"); exit(1); } /* set an option to place MOOV atom first. */ /* AVDictionary *opt = NULL; * av_dict_set(&opt, "movflags", "faststart", 0); * * printf("of->audio_codec = %d\n", of->audio_codec); * * if (oc->oformat->flags & AVFMT_GLOBALHEADER) * c->flags |= AVFMT_GLOBALHEADER; */ /* return 0; */ /* STD_BASE = (float) pow(2, 1.0/12.0); */ UNUSED int notes_len = 0; /* UNUSED Volume *vs = NULL; * Semitones *sts = MYALLOC(Semitones, 1); * Beats *bs = MYALLOC(Beats, 1); */ char *notes_content = MYALLOC(char, 500); LENT notes_str_len = 0; notes_str_len = (LENT) read_entire_file("notes.txt", ¬es_content); #ifdef DEBUG fprintf(stderr, "notes content = %s\n", notes_content); #endif PSheet psh = read_sheet(notes_content, notes_str_len); if (argc >= 2 && **(argv+1) == 'p') print_sheet(psh); #ifdef DEBUG fprintf(stderr, "after printing\n"); #endif /* read_sb(notes_content, notes_str_len, ¬es_len, * &sts, &bs); */ #ifdef DEBUG fprintf(stderr, "notes len = %d\nfirst bs = %.2f\n", notes_len, *bs); #endif /* vs = MYALLOC(Volume, notes_len); * for (int i = 0; i < notes_len;) *(vs+i++) = 0.3; */ /* WaveFrag wf = compose(vs, sts, bs, notes_len); */ WaveFrag wf = play_sheet(psh, 0.5); destroy_sheet(psh); printf("wf n = %lu\n", wf.n); /* AVStream *os = avformat_new_stream(oc, NULL); */ c = avcodec_alloc_context3(codec); if (!c) { fprintf(stderr, "Could not allocate audio codec context\n"); exit(1); } /* check that the encoder supports FLTP pcm input */ /* The P marks that this is "planar", i.e. the input is not interleaved between channels. */ c->sample_fmt = AV_SAMPLE_FMT_FLTP; if (!check_sample_fmt(codec, c->sample_fmt)) { fprintf(stderr, "Encoder does not support sample format %s", av_get_sample_fmt_name(c->sample_fmt)); exit(1); } /* set other audio parameters supported by the encoder */ c->sample_rate = (int) SAMPLE_RATE; c->channel_layout = select_channel_layout(codec); c->channels = av_get_channel_layout_nb_channels(c->channel_layout); /* this should be sizeof(float) * 8 * nb_channels * SAMPLE_RATE We divide by 1000 since the unit is KB.*/ c->bit_rate = sizeof(float) * 8 * SAMPLE_RATE * c->channels / 1000; /* os->time_base = (AVRational) { 1, (int) SAMPLE_RATE }; */ /* open it */ if (avcodec_open2(c, codec, NULL) < 0) { fprintf(stderr, "Could not open codec\n"); exit(1); } /* /\* set stream data *\/ * os->codecpar->sample_rate = SAMPLE_RATE; * os->codecpar->format = c->sample_fmt; * os->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; * os->codecpar->codec_id = of->audio_codec; * os->codecpar->bit_rate = c->bit_rate; * os->codecpar->channels = c->channels; * os->codecpar->channel_layout = c->channel_layout; * os->codecpar->frame_size = c->frame_size; * * if (avformat_write_header(oc, &opt) < 0) { * fprintf(stderr, "cannot write header\n"); * exit(1); * } * * av_dump_format(oc, 0, DEFAULT_OUTPUT_NAME, 1); */ /* return 0; */ /* audio_st.st->time_base = (AVRational){ 1, c->sample_rate }; * * if (oc->oformat->flags & AVFMT_GLOBALHEADER) * c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; */ /* av_dump_format(oc, 0, DEFAULT_OUTPUT_NAME, 1); */ /* ret = avio_open(&oc->pb, DEFAULT_OUTPUT_NAME, AVIO_FLAG_WRITE); * if (ret < 0) { * fprintf(stderr, "Could not open '%s': %s\n", DEFAULT_OUTPUT_NAME, * av_err2str(ret)); * return 1; * } * * ret = avformat_write_header(oc, &opt); * if (ret < 0) { * fprintf(stderr, "Error occurred when opening output file: %s\n", * av_err2str(ret)); * return 1; * } */ printf("sample rate = %d, channels = %d, bit rate = %lu\n" "frame size = %d\n", c->sample_rate, c->channels, (long) c->bit_rate, c->frame_size); /* return 0; */ FILE *file = fopen(DEFAULT_OUTPUT_NAME, "wb"); if (!file) { fprintf(stderr, "Could not open %s\n", DEFAULT_OUTPUT_NAME); exit(1); } /* packet for holding encoded output */ pkt = av_packet_alloc(); if (!pkt) { fprintf(stderr, "could not allocate the packet\n"); exit(1); } /* pkt->stream_index = os->index; */ /* frame containing input raw audio */ frame = av_frame_alloc(); if (!frame) { fprintf(stderr, "Could not allocate audio frame\n"); exit(1); } frame->nb_samples = c->frame_size; frame->format = c->sample_fmt; frame->channel_layout = c->channel_layout; /* allocate the data buffers */ ret = av_frame_get_buffer(frame, 0); if (ret < 0) { fprintf(stderr, "Could not allocate audio data buffers\n"); fprintf(stderr, "nbs = %d, format = %s, layout = %d\n", frame->nb_samples, av_get_sample_fmt_name(frame->format), av_get_channel_layout_nb_channels(frame->channel_layout)); exit(1); } /* write to the frame */ long frame_num = floor((float) wf.n/(float) c->frame_size) + 1; long w_counter = 0; for (int i = 0; i < frame_num; i++, w_counter += c->frame_size) { ret = av_frame_make_writable(frame); if (ret < 0) { fprintf(stderr, "cannot make frame writable\n"); exit(1); } for (int j = 0; j < c->channels; j++) { samples = (float *)frame->data[j]; for (int k = 0; k < c->frame_size && k+w_counter < wf.n; k++) samples[k] = *(wf.w+w_counter+k); } encode(c, frame, pkt, file); } /* fwrite(wf.w, sizeof(float), wf.n, file); */ /* flush the encoder */ encode(c, NULL, pkt, file); /* free(vs); */ /* free(sts); * free(bs); */ free(wf.w); free(notes_content); fclose(file); av_frame_free(&frame); av_packet_free(&pkt); avcodec_free_context(&c); /* av_dict_free(&opt); */ return 0; }