From 95f6ffe8452f1174f54acb3558fdfe048b008631 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 23 Oct 2018 19:51:06 +1100 Subject: [PATCH] added basic audio recording functionality --- icons/icons.qrc | 2 + icons/record-disabled.png | Bin 0 -> 1375 bytes icons/record.png | Bin 0 -> 1318 bytes panels/timeline.cpp | 5 ++ panels/timeline.h | 2 + panels/timeline.ui | 13 ++++ panels/viewer.cpp | 1 + playback/audio.cpp | 123 ++++++++++++++++++++++++++++++++++++++ playback/audio.h | 4 ++ ui/viewerwidget.cpp | 3 +- 10 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 icons/record-disabled.png create mode 100644 icons/record.png diff --git a/icons/icons.qrc b/icons/icons.qrc index a65c87c34..07232d1b6 100644 --- a/icons/icons.qrc +++ b/icons/icons.qrc @@ -43,5 +43,7 @@ clock.png sequence.png folder.png + record.png + record-disabled.png diff --git a/icons/record-disabled.png b/icons/record-disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..9510d9c8c3b6f85c2cb979285f8678684aa90ffd GIT binary patch literal 1375 zcmV-l1)%zgP)U8P*7-ZbZ>KLZ*U+lnSp_Ufq@}0xwybFAi#%#fq@|}KQEO56)-X|e7nZL z$iTqBa9P*U#mSX{G{Bl%P*lRez;J+pfx##xwK$o9f#C}S14DXwNkIt%17i#W1A|CX zc0maP17iUL1A|C*NRTrF17iyV0~1e4YDEbH0|SF|enDkXW_m`6f}y3QrGjHhep0GJ zaAk2xYHqQDXI^rCQ9*uDVo7QW0|Nup4h9AW240u^5(W3f%sd4n162kpgNVo|1qcff zJ_s=cNG>fZg9jx8g8+j9g8_pBLjXe}Lp{R+hNBE`7{wV~7)u#fFy3PlV+vxLz;uCG zm^qSpA@ds+OO_6nTdaDlt*rOhEZL^9ePa)2-_4=K(Z%tFGm-NGmm}8}ZcXk5JW@PU zd4+f<@d@)yL(o<5icqT158+-B6_LH7;i6x}CW#w~Uy-Pgl#@Irl`kzV zeL|*8R$ca%T%Wv){2zs_iiJvgN^h0dsuZZ2sQy$tsNSU!s;Q*;LF<6_B%M@UD?LHI zSNcZ`78uqV#TeU~$eS{ozBIdFzSClfs*^S+dw;4dus<{M;#|MXC)T}S9v!D zcV!QCPhBq)ZyO(X-(bH4|NMaZz==UigLj2o41F2S6d@OB6%`R(5i>J(Puzn9wnW{e zu;hl6HK{k#IWjCVGqdJqU(99Cv(K+6*i`tgSi2;vbXD1#3jNBGs$DgVwO(~o>mN4i zHPtkqZIx>)Y(Ls5-Br|mx>vQYvH$Kwn@O`L|D75??eGkZnfg$5<;Xeg_o%+-I&+-3%01W^SH2RkDT>t<8AY({UO#lFTB>(_`g8%^e z{{R4h=>PzAFaQARU;qF*m;eA5Z<1fdMgRZ-!AV3xRCwC#*}qFdQ53-OuThd%)>6)) zTVUei;u0=mZ7#VN%`VwiZR%2gK+QJPR^lQpgx0Gpw;nm?h?bC+@l9ic4Plhwe!M zeGw!}8t=C!i!2gln^%hDX^gT2)8weJ`6K;Tc-f>zp6O8m0#wQ3{&S3*9jXLJ0f=xz zSZQRKYa+S_4)BQpJ{`+&%r~O8 z=>2UupeX`02vCs&O1wHw-=QQ2Jag(8P~=(1GR)C)l)gt^&vD$~&=KH> zy1pBcqUMMU2}~bVd!L6o#yb z9tEOy`a^rC;Tu_&IsT=(T~1gg%bV8ryf5l(kzxI_J)Z|2Kil(*LGAglaGC!F24DaN hU;qYS0RJBFF#vYdRrnP~NPYkS002ovPDHLkV1o4cRt5k7 literal 0 HcmV?d00001 diff --git a/icons/record.png b/icons/record.png new file mode 100644 index 0000000000000000000000000000000000000000..87e09bfd8e0c9edc3babb9abca86c1571a4a4151 GIT binary patch literal 1318 zcmV+>1=;$EP)U8P*7-ZbZ>KLZ*U+lnSp_Ufq@}0xwybFAi#%#fq@|}KQEO56)-X|e7nZL z$iTqBa9P*U#mSX{G{Bl%P*lRez;J+pfx##xwK$o9f#C}S14DXwNkIt%17i#W1A|CX zc0maP17iUL1A|C*NRTrF17iyV0~1e4YDEbH0|SF|enDkXW_m`6f}y3QrGjHhep0GJ zaAk2xYHqQDXI^rCQ9*uDVo7QW0|Nup4h9AW240u^5(W3f%sd4n162kpgNVo|1qcff zJ_s=cNG>fZg9jx8g8+j9g8_pBLjXe}Lp{R+hNBE`7{wV~7)u#fFy3PlV+vxLz;uCG zm^qSpA@ds+OO_6nTdaDlt*rOhEZL^9ePa)2-_4=K(Z%tFGm-NGmm}8}ZcXk5JW@PU zd4+f<@d@)yL(o<5icqT158+-B6_LH7;i6x}CW#w~Uy-Pgl#@Irl`kzV zeL|*8R$ca%T%Wv){2zs_iiJvgN^h0dsuZZ2sQy$tsNSU!s;Q*;LF<6_B%M@UD?LHI zSNcZ`78uqV#TeU~$eS{ozBIdFzSClfs*^S+dw;4dus<{M;#|MXC)T}S9v!D zcV!QCPhBq)ZyO(X-(bH4|NMaZz==UigLj2o41F2S6d@OB6%`R(5i>J(Puzn9wnW{e zu;hl6HK{k#IWjCVGqdJqU(99Cv(K+6*i`tgSi2;vbXD1#3jNBGs$DgVwO(~o>mN4i zHPtkqZIx>)Y(Ls5-Br|mx>vQYvH$Kwn@O`L|D75??eGkZnfg$5<;Xeg_o%+-I&+-3%01W^SH2RkDT>t<8AY({UO#lFTB>(_`g8%^e z{{R4h=>PzAFaQARU;qF*m;eA5Z<1fdMgRZ-h)G02RCwC#nY~WJP!z}iHKHIG9d$G< zizdYI28IoNARfU^y5p`7pd;}SpbR<)ag;DAUkNb$9bhE%-h0~M$NAs2J?D2%d(TJf z5u4Q(6hHwKKmim$0Tf`v+IG6DfJog^}0&B;BGK`s8G_z3Ld19`_hJH|1F(g;+Y-2-`4<&U5usx=ik zKuH8pk^}f60AKzRPO&2Zm|^$VcSHk`{ec{yD+1`s0oo#fwj7`#0%*ujfg}}Aa3oK~ zLp(U||31z=jLLC<7sqmZhPNcjaR)a}`@fBM595|Ba%jo+{!ml;1KRRM6e7dm2kDJw zA!loy&(a*t7OTiQ$19yrc*mlxJH}3ZMWApa2T+ c?*YCC0K&X|+6nKW&;S4c07*qoM6N<$f=J*ws{jB1 literal 0 HcmV?d00001 diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 8448528b2..b2ef9e3e1 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -193,6 +193,7 @@ void Timeline::update_sequence() { ui->snappingButton->setEnabled(!null_sequence); ui->pushButton_4->setEnabled(!null_sequence); ui->pushButton_5->setEnabled(!null_sequence); + ui->recordButton->setEnabled(!null_sequence); ui->addButton->setEnabled(!null_sequence); ui->headers->setEnabled(!null_sequence); @@ -1152,3 +1153,7 @@ void Timeline::setScroll(int s) { ui->headers->set_scroll(s); repaint_timeline(); } + +void Timeline::on_recordButton_clicked() { + qDebug() << "recording is a" << start_recording(); +} diff --git a/panels/timeline.h b/panels/timeline.h index c92db4b62..cad39b7af 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -194,6 +194,8 @@ private slots: void setScroll(int); + void on_recordButton_clicked(); + private: QVector tool_buttons; void decheck_tool_buttons(QObject* sender); diff --git a/panels/timeline.ui b/panels/timeline.ui index af2bf631e..985a2eea3 100644 --- a/panels/timeline.ui +++ b/panels/timeline.ui @@ -245,6 +245,19 @@ + + + + Record audio + + + + :/icons/record.png + :/icons/record-disabled.png + + + + diff --git a/panels/viewer.cpp b/panels/viewer.cpp index c5e5ca61a..95d76dc1f 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -263,6 +263,7 @@ void Viewer::pause() { playing = false; set_playpause_icon(true); playback_updater.stop(); + stop_recording(); } void Viewer::update_playhead_timecode(long p) { diff --git a/playback/audio.cpp b/playback/audio.cpp index d9be1fbde..d5666074b 100644 --- a/playback/audio.cpp +++ b/playback/audio.cpp @@ -8,7 +8,9 @@ #include "ui_timeline.h" #include +#include #include +#include #include extern "C" { @@ -19,6 +21,9 @@ QAudioOutput* audio_output; QIODevice* audio_io_device; bool audio_device_set = false; QMutex audio_write_lock; +QAudioInput* audio_input = NULL; +QFile output_recording; +bool recording = false; qint8 audio_ibuffer[audio_ibuffer_size]; int audio_ibuffer_read = 0; @@ -177,3 +182,121 @@ int AudioSenderThread::send_audio_to_output(int offset, int max) { return actual_write; } + +void int32_to_char_array(qint32 i, char* array) { + memcpy(array, &i, 4); +} + +void write_wave_header(QFile& f, const QAudioFormat& format) { + qint32 int32bit; + char arr[4]; + + // 4 byte riff header + f.write("RIFF"); + + // 4 byte file size, filled in later + for (int i=0;i<4;i++) f.putChar(0); + + // 4 byte file type header + 4 byte format chunk marker + f.write("WAVEfmt"); + f.putChar(0x20); + + // 4 byte length of the above format data (always 16 bytes) + f.putChar(16); + for (int i=0;i<3;i++) f.putChar(0); + + // 2 byte type format (1 is PCM) + f.putChar(1); + f.putChar(0); + + // 2 byte channel count (2 for stereo) + f.putChar(2); + f.putChar(0); + + // 4 byte integer for sample rate + int32bit = format.sampleRate(); + int32_to_char_array(int32bit, arr); + f.write(arr, 4); + + // 4 byte integer for bytes per second + int32bit = (format.sampleRate() * format.sampleSize() * format.channelCount()) / 8; + int32_to_char_array(int32bit, arr); + f.write(arr, 4); + + // 2 byte integer for bytes per sample per channel + int32bit = (format.sampleSize() * format.channelCount()) / 8; + int32_to_char_array(int32bit, arr); + f.write(arr, 2); + + // 2 byte integer for bits per sample (16) + int32bit = format.sampleSize(); + int32_to_char_array(int32bit, arr); + f.write(arr, 2); + + // data chunk header + f.write("data"); + + // 4 byte integer for data chunk size (filled in later)? + for (int i=0;i<4;i++) f.putChar(0); +} + +void write_wave_trailer(QFile& f) { + char arr[4]; + + f.seek(4); + + // 4 bytes for total file size - 8 bytes + qint32 file_size = f.size() - 8; + int32_to_char_array(file_size, arr); + f.write(arr, 4); + + f.seek(40); + + // 4 bytes for data chunk size (file size - header) + file_size = f.size() - 44; + int32_to_char_array(file_size, arr); + f.write(arr, 4); +} + +bool start_recording() { + if (sequence == NULL) { + qDebug() << "[ERROR] No active sequence to record into"; + return false; + } + output_recording.setFileName("C:/Users/Matt/output.wav"); + if (!output_recording.open(QFile::WriteOnly)) { + qDebug() << "[ERROR] Failed to open output file. Does Olive have permission to write to this directory?"; + return false; + } + + QAudioFormat audio_format = audio_output->format(); + QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice(); + if (!info.isFormatSupported(audio_format)) { + qDebug() << "[WARNING] Default format not supported, using nearest"; + audio_format = info.nearestFormat(audio_format); + } + write_wave_header(output_recording, audio_format); + audio_input = new QAudioInput(audio_format); + audio_input->start(&output_recording); + recording = true; + + return true; +} + +void stop_recording() { + if (recording) { + audio_input->stop(); + + /*if (audio_input->state() == QAudio::ActiveState) { + + }*/ + + write_wave_trailer(output_recording); + + output_recording.close(); + + delete audio_input; + audio_input = NULL; + } + recording = false; +} diff --git a/playback/audio.h b/playback/audio.h index 60d75f77e..913ed0ac9 100644 --- a/playback/audio.h +++ b/playback/audio.h @@ -46,4 +46,8 @@ void init_audio(Sequence *s); void stop_audio(); int get_buffer_offset_from_frame(Sequence *s, long frame); +bool start_recording(); +void stop_recording(); +extern bool recording; + #endif // AUDIO_H diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index bbbf95d5b..dd5b74089 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -529,7 +529,8 @@ void ViewerWidget::paintGL() { glDisable(GL_TEXTURE_2D); } while (loop); - if (viewer->playing + if (!recording + && viewer->playing && (viewer->seq->playhead == viewer->seq->getEndFrame() || (viewer->seq->using_workarea && viewer->seq->playhead >= viewer->seq->workarea_out))) { viewer->pause(); }